diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index dcd652498c51..21b1a5f91f0c 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -38,7 +38,7 @@ portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) portmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) -intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/lib/subintf.cpp shellcmd.h intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) intfmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index d0eaca21d93d..1567762ffaca 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -9,13 +9,16 @@ #include "shellcmd.h" #include "macaddress.h" #include "warm_restart.h" +#include "subscriberstatetable.h" #include +#include "subintf.h" using namespace std; using namespace swss; #define VLAN_PREFIX "Vlan" #define LAG_PREFIX "PortChannel" +#define SUBINTF_LAG_PREFIX "Po" #define LOOPBACK_PREFIX "Loopback" #define VNET_PREFIX "Vnet" #define MTU_INHERITANCE "0" @@ -23,6 +26,7 @@ using namespace swss; #define VRF_MGMT "mgmt" #define LOOPBACK_DEFAULT_MTU_STR "65536" +#define DEFAULT_MTU_STR 9100 IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), @@ -35,8 +39,19 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), m_stateVrfTable(stateDb, STATE_VRF_TABLE_NAME), m_stateIntfTable(stateDb, STATE_INTERFACE_TABLE_NAME), - m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME) + m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME), + m_appLagTable(appDb, APP_LAG_TABLE_NAME) { + auto subscriberStateTable = new swss::SubscriberStateTable(stateDb, + STATE_PORT_TABLE_NAME, TableConsumable::DEFAULT_POP_BATCH_SIZE, 100); + auto stateConsumer = new Consumer(subscriberStateTable, this, STATE_PORT_TABLE_NAME); + Orch::addExecutor(stateConsumer); + + auto subscriberStateLagTable = new swss::SubscriberStateTable(stateDb, + STATE_LAG_TABLE_NAME, TableConsumable::DEFAULT_POP_BATCH_SIZE, 200); + auto stateLagConsumer = new Consumer(subscriberStateLagTable, this, STATE_LAG_TABLE_NAME); + Orch::addExecutor(stateLagConsumer); + if (!WarmStart::isWarmStart()) { flushLoopbackIntfs(); @@ -288,22 +303,160 @@ void IntfMgr::addHostSubIntf(const string&intf, const string &subIntf, const str EXEC_WITH_ERROR_THROW(cmd.str(), res); } -void IntfMgr::setHostSubIntfMtu(const string &subIntf, const string &mtu) + +std::string IntfMgr::getIntfAdminStatus(const string &alias) +{ + Table *portTable; + string admin = "down"; + if (!alias.compare(0, strlen("Eth"), "Eth")) + { + portTable = &m_statePortTable; + } + else if (!alias.compare(0, strlen("Po"), "Po")) + { + portTable = &m_appLagTable; + } + else + { + return admin; + } + + vector temp; + portTable->get(alias, temp); + + for (auto idx : temp) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == "admin_status") + { + admin = value; + } + } + return admin; +} + +std::string IntfMgr::getIntfMtu(const string &alias) +{ + Table *portTable; + string mtu = "0"; + if (!alias.compare(0, strlen("Eth"), "Eth")) + { + portTable = &m_statePortTable; + } + else if (!alias.compare(0, strlen("Po"), "Po")) + { + portTable = &m_appLagTable; + } + else + { + return mtu; + } + vector temp; + portTable->get(alias, temp); + for (auto idx : temp) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + if (field == "mtu") + { + mtu = value; + } + } + if (mtu.empty()) + { + mtu = std::to_string(DEFAULT_MTU_STR); + } + return mtu; +} + +void IntfMgr::updateSubIntfMtu(const string &alias, const string &mtu) +{ + string intf; + for (auto entry : m_subIntfList) + { + intf = entry.first; + subIntf subIf(intf); + if (subIf.parentIntf() == alias) + { + std::vector fvVector; + + string subif_config_mtu = m_subIntfList[intf].mtu; + if (subif_config_mtu == MTU_INHERITANCE || subif_config_mtu.empty()) + subif_config_mtu = std::to_string(DEFAULT_MTU_STR); + + string subintf_mtu = setHostSubIntfMtu(intf, subif_config_mtu, mtu); + + FieldValueTuple fvTuple("mtu", subintf_mtu); + fvVector.push_back(fvTuple); + m_appIntfTableProducer.set(intf, fvVector); + } + } +} + +std::string IntfMgr::setHostSubIntfMtu(const string &alias, const string &mtu, const string &parent_mtu) { stringstream cmd; string res; - cmd << IP_CMD " link set " << shellquote(subIntf) << " mtu " << shellquote(mtu); + string subifMtu = mtu; + subIntf subIf(alias); + + int pmtu = (uint32_t)stoul(parent_mtu); + int cmtu = (uint32_t)stoul(mtu); + + if (pmtu < cmtu) + { + subifMtu = parent_mtu; + } + SWSS_LOG_INFO("subintf %s active mtu: %s", alias.c_str(), subifMtu.c_str()); + cmd << IP_CMD " link set " << shellquote(alias) << " mtu " << shellquote(subifMtu); EXEC_WITH_ERROR_THROW(cmd.str(), res); + + return subifMtu; } -void IntfMgr::setHostSubIntfAdminStatus(const string &subIntf, const string &adminStatus) +void IntfMgr::updateSubIntfAdminStatus(const string &alias, const string &admin) +{ + string intf; + for (auto entry : m_subIntfList) + { + intf = entry.first; + subIntf subIf(intf); + if (subIf.parentIntf() == alias) + { + /* Avoid duplicate interface admin UP event. */ + string curr_admin = m_subIntfList[intf].currAdminStatus; + if (curr_admin == "up" && curr_admin == admin) + { + continue; + } + std::vector fvVector; + string subintf_admin = setHostSubIntfAdminStatus(intf, m_subIntfList[intf].adminStatus, admin); + m_subIntfList[intf].currAdminStatus = subintf_admin; + FieldValueTuple fvTuple("admin_status", subintf_admin); + fvVector.push_back(fvTuple); + m_appIntfTableProducer.set(intf, fvVector); + } + } +} + +std::string IntfMgr::setHostSubIntfAdminStatus(const string &alias, const string &admin_status, const string &parent_admin_status) { stringstream cmd; string res; - cmd << IP_CMD " link set " << shellquote(subIntf) << " " << shellquote(adminStatus); - EXEC_WITH_ERROR_THROW(cmd.str(), res); + if (parent_admin_status == "up" || admin_status == "down") + { + SWSS_LOG_INFO("subintf %s admin_status: %s", alias.c_str(), admin_status.c_str()); + cmd << IP_CMD " link set " << shellquote(alias) << " " << shellquote(admin_status); + EXEC_WITH_ERROR_THROW(cmd.str(), res); + return admin_status; + } + else + { + return "down"; + } } void IntfMgr::removeHostSubIntf(const string &subIntf) @@ -319,7 +472,7 @@ void IntfMgr::setSubIntfStateOk(const string &alias) { vector fvTuples = {{"state", "ok"}}; - if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + if (!alias.compare(0, strlen(SUBINTF_LAG_PREFIX), SUBINTF_LAG_PREFIX)) { m_stateLagTable.set(alias, fvTuples); } @@ -332,7 +485,7 @@ void IntfMgr::setSubIntfStateOk(const string &alias) void IntfMgr::removeSubIntfState(const string &alias) { - if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + if (!alias.compare(0, strlen(SUBINTF_LAG_PREFIX), SUBINTF_LAG_PREFIX)) { m_stateLagTable.del(alias); } @@ -451,6 +604,14 @@ bool IntfMgr::isIntfStateOk(const string &alias) { return true; } + else if (!alias.compare(0, strlen(SUBINTF_LAG_PREFIX), SUBINTF_LAG_PREFIX)) + { + if (m_stateLagTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str()); + return true; + } + } return false; } @@ -467,11 +628,24 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, size_t found = alias.find(VLAN_SUB_INTERFACE_SEPARATOR); if (found != string::npos) { - // This is a sub interface + subIntf subIf(alias); // alias holds the complete sub interface name // while parentAlias holds the parent port name - vlanId = alias.substr(found + 1); - parentAlias = alias.substr(0, found); + /*Check if subinterface is valid and sub interface name length is < 15(IFNAMSIZ)*/ + if (!subIf.isValid()) + { + SWSS_LOG_ERROR("Invalid subnitf: %s", alias.c_str()); + return true; + } + parentAlias = subIf.parentIntf(); + int subIntfId = subIf.subIntfIdx(); + /*If long name format, subinterface Id is vlanid */ + if (!subIf.isShortName()) + { + vlanId = std::to_string(subIntfId); + FieldValueTuple vlanTuple("vlan", vlanId); + data.push_back(vlanTuple); + } } bool is_lo = !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX); string mac = ""; @@ -521,6 +695,10 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { ipv6_link_local_mode = value; } + else if (field == "vlan") + { + vlanId = value; + } } if (op == SET_COMMAND) @@ -584,8 +762,14 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, if (!parentAlias.empty()) { + subIntf subIf(alias); if (m_subIntfList.find(alias) == m_subIntfList.end()) { + if (vlanId == "0" || vlanId.empty()) + { + SWSS_LOG_INFO("Vlan ID not configured for sub interface %s", alias.c_str()); + return false; + } try { addHostSubIntf(parentAlias, alias, vlanId); @@ -596,25 +780,33 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, return false; } - m_subIntfList.insert(alias); + m_subIntfList[alias].vlanId = vlanId; } if (!mtu.empty()) { + string subintf_mtu; try { - setHostSubIntfMtu(alias, mtu); + string parentMtu = getIntfMtu(subIf.parentIntf()); + subintf_mtu = setHostSubIntfMtu(alias, mtu, parentMtu); + FieldValueTuple fvTuple("mtu", mtu); + std::remove(data.begin(), data.end(), fvTuple); + FieldValueTuple newMtuFvTuple("mtu", subintf_mtu); + data.push_back(newMtuFvTuple); } catch (const std::runtime_error &e) { SWSS_LOG_NOTICE("Sub interface ip link set mtu failure. Runtime error: %s", e.what()); return false; } + m_subIntfList[alias].mtu = mtu; } else { FieldValueTuple fvTuple("mtu", MTU_INHERITANCE); data.push_back(fvTuple); + m_subIntfList[alias].mtu = MTU_INHERITANCE; } if (adminStatus.empty()) @@ -625,13 +817,20 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, } try { - setHostSubIntfAdminStatus(alias, adminStatus); + string parentAdmin = getIntfAdminStatus(subIf.parentIntf()); + string subintf_admin = setHostSubIntfAdminStatus(alias, adminStatus, parentAdmin); + m_subIntfList[alias].currAdminStatus = subintf_admin; + FieldValueTuple fvTuple("admin_status", adminStatus); + std::remove(data.begin(), data.end(), fvTuple); + FieldValueTuple newAdminFvTuple("admin_status", subintf_admin); + data.push_back(newAdminFvTuple); } catch (const std::runtime_error &e) { SWSS_LOG_NOTICE("Sub interface ip link set admin status %s failure. Runtime error: %s", adminStatus.c_str(), e.what()); return false; } + m_subIntfList[alias].adminStatus = adminStatus; // set STATE_DB port state setSubIntfStateOk(alias); @@ -788,51 +987,57 @@ void IntfMgr::doTask(Consumer &consumer) while (it != consumer.m_toSync.end()) { KeyOpFieldsValuesTuple t = it->second; - - vector keys = tokenize(kfvKey(t), config_db_key_delimiter); - const vector& data = kfvFieldsValues(t); - string op = kfvOp(t); - - if (keys.size() == 1) + if ((table_name == STATE_PORT_TABLE_NAME) || (table_name == STATE_LAG_TABLE_NAME)) { - if((table_name == CFG_VOQ_INBAND_INTERFACE_TABLE_NAME) && - (op == SET_COMMAND)) - { - //No further processing needed. Just relay to orchagent - m_appIntfTableProducer.set(keys[0], data); - m_stateIntfTable.hset(keys[0], "vrf", ""); + doPortTableTask(kfvKey(t), kfvFieldsValues(t), kfvOp(t)); + } + else + { + vector keys = tokenize(kfvKey(t), config_db_key_delimiter); + const vector& data = kfvFieldsValues(t); + string op = kfvOp(t); - it = consumer.m_toSync.erase(it); - continue; - } - if (!doIntfGeneralTask(keys, data, op)) - { - it++; - continue; - } - else + if (keys.size() == 1) { - //Entry programmed, remove it from pending list if present - m_pendingReplayIntfList.erase(keys[0]); + if((table_name == CFG_VOQ_INBAND_INTERFACE_TABLE_NAME) && + (op == SET_COMMAND)) + { + //No further processing needed. Just relay to orchagent + m_appIntfTableProducer.set(keys[0], data); + m_stateIntfTable.hset(keys[0], "vrf", ""); + + it = consumer.m_toSync.erase(it); + continue; + } + if (!doIntfGeneralTask(keys, data, op)) + { + it++; + continue; + } + else + { + //Entry programmed, remove it from pending list if present + m_pendingReplayIntfList.erase(keys[0]); + } } - } - else if (keys.size() == 2) - { - if (!doIntfAddrTask(keys, data, op)) + else if (keys.size() == 2) { - it++; - continue; + if (!doIntfAddrTask(keys, data, op)) + { + it++; + continue; + } + else + { + //Entry programmed, remove it from pending list if present + m_pendingReplayIntfList.erase(keys[0] + config_db_key_delimiter + keys[1] ); + } } else { - //Entry programmed, remove it from pending list if present - m_pendingReplayIntfList.erase(keys[0] + config_db_key_delimiter + keys[1] ); + SWSS_LOG_ERROR("Invalid key %s", kfvKey(t).c_str()); } } - else - { - SWSS_LOG_ERROR("Invalid key %s", kfvKey(t).c_str()); - } it = consumer.m_toSync.erase(it); } @@ -842,3 +1047,26 @@ void IntfMgr::doTask(Consumer &consumer) setWarmReplayDoneState(); } } + +void IntfMgr::doPortTableTask(const string& key, vector data, string op) +{ + if (op == SET_COMMAND) + { + for (auto idx : data) + { + const auto &field = fvField(idx); + const auto &value = fvValue(idx); + + if (field == "admin_status") + { + SWSS_LOG_INFO("Port %s Admin %s", key.c_str(), value.c_str()); + updateSubIntfAdminStatus(key, value); + } + else if (field == "mtu") + { + SWSS_LOG_INFO("Port %s MTU %s", key.c_str(), value.c_str()); + updateSubIntfMtu(key, value); + } + } + } +} diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 655fb4deeb62..e1a5049fff88 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -9,6 +9,16 @@ #include #include +struct SubIntfInfo +{ + std::string vlanId; + std::string mtu; + std::string adminStatus; + std::string currAdminStatus; +}; + +typedef std::map SubIntfMap; + namespace swss { class IntfMgr : public Orch @@ -20,9 +30,9 @@ class IntfMgr : public Orch private: ProducerStateTable m_appIntfTableProducer; Table m_cfgIntfTable, m_cfgVlanIntfTable, m_cfgLagIntfTable, m_cfgLoopbackIntfTable; - Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable, m_appLagTable; - std::set m_subIntfList; + SubIntfMap m_subIntfList; std::set m_loopbackIntfList; std::set m_pendingReplayIntfList; @@ -34,6 +44,7 @@ class IntfMgr : public Orch bool doIntfGeneralTask(const std::vector& keys, std::vector data, const std::string& op); bool doIntfAddrTask(const std::vector& keys, const std::vector& data, const std::string& op); void doTask(Consumer &consumer); + void doPortTableTask(const std::string& key, std::vector data, std::string op); bool isIntfStateOk(const std::string &alias); bool isIntfCreated(const std::string &alias); @@ -46,9 +57,11 @@ class IntfMgr : public Orch void delLoopbackIntf(const std::string &alias); void flushLoopbackIntfs(void); + std::string getIntfAdminStatus(const std::string &alias); + std::string getIntfMtu(const std::string &alias); void addHostSubIntf(const std::string&intf, const std::string &subIntf, const std::string &vlan); - void setHostSubIntfMtu(const std::string &subIntf, const std::string &mtu); - void setHostSubIntfAdminStatus(const std::string &subIntf, const std::string &admin_status); + std::string setHostSubIntfMtu(const std::string &alias, const std::string &mtu, const std::string &parent_mtu); + std::string setHostSubIntfAdminStatus(const std::string &alias, const std::string &admin_status, const std::string &parent_admin_status); void removeHostSubIntf(const std::string &subIntf); void setSubIntfStateOk(const std::string &alias); void removeSubIntfState(const std::string &alias); @@ -56,6 +69,9 @@ class IntfMgr : public Orch bool setIntfProxyArp(const std::string &alias, const std::string &proxy_arp); bool setIntfGratArp(const std::string &alias, const std::string &grat_arp); + void updateSubIntfAdminStatus(const std::string &alias, const std::string &admin); + void updateSubIntfMtu(const std::string &alias, const std::string &mtu); + bool m_replayDone {false}; }; diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index e0106f00cee4..93d5aa01e040 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -38,6 +38,7 @@ endif orchagent_SOURCES = \ main.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ + $(top_srcdir)/lib/subintf.cpp \ orchdaemon.cpp \ orch.cpp \ notifications.cpp \ diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index b270a0c5e59e..1feebb4d753e 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -657,11 +657,13 @@ void IntfsOrch::doTask(Consumer &consumer) MacAddress mac; uint32_t mtu = 0; - bool adminUp = false; + bool adminUp; + bool adminStateChanged = false; uint32_t nat_zone_id = 0; string proxy_arp = ""; string inband_type = ""; bool mpls = false; + string vlan = ""; for (auto idx : data) { @@ -736,6 +738,7 @@ void IntfsOrch::doTask(Consumer &consumer) SWSS_LOG_WARN("Sub interface %s unknown admin status %s", alias.c_str(), value.c_str()); } } + adminStateChanged = true; } else if (field == "nat_zone") { @@ -749,6 +752,10 @@ void IntfsOrch::doTask(Consumer &consumer) { inband_type = value; } + else if (field == "vlan") + { + vlan = value; + } } if (alias == "eth0" || alias == "docker0") @@ -818,7 +825,11 @@ void IntfsOrch::doTask(Consumer &consumer) { if (!ip_prefix_in_key && isSubIntf) { - if (!gPortsOrch->addSubPort(port, alias, adminUp, mtu)) + if (adminStateChanged == false) + { + adminUp = port.m_admin_state_up; + } + if (!gPortsOrch->addSubPort(port, alias, vlan, adminUp, mtu)) { it++; continue; @@ -858,6 +869,10 @@ void IntfsOrch::doTask(Consumer &consumer) } else { + if (adminStateChanged == false) + { + adminUp = port.m_admin_state_up; + } if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu)) { it++; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index db592024a649..ada1f4bb920b 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -5,6 +5,7 @@ #include "gearboxutils.h" #include "vxlanorch.h" #include "directory.h" +#include "subintf.h" #include #include @@ -807,7 +808,7 @@ bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port return false; } -bool PortsOrch::addSubPort(Port &port, const string &alias, const bool &adminUp, const uint32_t &mtu) +bool PortsOrch::addSubPort(Port &port, const string &alias, const string &vlan, const bool &adminUp, const uint32_t &mtu) { SWSS_LOG_ENTER(); @@ -817,21 +818,21 @@ bool PortsOrch::addSubPort(Port &port, const string &alias, const bool &adminUp, SWSS_LOG_ERROR("%s is not a sub interface", alias.c_str()); return false; } - string parentAlias = alias.substr(0, found); - string vlanId = alias.substr(found + 1); + subIntf subIf(alias); + string parentAlias = subIf.parentIntf(); sai_vlan_id_t vlan_id; try { - vlan_id = static_cast(stoul(vlanId)); + vlan_id = static_cast(stoul(vlan)); } catch (const std::invalid_argument &e) { - SWSS_LOG_ERROR("Invalid argument %s to %s()", vlanId.c_str(), e.what()); + SWSS_LOG_ERROR("Invalid argument %s to %s()", vlan.c_str(), e.what()); return false; } catch (const std::out_of_range &e) { - SWSS_LOG_ERROR("Out of range argument %s to %s()", vlanId.c_str(), e.what()); + SWSS_LOG_ERROR("Out of range argument %s to %s()", vlan.c_str(), e.what()); return false; } if (vlan_id > MAX_VALID_VLAN_ID) diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index c4e192aa1429..16b4922bc939 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -132,7 +132,7 @@ class PortsOrch : public Orch, public Subject void refreshPortStatus(); bool removeAclTableGroup(const Port &p); - bool addSubPort(Port &port, const string &alias, const bool &adminUp = true, const uint32_t &mtu = 0); + bool addSubPort(Port &port, const string &alias, const string &vlan, const bool &adminUp = true, const uint32_t &mtu = 0); bool removeSubPort(const string &alias); bool updateL3VniStatus(uint16_t vlan_id, bool status); void getLagMember(Port &lag, vector &portv); diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index 08df3ff9ec6f..4a2b351ee07e 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -182,6 +182,7 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) unsigned int ifindex = rtnl_link_get_ifindex(link); int master = rtnl_link_get_master(link); char *type = rtnl_link_get_type(link); + unsigned int mtu = rtnl_link_get_mtu(link); if (type) { @@ -251,10 +252,14 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) { g_portSet.erase(key); FieldValueTuple tuple("state", "ok"); + FieldValueTuple admin_status("admin_status", (admin ? "up" : "down")); + FieldValueTuple port_mtu("mtu", to_string(mtu)); vector vector; vector.push_back(tuple); FieldValueTuple op("netdev_oper_status", oper ? "up" : "down"); vector.push_back(op); + vector.push_back(admin_status); + vector.push_back(port_mtu); m_statePortTable.set(key, vector); SWSS_LOG_NOTICE("Publish %s(ok:%s) to state db", key.c_str(), oper ? "up" : "down"); } diff --git a/teamsyncd/teamsync.cpp b/teamsyncd/teamsync.cpp index 846d474a6cd3..6d8c025911b4 100644 --- a/teamsyncd/teamsync.cpp +++ b/teamsyncd/teamsync.cpp @@ -157,11 +157,18 @@ void TeamSync::addLag(const string &lagName, int ifindex, bool admin_state, SWSS_LOG_INFO("Add %s admin_status:%s oper_status:%s, mtu: %d", lagName.c_str(), admin_state ? "up" : "down", oper_state ? "up" : "down", mtu); + bool lag_update = true; /* Return when the team instance has already been tracked */ if (m_teamSelectables.find(lagName) != m_teamSelectables.end()) - return; + { + auto tsync = m_teamSelectables[lagName]; + if (tsync->admin_state == admin_state && tsync->mtu == mtu) + return; + tsync->admin_state = admin_state; + tsync->mtu = mtu; + lag_update = false; + } - fvVector.clear(); FieldValueTuple s("state", "ok"); fvVector.push_back(s); if (m_warmstart) @@ -173,10 +180,13 @@ void TeamSync::addLag(const string &lagName, int ifindex, bool admin_state, m_stateLagTable.set(lagName, fvVector); } - /* Create the team instance */ - auto sync = make_shared(lagName, ifindex, &m_lagMemberTable); - m_teamSelectables[lagName] = sync; - m_selectablesToAdd.insert(lagName); + if (lag_update) + { + /* Create the team instance */ + auto sync = make_shared(lagName, ifindex, &m_lagMemberTable); + m_teamSelectables[lagName] = sync; + m_selectablesToAdd.insert(lagName); + } } void TeamSync::removeLag(const string &lagName) diff --git a/teamsyncd/teamsync.h b/teamsyncd/teamsync.h index 406953e31210..deb5d8412939 100644 --- a/teamsyncd/teamsync.h +++ b/teamsyncd/teamsync.h @@ -43,6 +43,8 @@ class TeamSync : public NetMsg /* member_name -> enabled|disabled */ std::map m_lagMembers; + bool admin_state; + unsigned int mtu; protected: int onChange(); static int teamdHandler(struct team_handle *th, void *arg, diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 7f31379658f5..741946384784 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -33,6 +33,7 @@ tests_SOURCES = aclorch_ut.cpp \ mock_redisreply.cpp \ bulker_ut.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ + $(top_srcdir)/lib/subintf.cpp \ $(top_srcdir)/orchagent/orchdaemon.cpp \ $(top_srcdir)/orchagent/orch.cpp \ $(top_srcdir)/orchagent/notifications.cpp \ diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py index 2d442fd027fb..b0f2863aa673 100644 --- a/tests/test_sub_port_intf.py +++ b/tests/test_sub_port_intf.py @@ -45,6 +45,7 @@ PEER_LIST = "peer_list" ETHERNET_PREFIX = "Ethernet" +SUBINTF_LAG_PREFIX = "Po" LAG_PREFIX = "PortChannel" VRF_PREFIX = "Vrf" VNET_PREFIX = "Vnet" @@ -52,6 +53,8 @@ VLAN_SUB_INTERFACE_SEPARATOR = "." APPL_DB_SEPARATOR = ":" +ETHERNET_PORT_DEFAULT_MTU = "1500" + class TestSubPortIntf(object): SUB_PORT_INTERFACE_UNDER_TEST = "Ethernet64.10" @@ -97,7 +100,62 @@ def connect_dbs(self, dvs): if phy_port in key: self.buf_q_fvs[key] = self.config_db.get_entry("BUFFER_QUEUE", key) + def get_subintf_longname(self, port_name): + if port_name is None: + return None + sub_intf_sep_idx = port_name.find(VLAN_SUB_INTERFACE_SEPARATOR) + if sub_intf_sep_idx == -1: + return str(port_name) + parent_intf = port_name[:sub_intf_sep_idx] + sub_intf_idx = port_name[(sub_intf_sep_idx+1):] + if port_name.startswith("Eth"): + if port_name.startswith("Ethernet"): + intf_index=port_name[len("Ethernet"):sub_intf_sep_idx] + else: + intf_index=port_name[len("Eth"):sub_intf_sep_idx] + return "Ethernet"+intf_index+VLAN_SUB_INTERFACE_SEPARATOR+sub_intf_idx + elif port_name.startswith("Po"): + if port_name.startswith("PortChannel"): + intf_index=port_name[len("PortChannel"):sub_intf_sep_idx] + else: + intf_index=port_name[len("Po"):sub_intf_sep_idx] + return "PortChannel"+intf_index+VLAN_SUB_INTERFACE_SEPARATOR+sub_intf_idx + else: + return str(port_name) + + def get_port_longname(self, port_name): + if port_name is None: + return None + + if VLAN_SUB_INTERFACE_SEPARATOR in port_name: + return self.get_subintf_longname(port_name) + else: + if port_name.startswith("Eth"): + if port_name.startswith("Ethernet"): + return port_name + intf_index=port_name[len("Eth"):len(port_name)] + return "Ethernet"+intf_index + elif port_name.startswith("Po"): + if port_name.startswith("PortChannel"): + return port_name + intf_index=port_name[len("Po"):len(port_name)] + return "PortChannel"+intf_index + else: + return port_name + + def get_parent_port(self, port_name): + port = self.get_port_longname(port_name) + idx = port.find(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = "" + if port.startswith(ETHERNET_PREFIX): + parent_port = port[:idx] + else: + assert port.startswith(SUBINTF_LAG_PREFIX) + parent_port = port[:idx] + return parent_port + def get_parent_port_index(self, port_name): + port_name = self.get_port_longname(port_name) if port_name.startswith(ETHERNET_PREFIX): idx = int(port_name[len(ETHERNET_PREFIX):]) else: @@ -110,7 +168,7 @@ def set_parent_port_oper_status(self, dvs, port_name, status): srv_idx = self.get_parent_port_index(port_name) // 4 dvs.servers[srv_idx].runcmd("ip link set dev eth0 " + status) else: - assert port_name.startswith(LAG_PREFIX) + assert port_name.startswith(SUBINTF_LAG_PREFIX) dvs.runcmd("bash -c 'echo " + ("1" if status == "up" else "0") + " > /sys/class/net/" + port_name + "/carrier'") time.sleep(1) @@ -121,7 +179,7 @@ def set_parent_port_admin_status(self, dvs, port_name, status): if port_name.startswith(ETHERNET_PREFIX): tbl_name = CFG_PORT_TABLE_NAME else: - assert port_name.startswith(LAG_PREFIX) + assert port_name.startswith(SUBINTF_LAG_PREFIX) tbl_name = CFG_LAG_TABLE_NAME self.config_db.create_entry(tbl_name, port_name, fvs) time.sleep(1) @@ -154,8 +212,28 @@ def create_vrf(self, vrf_name): self.create_vxlan_tunnel(self.TUNNEL_UNDER_TEST, self.VTEP_IP_UNDER_TEST) self.create_vnet(vrf_name, self.TUNNEL_UNDER_TEST, self.VNI_UNDER_TEST) + def is_short_name(self, port_name): + idx = port_name.find(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = port_name[:idx] + is_short = False + if parent_port.startswith("Eth"): + if parent_port.startswith(ETHERNET_PREFIX): + is_short = False + else: + is_short = True + elif parent_port.startswith("Po"): + if parent_port.startswith("PortChannel"): + is_short = False + else: + is_short = True + return is_short + def create_sub_port_intf_profile(self, sub_port_intf_name, vrf_name=None): fvs = {ADMIN_STATUS: "up"} + idx = sub_port_intf_name.find(VLAN_SUB_INTERFACE_SEPARATOR) + sub_port_idx = sub_port_intf_name[(idx+1):] + if self.is_short_name(sub_port_intf_name) == True: + fvs["vlan"] = sub_port_idx if vrf_name: fvs[VRF_NAME if vrf_name.startswith(VRF_PREFIX) else VNET_NAME] = vrf_name @@ -189,8 +267,12 @@ def add_lag_members(self, lag, members): self.config_db.create_entry(CFG_LAG_MEMBER_TABLE_NAME, key, fvs) def create_sub_port_intf_profile_appl_db(self, sub_port_intf_name, admin_status, vrf_name=None): + substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = self.get_parent_port(sub_port_intf_name) + vlan_id = substrs[1] pairs = [ (ADMIN_STATUS, admin_status), + ("vlan", vlan_id), ("mtu", "0"), ] if vrf_name: @@ -255,7 +337,7 @@ def remove_parent_port_appl_db(self, port_name): if port_name in key: self.config_db.delete_entry("BUFFER_QUEUE", key) else: - assert port_name.startswith(LAG_PREFIX) + assert port_name.startswith(SUBINTF_LAG_PREFIX) tbl_name = APP_LAG_TABLE_NAME tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, tbl_name) tbl._del(port_name) @@ -405,14 +487,14 @@ def _access_function(): def _test_sub_port_intf_creation(self, dvs, sub_port_intf_name, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) - parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vlan_id = substrs[1] if parent_port.startswith(ETHERNET_PREFIX): state_tbl_name = STATE_PORT_TABLE_NAME phy_ports = [parent_port] parent_port_oid = dvs.asicdb.portnamemap[parent_port] else: - assert parent_port.startswith(LAG_PREFIX) + assert parent_port.startswith(SUBINTF_LAG_PREFIX) state_tbl_name = STATE_LAG_TABLE_NAME phy_ports = self.LAG_MEMBERS_UNDER_TEST old_lag_oids = self.get_oids(ASIC_LAG_TABLE) @@ -421,7 +503,7 @@ def _test_sub_port_intf_creation(self, dvs, sub_port_intf_name, vrf_name=None): old_rif_oids = self.get_oids(ASIC_RIF_TABLE) self.set_parent_port_admin_status(dvs, parent_port, "up") - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): parent_port_oid = self.get_newly_created_oid(ASIC_LAG_TABLE, old_lag_oids) # Add lag members to test physical port host interface vlan tag attribute self.add_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) @@ -490,7 +572,7 @@ def _test_sub_port_intf_creation(self, dvs, sub_port_intf_name, vrf_name=None): self.remove_vxlan_tunnel(self.TUNNEL_UNDER_TEST) self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): # Remove lag members from lag parent port self.remove_lag_members(parent_port, self.LAG_MEMBERS_UNDER_TEST) self.asic_db.wait_for_n_keys(ASIC_LAG_MEMBER_TABLE, 0) @@ -513,7 +595,7 @@ def test_sub_port_intf_creation(self, dvs): def _test_sub_port_intf_add_ip_addrs(self, dvs, sub_port_intf_name, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) - parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vrf_oid = self.default_vrf_oid old_rif_oids = self.get_oids(ASIC_RIF_TABLE) @@ -581,7 +663,7 @@ def _test_sub_port_intf_add_ip_addrs(self, dvs, sub_port_intf_name, vrf_name=Non self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) # Remove lag - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): self.remove_lag(parent_port) self.asic_db.wait_for_n_keys(ASIC_LAG_TABLE, 0) @@ -599,7 +681,7 @@ def test_sub_port_intf_add_ip_addrs(self, dvs): def _test_sub_port_intf_appl_db_proc_seq(self, dvs, sub_port_intf_name, admin_up, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) - parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vlan_id = substrs[1] vrf_oid = self.default_vrf_oid @@ -610,7 +692,7 @@ def _test_sub_port_intf_appl_db_proc_seq(self, dvs, sub_port_intf_name, admin_up if parent_port.startswith(ETHERNET_PREFIX): parent_port_oid = dvs.asicdb.portnamemap[parent_port] else: - assert parent_port.startswith(LAG_PREFIX) + assert parent_port.startswith(SUBINTF_LAG_PREFIX) parent_port_oid = self.get_newly_created_oid(ASIC_LAG_TABLE, old_lag_oids) if vrf_name: self.create_vrf(vrf_name) @@ -656,7 +738,7 @@ def _test_sub_port_intf_appl_db_proc_seq(self, dvs, sub_port_intf_name, admin_up self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) # Remove lag - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): self.remove_lag(parent_port) self.check_lag_removal(parent_port_oid) @@ -684,6 +766,7 @@ def test_sub_port_intf_appl_db_proc_seq(self, dvs): def _test_sub_port_intf_admin_status_change(self, dvs, sub_port_intf_name, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vrf_oid = self.default_vrf_oid old_rif_oids = self.get_oids(ASIC_RIF_TABLE) @@ -779,7 +862,7 @@ def _test_sub_port_intf_admin_status_change(self, dvs, sub_port_intf_name, vrf_n self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) # Remove lag - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): self.remove_lag(parent_port) self.asic_db.wait_for_n_keys(ASIC_LAG_TABLE, 0) @@ -798,6 +881,7 @@ def test_sub_port_intf_admin_status_change(self, dvs): def _test_sub_port_intf_remove_ip_addrs(self, dvs, sub_port_intf_name, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) old_rif_oids = self.get_oids(ASIC_RIF_TABLE) @@ -862,7 +946,7 @@ def _test_sub_port_intf_remove_ip_addrs(self, dvs, sub_port_intf_name, vrf_name= self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) # Remove lag - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): self.remove_lag(parent_port) self.asic_db.wait_for_n_keys(ASIC_LAG_TABLE, 0) @@ -881,6 +965,7 @@ def test_sub_port_intf_remove_ip_addrs(self, dvs): def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name, removal_seq_test=False, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vlan_id = substrs[1] if parent_port.startswith(ETHERNET_PREFIX): state_tbl_name = STATE_PORT_TABLE_NAME @@ -888,7 +973,7 @@ def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name, removal_seq_test= parent_port_oid = dvs.asicdb.portnamemap[parent_port] asic_tbl_name = ASIC_PORT_TABLE else: - assert parent_port.startswith(LAG_PREFIX) + assert parent_port.startswith(SUBINTF_LAG_PREFIX) state_tbl_name = STATE_LAG_TABLE_NAME phy_ports = self.LAG_MEMBERS_UNDER_TEST old_lag_oids = self.get_oids(ASIC_LAG_TABLE) @@ -898,7 +983,7 @@ def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name, removal_seq_test= old_rif_oids = self.get_oids(ASIC_RIF_TABLE) self.set_parent_port_admin_status(dvs, parent_port, "up") - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): parent_port_oid = self.get_newly_created_oid(ASIC_LAG_TABLE, old_lag_oids) if removal_seq_test == False: # Add lag members to test physical port host interface vlan tag attribute @@ -981,6 +1066,10 @@ def _test_sub_port_intf_removal(self, dvs, sub_port_intf_name, removal_seq_test= "SAI_ROUTER_INTERFACE_ATTR_PORT_ID": parent_port_oid, } rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + #If subintf mtu deleted, it inherits from parent + if vrf_name == self.VRF_UNDER_TEST: + if parent_port.startswith(ETHERNET_PREFIX): + fv_dict["SAI_ROUTER_INTERFACE_ATTR_MTU"] = ETHERNET_PORT_DEFAULT_MTU self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) else: rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) @@ -1076,6 +1165,7 @@ def test_sub_port_intf_removal(self, dvs): def _test_sub_port_intf_mtu(self, dvs, sub_port_intf_name, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vrf_oid = self.default_vrf_oid old_rif_oids = self.get_oids(ASIC_RIF_TABLE) @@ -1122,7 +1212,7 @@ def _test_sub_port_intf_mtu(self, dvs, sub_port_intf_name, vrf_name=None): self.app_db.wait_for_n_keys(ASIC_TUNNEL_TABLE, 0) # Remove lag - if parent_port.startswith(LAG_PREFIX): + if parent_port.startswith(SUBINTF_LAG_PREFIX): self.remove_lag(parent_port) self.asic_db.wait_for_n_keys(ASIC_LAG_TABLE, 0) @@ -1244,13 +1334,14 @@ def check_nhg_members_on_parent_port_oper_status_change(self, dvs, parent_port_p def _test_sub_port_intf_nhg_accel(self, dvs, sub_port_intf_name, nhop_num=3, create_intf_on_parent_port=False, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vlan_id = substrs[1] assert len(vlan_id) == 2 if parent_port.startswith(ETHERNET_PREFIX): parent_port_prefix = ETHERNET_PREFIX else: - assert parent_port.startswith(LAG_PREFIX) + assert parent_port.startswith(SUBINTF_LAG_PREFIX) parent_port_prefix = LAG_PREFIX parent_port_idx_base = self.get_parent_port_index(parent_port) @@ -1372,13 +1463,14 @@ def _test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(self, dvs, sub_ create_intf_on_parent_port=False, vrf_name=None): substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) parent_port = substrs[0] + parent_port = self.get_parent_port(sub_port_intf_name) vlan_id = substrs[1] assert len(vlan_id) == 2 if parent_port.startswith(ETHERNET_PREFIX): parent_port_prefix = ETHERNET_PREFIX else: - assert parent_port.startswith(LAG_PREFIX) + assert parent_port.startswith(SUBINTF_LAG_PREFIX) parent_port_prefix = LAG_PREFIX parent_port_idx_base = self.get_parent_port_index(parent_port)