From 56d66a188229cd8d2104313ab3dd08a5d5e2e90a Mon Sep 17 00:00:00 2001 From: Wenda Ni Date: Thu, 7 Nov 2019 17:13:21 -0800 Subject: [PATCH] Sub port interface implementation (#969) IntfMgrd: Listen to CONFIG_DB VLAN_SUB_INTERFACE table. Process host sub port interface: create, set (admin_status), and remove using ip link and ip address system call, and update the STATE_DB Relay the VLAN_SUB_INTERFACE table operation and field-value content to APPL_DB INTF_TABLE. IntfOrch: Listen to APPL_DB INTF_TABLE. Process sub port interface on physical port: create (a Port object for sub port interface), set (admin_status), and remove. PortsOrch: Add member function to create a Port object for sub port interface vs unit test: sub port interface creation sub port interface add IP addresses sub port interface admin status change sub port interface remove IP addresses sub port interface removal Signed-off-by: Wenda Ni --- cfgmgr/intfmgr.cpp | 256 +++++++++++++++++++++++- cfgmgr/intfmgr.h | 13 +- cfgmgr/intfmgrd.cpp | 1 + orchagent/bufferorch.cpp | 2 +- orchagent/intfsorch.cpp | 168 +++++++++++++++- orchagent/intfsorch.h | 5 +- orchagent/orch.h | 1 + orchagent/port.h | 3 + orchagent/portsorch.cpp | 96 +++++++++ orchagent/portsorch.h | 3 + orchagent/vnetorch.cpp | 4 +- orchagent/vnetorch.h | 2 +- tests/test_sub_port_intf.py | 381 ++++++++++++++++++++++++++++++++++++ 13 files changed, 908 insertions(+), 27 deletions(-) create mode 100644 tests/test_sub_port_intf.py diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index a84fe92a9e31..3d1915c1b8e5 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -15,14 +15,13 @@ using namespace swss; #define LAG_PREFIX "PortChannel" #define LOOPBACK_PREFIX "Loopback" #define VNET_PREFIX "Vnet" +#define MTU_INHERITANCE "0" #define VRF_PREFIX "Vrf" #define LOOPBACK_DEFAULT_MTU_STR "65536" IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &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), m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), @@ -167,6 +166,157 @@ bool IntfMgr::isIntfChangeVrf(const string &alias, const string &vrfName) return false; } +void IntfMgr::addHostSubIntf(const string&intf, const string &subIntf, const string &vlan) +{ + // TODO: remove when validation check at mgmt is in place + for (const auto &c : intf) + { + if (!isalnum(c)) + { + SWSS_LOG_ERROR("Invalid parent port name %s for host sub interface %s", intf.c_str(), subIntf.c_str()); + return; + } + } + for (const auto &c : vlan) + { + if (!isdigit(c)) + { + SWSS_LOG_ERROR("Invalid vlan id %s for host sub interface %s", vlan.c_str(), subIntf.c_str()); + return; + } + } + + stringstream cmd; + string res; + + cmd << IP_CMD << " link add link " << intf << " name " << subIntf << " type vlan id " << vlan; + EXEC_WITH_ERROR_THROW(cmd.str(), res); +} + +void IntfMgr::setHostSubIntfMtu(const string &subIntf, const string &mtu) +{ + // TODO: remove when validation check at mgmt is in place + size_t found = subIntf.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found == string::npos) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + size_t i = 0; + for (const auto &c : subIntf) + { + if (i < found && !isalnum(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + else if (i > found && !isdigit(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + i++; + } + + stringstream cmd; + string res; + + cmd << IP_CMD << " link set " << subIntf << " mtu " << mtu; + EXEC_WITH_ERROR_THROW(cmd.str(), res); +} + +void IntfMgr::setHostSubIntfAdminStatus(const string &subIntf, const string &adminStatus) +{ + // TODO: remove when validation check at mgmt is in place + size_t found = subIntf.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found == string::npos) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + size_t i = 0; + for (const auto &c : subIntf) + { + if (i < found && !isalnum(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + else if (i > found && !isdigit(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + i++; + } + + stringstream cmd; + string res; + + cmd << IP_CMD << " link set " << subIntf << " " << adminStatus; + EXEC_WITH_ERROR_THROW(cmd.str(), res); +} + +void IntfMgr::removeHostSubIntf(const string &subIntf) +{ + // TODO: remove when validation check at mgmt is in place + size_t found = subIntf.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found == string::npos) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + size_t i = 0; + for (const auto &c : subIntf) + { + if (i < found && !isalnum(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + else if (i > found && !isdigit(c)) + { + SWSS_LOG_ERROR("Invalid host sub interface name: %s", subIntf.c_str()); + return; + } + i++; + } + + stringstream cmd; + string res; + + cmd << IP_CMD << " link del " << subIntf; + EXEC_WITH_ERROR_THROW(cmd.str(), res); +} + +void IntfMgr::setSubIntfStateOk(const string &alias) +{ + vector fvTuples = {{"state", "ok"}}; + + if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + { + m_stateLagTable.set(alias, fvTuples); + } + else + { + // EthernetX using PORT_TABLE + m_statePortTable.set(alias, fvTuples); + } +} + +void IntfMgr::removeSubIntfState(const string &alias) +{ + if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + { + m_stateLagTable.del(alias); + } + else + { + // EthernetX using PORT_TABLE + m_statePortTable.del(alias); + } +} + bool IntfMgr::isIntfStateOk(const string &alias) { vector temp; @@ -217,23 +367,43 @@ bool IntfMgr::isIntfStateOk(const string &alias) } bool IntfMgr::doIntfGeneralTask(const vector& keys, - const vector& data, + vector data, const string& op) { SWSS_LOG_ENTER(); string alias(keys[0]); - string vrf_name = ""; + string vlanId; + string subIntfAlias; + size_t found = alias.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found != string::npos) + { + // This is a sub interface + // subIntfAlias holds the complete sub interface name + // while alias becomes the parent interface + subIntfAlias = alias; + vlanId = alias.substr(found + 1); + alias = alias.substr(0, found); + } bool is_lo = !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX); + string vrf_name = ""; + string mtu = ""; + string adminStatus = ""; for (auto idx : data) { const auto &field = fvField(idx); const auto &value = fvValue(idx); + if (field == "vnet_name" || field == "vrf_name") { vrf_name = value; } + + if (field == "admin_status") + { + adminStatus = value; + } } if (op == SET_COMMAND) @@ -256,16 +426,73 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, SWSS_LOG_ERROR("%s can not change to %s directly, skipping", alias.c_str(), vrf_name.c_str()); return true; } + if (is_lo) { addLoopbackIntf(alias); } + if (!vrf_name.empty()) { setIntfVrf(alias, vrf_name); } - m_appIntfTableProducer.set(alias, data); - m_stateIntfTable.hset(alias, "vrf", vrf_name); + + if (!subIntfAlias.empty()) + { + if (m_subIntfList.find(subIntfAlias) == m_subIntfList.end()) + { + try + { + addHostSubIntf(alias, subIntfAlias, vlanId); + } + catch (const std::runtime_error &e) + { + SWSS_LOG_NOTICE("Sub interface ip link add failure. Runtime error: %s", e.what()); + return false; + } + + m_subIntfList.insert(subIntfAlias); + } + + if (!mtu.empty()) + { + try + { + setHostSubIntfMtu(subIntfAlias, mtu); + } + catch (const std::runtime_error &e) + { + SWSS_LOG_NOTICE("Sub interface ip link set mtu failure. Runtime error: %s", e.what()); + return false; + } + } + else + { + FieldValueTuple fvTuple("mtu", MTU_INHERITANCE); + data.push_back(fvTuple); + } + + if (adminStatus.empty()) + { + adminStatus = "up"; + FieldValueTuple fvTuple("admin_status", adminStatus); + data.push_back(fvTuple); + } + try + { + setHostSubIntfAdminStatus(subIntfAlias, adminStatus); + } + 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; + } + + // set STATE_DB port state + setSubIntfStateOk(subIntfAlias); + } + m_appIntfTableProducer.set(subIntfAlias.empty() ? alias : subIntfAlias, data); + m_stateIntfTable.hset(subIntfAlias.empty() ? alias : subIntfAlias, "vrf", vrf_name); } else if (op == DEL_COMMAND) { @@ -275,13 +502,24 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { return false; } + setIntfVrf(alias, ""); + if (is_lo) { delLoopbackIntf(alias); } - m_appIntfTableProducer.del(alias); - m_stateIntfTable.del(alias); + + if (!subIntfAlias.empty()) + { + removeHostSubIntf(subIntfAlias); + m_subIntfList.erase(subIntfAlias); + + removeSubIntfState(subIntfAlias); + } + + m_appIntfTableProducer.del(subIntfAlias.empty() ? alias : subIntfAlias); + m_stateIntfTable.del(subIntfAlias.empty() ? alias : subIntfAlias); } else { @@ -304,7 +542,7 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, if (op == SET_COMMAND) { /* - * Don't proceed if port/LAG/VLAN and intfGeneral are not ready yet. + * Don't proceed if port/LAG/VLAN/subport and intfGeneral is not ready yet. * The pending task will be checked periodically and retried. */ if (!isIntfStateOk(alias) || !isIntfCreated(alias)) diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 4e82baef5e24..7aab9fe44798 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -7,6 +7,7 @@ #include #include +#include namespace swss { @@ -18,12 +19,13 @@ class IntfMgr : public Orch private: ProducerStateTable m_appIntfTableProducer; - Table m_cfgIntfTable, m_cfgVlanIntfTable; Table m_statePortTable, m_stateLagTable, m_stateVlanTable, m_stateVrfTable, m_stateIntfTable; + std::set m_subIntfList; + void setIntfIp(const std::string &alias, const std::string &opCmd, const IpPrefix &ipPrefix); void setIntfVrf(const std::string &alias, const std::string &vrfName); - bool doIntfGeneralTask(const std::vector& keys, const std::vector& data, const std::string& op); + 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); bool isIntfStateOk(const std::string &alias); @@ -32,6 +34,13 @@ class IntfMgr : public Orch int getIntfIpCount(const std::string &alias); void addLoopbackIntf(const std::string &alias); void delLoopbackIntf(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); + void removeHostSubIntf(const std::string &subIntf); + void setSubIntfStateOk(const std::string &alias); + void removeSubIntfState(const std::string &alias); }; } diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index 54f16861a46f..891373dcc7dc 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -45,6 +45,7 @@ int main(int argc, char **argv) CFG_LAG_INTF_TABLE_NAME, CFG_VLAN_INTF_TABLE_NAME, CFG_LOOPBACK_INTERFACE_TABLE_NAME, + CFG_VLAN_SUB_INTF_TABLE_NAME, }; DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index ce2f2f2d1e29..ee1de1899d40 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -598,7 +598,7 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) } /* -Input sample "BUFFER_PG_TABLE|Ethernet4,Ethernet45|10-15" +Input sample "BUFFER_PG|Ethernet4,Ethernet45|10-15" */ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) { diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index fc84a397521a..f54022412957 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -144,7 +144,7 @@ void IntfsOrch::decreaseRouterIntfsRefCount(const string &alias) alias.c_str(), m_syncdIntfses[alias].ref_count); } -bool IntfsOrch::setRouterIntfsMtu(Port &port) +bool IntfsOrch::setRouterIntfsMtu(const Port &port) { SWSS_LOG_ENTER(); @@ -165,6 +165,36 @@ bool IntfsOrch::setRouterIntfsMtu(Port &port) return true; } +bool IntfsOrch::setRouterIntfsAdminStatus(const Port &port) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.value.booldata = port.m_admin_state_up; + + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE; + 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 V4 admin status to %s, rv:%d", + port.m_alias.c_str(), port.m_admin_state_up == true ? "up" : "down", status); + return false; + } + + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE; + 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 V6 admin status to %s, rv:%d", + port.m_alias.c_str(), port.m_admin_state_up == true ? "up" : "down", status); + return false; + } + + return true; +} + set IntfsOrch:: getSubnetRoutes() { SWSS_LOG_ENTER(); @@ -182,7 +212,7 @@ set IntfsOrch:: getSubnetRoutes() return subnet_routes; } -bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix) +bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix, const bool adminUp, const uint32_t mtu) { SWSS_LOG_ENTER(); @@ -206,6 +236,35 @@ bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPre return false; } } + else + { + if (!ip_prefix && port.m_type == Port::SUBPORT) + { + // port represents a sub interface + // Change sub interface config at run time + bool attrChanged = false; + if (mtu && port.m_mtu != mtu) + { + port.m_mtu = mtu; + attrChanged = true; + + setRouterIntfsMtu(port); + } + + if (port.m_admin_state_up != adminUp) + { + port.m_admin_state_up = adminUp; + attrChanged = true; + + setRouterIntfsAdminStatus(port); + } + + if (attrChanged) + { + gPortsOrch->setPort(alias, port); + } + } + } if (!ip_prefix || m_syncdIntfses[alias].ip_addresses.count(*ip_prefix)) { @@ -290,6 +349,15 @@ bool IntfsOrch::removeIntf(const string& alias, sai_object_id_t vrf_id, const Ip gPortsOrch->decreasePortRefCount(alias); m_syncdIntfses.erase(alias); m_vrfOrch->decreaseVrfRefCount(vrf_id); + + if (port.m_type == Port::SUBPORT) + { + if (!gPortsOrch->removeSubPort(alias)) + { + return false; + } + } + return true; } else @@ -317,6 +385,14 @@ void IntfsOrch::doTask(Consumer &consumer) vector keys = tokenize(kfvKey(t), ':'); string alias(keys[0]); + + bool isSubIntf = false; + size_t found = alias.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found != string::npos) + { + isSubIntf = true; + } + IpPrefix ip_prefix; bool ip_prefix_in_key = false; bool is_lo = !alias.compare(0, strlen(LOOPBACK_PREFIX), LOOPBACK_PREFIX); @@ -329,7 +405,8 @@ void IntfsOrch::doTask(Consumer &consumer) const vector& data = kfvFieldsValues(t); string vrf_name = "", vnet_name = ""; - + uint32_t mtu; + bool adminUp; for (auto idx : data) { const auto &field = fvField(idx); @@ -342,6 +419,39 @@ void IntfsOrch::doTask(Consumer &consumer) { vnet_name = value; } + else if (field == "mtu") + { + try + { + mtu = static_cast(stoul(value)); + } + catch (const std::invalid_argument &e) + { + SWSS_LOG_ERROR("Invalid argument %s to %s()", value.c_str(), e.what()); + continue; + } + catch (const std::out_of_range &e) + { + SWSS_LOG_ERROR("Out of range argument %s to %s()", value.c_str(), e.what()); + continue; + } + } + else if (field == "admin_status") + { + if (value == "up") + { + adminUp = true; + } + else + { + adminUp = false; + + if (value != "down") + { + SWSS_LOG_WARN("Sub interface %s unknown admin status %s", alias.c_str(), value.c_str()); + } + } + } } if (alias == "eth0" || alias == "docker0") @@ -398,9 +508,20 @@ void IntfsOrch::doTask(Consumer &consumer) Port port; if (!gPortsOrch->getPort(alias, port)) { - /* TODO: Resolve the dependency relationship and add ref_count to port */ - it++; - continue; + if (isSubIntf) + { + if (!gPortsOrch->addSubPort(port, alias, adminUp, mtu)) + { + it++; + continue; + } + } + else + { + /* TODO: Resolve the dependency relationship and add ref_count to port */ + it++; + continue; + } } if (m_vnetInfses.find(alias) != m_vnetInfses.end()) @@ -416,7 +537,7 @@ void IntfsOrch::doTask(Consumer &consumer) it++; continue; } - if (!vnet_orch->setIntf(alias, vnet_name, ip_prefix_in_key ? &ip_prefix : nullptr)) + if (!vnet_orch->setIntf(alias, vnet_name, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu)) { it++; continue; @@ -429,7 +550,7 @@ void IntfsOrch::doTask(Consumer &consumer) } else { - if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr)) + if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu)) { it++; continue; @@ -560,35 +681,59 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port) case Port::PHY: case Port::LAG: attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_PORT; + attrs.push_back(attr); break; case Port::VLAN: attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_VLAN; + attrs.push_back(attr); + break; + case Port::SUBPORT: + attr.value.s32 = SAI_ROUTER_INTERFACE_TYPE_SUB_PORT; + attrs.push_back(attr); break; default: SWSS_LOG_ERROR("Unsupported port type: %d", port.m_type); break; } - attrs.push_back(attr); switch(port.m_type) { case Port::PHY: attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; attr.value.oid = port.m_port_id; + attrs.push_back(attr); break; case Port::LAG: attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; attr.value.oid = port.m_lag_id; + attrs.push_back(attr); break; case Port::VLAN: attr.id = SAI_ROUTER_INTERFACE_ATTR_VLAN_ID; attr.value.oid = port.m_vlan_info.vlan_oid; + attrs.push_back(attr); + break; + case Port::SUBPORT: + attr.id = SAI_ROUTER_INTERFACE_ATTR_PORT_ID; + attr.value.oid = port.m_parent_port_id; + attrs.push_back(attr); + + attr.id = SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID; + attr.value.u16 = port.m_vlan_info.vlan_id; + attrs.push_back(attr); + + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE; + attr.value.booldata = port.m_admin_state_up; + attrs.push_back(attr); + + attr.id = SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE; + attr.value.booldata = port.m_admin_state_up; + attrs.push_back(attr); break; default: SWSS_LOG_ERROR("Unsupported port type: %d", port.m_type); break; } - attrs.push_back(attr); attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; attr.value.u32 = port.m_mtu; @@ -850,6 +995,9 @@ void IntfsOrch::doTask(SelectableTimer &timer) case Port::VLAN: type = "SAI_ROUTER_INTERFACE_TYPE_VLAN"; break; + case Port::SUBPORT: + type = "SAI_ROUTER_INTERFACE_TYPE_SUB_PORT"; + break; default: SWSS_LOG_ERROR("Unsupported port type: %d", it->m_type); type = ""; diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 5ac0d047ed3e..22e41a494546 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -39,14 +39,15 @@ class IntfsOrch : public Orch void increaseRouterIntfsRefCount(const string&); void decreaseRouterIntfsRefCount(const string&); - bool setRouterIntfsMtu(Port &port); + bool setRouterIntfsMtu(const Port &port); + bool setRouterIntfsAdminStatus(const Port &port); std::set getSubnetRoutes(); void generateInterfaceMap(); void addRifToFlexCounter(const string&, const string&, const string&); void removeRifFromFlexCounter(const string&, const string&); - bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr); + bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0); bool removeIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr); void addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix); diff --git a/orchagent/orch.h b/orchagent/orch.h index 202aa91e21c1..1ea75b19d35d 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -38,6 +38,7 @@ const char state_db_key_delimiter = '|'; #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" +#define VLAN_SUB_INTERFACE_SEPARATOR "." const int default_orch_pri = 0; diff --git a/orchagent/port.h b/orchagent/port.h index ad264a1db569..8950985523f8 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -44,6 +44,7 @@ class Port LOOPBACK, VLAN, LAG, + SUBPORT, UNKNOWN } ; @@ -86,8 +87,10 @@ class Port sai_object_id_t m_ingress_acl_table_group_id = 0; sai_object_id_t m_egress_acl_table_group_id = 0; vlan_members_t m_vlan_members; + sai_object_id_t m_parent_port_id = 0; sai_port_oper_status_t m_oper_status = SAI_PORT_OPER_STATUS_UNKNOWN; std::set m_members; + std::set m_child_ports; std::vector m_queue_ids; std::vector m_priority_group_ids; sai_port_priority_flow_control_mode_t m_pfc_asym = SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 8d12d1ff917c..16ea7099e85d 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -40,6 +40,7 @@ extern BufferOrch *gBufferOrch; #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 +#define MAX_VALID_VLAN_ID 4094 #define PORT_FLEX_STAT_COUNTER_POLL_MSECS "1000" #define QUEUE_FLEX_STAT_COUNTER_POLL_MSECS "10000" #define QUEUE_WATERMARK_FLEX_STAT_COUNTER_POLL_MSECS "10000" @@ -548,6 +549,101 @@ bool PortsOrch::getAclBindPortId(string alias, sai_object_id_t &port_id) } } +bool PortsOrch::addSubPort(Port &port, const string &alias, const bool &adminUp, const uint32_t &mtu) +{ + size_t found = alias.find(VLAN_SUB_INTERFACE_SEPARATOR); + if (found == string::npos) + { + 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); + sai_vlan_id_t vlan_id; + try + { + vlan_id = static_cast(stoul(vlanId)); + } + catch (const std::invalid_argument &e) + { + SWSS_LOG_ERROR("Invalid argument %s to %s()", vlanId.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()); + return false; + } + if (vlan_id > MAX_VALID_VLAN_ID) + { + SWSS_LOG_ERROR("sub interface %s Port object creation: invalid VLAN id %u", alias.c_str(), vlan_id); + return false; + } + + auto it = m_portList.find(parentAlias); + if (it == m_portList.end()) + { + SWSS_LOG_NOTICE("Sub interface %s Port object creation: parent port %s is not ready", alias.c_str(), parentAlias.c_str()); + return false; + } + Port &parentPort = it->second; + + Port p(alias, Port::SUBPORT); + + p.m_admin_state_up = adminUp; + + if (mtu) + { + p.m_mtu = mtu; + } + else + { + SWSS_LOG_NOTICE("Sub interface %s inherits mtu size %u from parent port %s", alias.c_str(), parentPort.m_mtu, parentAlias.c_str()); + p.m_mtu = parentPort.m_mtu; + } + + p.m_parent_port_id = parentPort.m_port_id; + p.m_vlan_info.vlan_id = vlan_id; + + parentPort.m_child_ports.insert(p.m_alias); + + m_portList[alias] = p; + port = p; + return true; +} + +bool PortsOrch::removeSubPort(const string &alias) +{ + auto it = m_portList.find(alias); + if (it == m_portList.end()) + { + SWSS_LOG_WARN("Sub interface %s Port object not found", alias.c_str()); + return false; + } + Port &port = it->second; + + if (port.m_type != Port::SUBPORT) + { + SWSS_LOG_ERROR("Sub interface %s not of type sub port", alias.c_str()); + return false; + } + + Port parentPort; + if (!getPort(port.m_parent_port_id, parentPort)) + { + SWSS_LOG_WARN("Sub interface %s: parent Port object not found", alias.c_str()); + } + + if (!parentPort.m_child_ports.erase(alias)) + { + SWSS_LOG_WARN("Sub interface %s not associated to parent port %s", alias.c_str(), parentPort.m_alias.c_str()); + } + m_portList[parentPort.m_alias] = parentPort; + + m_portList.erase(it); + return true; +} + void PortsOrch::setPort(string alias, Port p) { m_portList[alias] = p; diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 75831fdaf9e7..0fe3bad48021 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -88,6 +88,9 @@ 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 removeSubPort(const string &alias); private: unique_ptr m_counterTable; unique_ptr
m_portTable; diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 6552f79cc5b0..0dae814e86f5 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -1338,7 +1338,7 @@ VNetOrch::VNetOrch(DBConnector *db, const std::string& tableName, VNET_EXEC op) } } -bool VNetOrch::setIntf(const string& alias, const string name, const IpPrefix *prefix) +bool VNetOrch::setIntf(const string& alias, const string name, const IpPrefix *prefix, const bool adminUp, const uint32_t mtu) { SWSS_LOG_ENTER(); @@ -1353,7 +1353,7 @@ bool VNetOrch::setIntf(const string& alias, const string name, const IpPrefix *p auto *vnet_obj = getTypePtr(name); sai_object_id_t vrf_id = vnet_obj->getVRidIngress(); - return gIntfsOrch->setIntf(alias, vrf_id, prefix); + return gIntfsOrch->setIntf(alias, vrf_id, prefix, adminUp, mtu); } else { diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 49f3413d1f45..965b8cadb844 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -305,7 +305,7 @@ class VNetOrch : public Orch2 public: VNetOrch(DBConnector *db, const std::string&, VNET_EXEC op = VNET_EXEC::VNET_EXEC_VRF); - bool setIntf(const string& alias, const string name, const IpPrefix *prefix = nullptr); + bool setIntf(const string& alias, const string name, const IpPrefix *prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0); bool delIntf(const string& alias, const string name, const IpPrefix *prefix = nullptr); bool isVnetExists(const std::string& name) const diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py new file mode 100644 index 000000000000..36268e8f801b --- /dev/null +++ b/tests/test_sub_port_intf.py @@ -0,0 +1,381 @@ +import pytest +import time +import json +from swsscommon import swsscommon + +CFG_VLAN_SUB_INTF_TABLE_NAME = "VLAN_SUB_INTERFACE" +CFG_PORT_TABLE_NAME = "PORT" + +STATE_PORT_TABLE_NAME = "PORT_TABLE" +STATE_INTERFACE_TABLE_NAME = "INTERFACE_TABLE" + +APP_INTF_TABLE_NAME = "INTF_TABLE" + +ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" +ASIC_ROUTE_ENTRY_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + +ADMIN_STATUS = "admin_status" + + +class TestSubPortIntf(object): + PHYSICAL_PORT_UNDER_TEST = "Ethernet64" + SUB_PORT_INTERFACE_UNDER_TEST = "Ethernet64.10" + + IPV4_ADDR_UNDER_TEST = "10.0.0.33/31" + IPV4_TOME_UNDER_TEST = "10.0.0.33/32" + IPV4_SUBNET_UNDER_TEST = "10.0.0.32/31" + + IPV6_ADDR_UNDER_TEST = "fc00::41/126" + IPV6_TOME_UNDER_TEST = "fc00::41/128" + IPV6_SUBNET_UNDER_TEST = "fc00::40/126" + + def connect_dbs(self, dvs): + self.config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + self.state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + self.appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + def set_parent_port_admin_status(self, port_name, status): + fvs = swsscommon.FieldValuePairs([(ADMIN_STATUS, status)]) + + tbl = swsscommon.Table(self.config_db, CFG_PORT_TABLE_NAME) + tbl.set(port_name, fvs) + + time.sleep(1) + + def create_sub_port_intf_profile(self, sub_port_intf_name): + fvs = swsscommon.FieldValuePairs([(ADMIN_STATUS, "up")]) + + tbl = swsscommon.Table(self.config_db, CFG_VLAN_SUB_INTF_TABLE_NAME) + tbl.set(sub_port_intf_name, fvs) + + time.sleep(1) + + def add_sub_port_intf_ip_addr(self, sub_port_intf_name, ip_addr): + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + + tbl = swsscommon.Table(self.config_db, CFG_VLAN_SUB_INTF_TABLE_NAME) + tbl.set(sub_port_intf_name + "|" + ip_addr, fvs) + + time.sleep(2) + + def set_sub_port_intf_admin_status(self, sub_port_intf_name, status): + fvs = swsscommon.FieldValuePairs([(ADMIN_STATUS, status)]) + + tbl = swsscommon.Table(self.config_db, CFG_VLAN_SUB_INTF_TABLE_NAME) + tbl.set(sub_port_intf_name, fvs) + + time.sleep(1) + + def remove_sub_port_intf_profile(self, sub_port_intf_name): + tbl = swsscommon.Table(self.config_db, CFG_VLAN_SUB_INTF_TABLE_NAME) + tbl._del(sub_port_intf_name) + + time.sleep(1) + + def remove_sub_port_intf_ip_addr(self, sub_port_intf_name, ip_addr): + tbl = swsscommon.Table(self.config_db, CFG_VLAN_SUB_INTF_TABLE_NAME) + tbl._del(sub_port_intf_name + "|" + ip_addr) + + time.sleep(1) + + def get_oids(self, table): + tbl = swsscommon.Table(self.asic_db, table) + return set(tbl.getKeys()) + + def get_newly_created_oid(self, table, old_oids): + new_oids = self.get_oids(table) + oid = list(new_oids - old_oids) + assert len(oid) == 1, "Wrong # of newly created oids: %d, expected #: 1." % (len(oid)) + return oid[0] + + def check_sub_port_intf_key_existence(self, db, table_name, key): + tbl = swsscommon.Table(db, table_name) + + keys = tbl.getKeys() + assert key in keys, "Key %s not exist" % (key) + + def check_sub_port_intf_fvs(self, db, table_name, key, fv_dict): + tbl = swsscommon.Table(db, table_name) + + keys = tbl.getKeys() + assert key in keys + + (status, fvs) = tbl.get(key) + assert status == True + assert len(fvs) >= len(fv_dict) + + for field, value in fvs: + if field in fv_dict: + assert fv_dict[field] == value, \ + "Wrong value for field %s: %s, expected value: %s" % (field, value, fv_dict[field]) + + def check_sub_port_intf_route_entries(self): + ipv4_ip2me_found = False + ipv4_subnet_found = False + ipv6_ip2me_found = False + ipv6_subnet_found = False + + tbl = swsscommon.Table(self.asic_db, ASIC_ROUTE_ENTRY_TABLE) + raw_route_entries = tbl.getKeys() + + for raw_route_entry in raw_route_entries: + route_entry = json.loads(raw_route_entry) + if route_entry["dest"] == self.IPV4_TOME_UNDER_TEST: + ipv4_ip2me_found = True + elif route_entry["dest"] == self.IPV4_SUBNET_UNDER_TEST: + ipv4_subnet_found = True + elif route_entry["dest"] == self.IPV6_TOME_UNDER_TEST: + ipv6_ip2me_found = True + elif route_entry["dest"] == self.IPV6_SUBNET_UNDER_TEST: + ipv6_subnet_found = True + + assert ipv4_ip2me_found and ipv4_subnet_found and ipv6_ip2me_found and ipv6_subnet_found + + def check_sub_port_intf_key_removal(self, db, table_name, key): + tbl = swsscommon.Table(db, table_name) + + keys = tbl.getKeys() + assert key not in keys, "Key %s not removed" % (key) + + def check_sub_port_intf_route_entries_removal(self, removed_route_entries): + tbl = swsscommon.Table(self.asic_db, ASIC_ROUTE_ENTRY_TABLE) + raw_route_entries = tbl.getKeys() + for raw_route_entry in raw_route_entries: + route_entry = json.loads(raw_route_entry) + assert route_entry["dest"] not in removed_route_entries + + def test_sub_port_intf_creation(self, dvs): + self.connect_dbs(dvs) + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(self.PHYSICAL_PORT_UNDER_TEST, "up") + self.create_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + # Verify that sub port interface state ok is pushed to STATE_DB by Intfmgrd + fv_dict = { + "state": "ok", + } + self.check_sub_port_intf_fvs(self.state_db, STATE_PORT_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + # Verify that sub port interface configuration is synced to APPL_DB INTF_TABLE by Intfmgrd + fv_dict = { + ADMIN_STATUS: "up", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + # Verify that a sub port router interface entry is created in ASIC_DB + fv_dict = { + "SAI_ROUTER_INTERFACE_ATTR_TYPE": "SAI_ROUTER_INTERFACE_TYPE_SUB_PORT", + "SAI_ROUTER_INTERFACE_ATTR_OUTER_VLAN_ID": "10", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_MTU": "9100", + } + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + + # Remove a sub port interface + self.remove_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + def test_sub_port_intf_add_ip_addrs(self, dvs): + self.connect_dbs(dvs) + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(self.PHYSICAL_PORT_UNDER_TEST, "up") + self.create_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + + # Verify that ip address state ok is pushed to STATE_DB INTERFACE_TABLE by Intfmgrd + fv_dict = { + "state": "ok", + } + self.check_sub_port_intf_fvs(self.state_db, STATE_INTERFACE_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + "|" + self.IPV4_ADDR_UNDER_TEST, fv_dict) + self.check_sub_port_intf_fvs(self.state_db, STATE_INTERFACE_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + "|" + self.IPV6_ADDR_UNDER_TEST, fv_dict) + + # Verify that ip address configuration is synced to APPL_DB INTF_TABLE by Intfmgrd + fv_dict = { + "scope": "global", + "family": "IPv4", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + ":" + self.IPV4_ADDR_UNDER_TEST, fv_dict) + fv_dict["family"] = "IPv6" + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + ":" + self.IPV6_ADDR_UNDER_TEST, fv_dict) + + # Verify that an IPv4 ip2me route entry is created in ASIC_DB + # Verify that an IPv4 subnet route entry is created in ASIC_DB + # Verify that an IPv6 ip2me route entry is created in ASIC_DB + # Verify that an IPv6 subnet route entry is created in ASIC_DB + self.check_sub_port_intf_route_entries() + + # Remove IP addresses + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + # Remove a sub port interface + self.remove_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + def test_sub_port_intf_admin_status_change(self, dvs): + self.connect_dbs(dvs) + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(self.PHYSICAL_PORT_UNDER_TEST, "up") + self.create_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + fv_dict = { + ADMIN_STATUS: "up", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + fv_dict = { + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_MTU": "9100", + } + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + + # Change sub port interface admin status to down + self.set_sub_port_intf_admin_status(self.SUB_PORT_INTERFACE_UNDER_TEST, "down") + + # Verify that sub port interface admin status change is synced to APPL_DB INTF_TABLE by Intfmgrd + fv_dict = { + ADMIN_STATUS: "down", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + # Verify that sub port router interface entry in ASIC_DB has the updated admin status + fv_dict = { + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE": "false", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE": "false", + "SAI_ROUTER_INTERFACE_ATTR_MTU": "9100", + } + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + + # Change sub port interface admin status to up + self.set_sub_port_intf_admin_status(self.SUB_PORT_INTERFACE_UNDER_TEST, "up") + + # Verify that sub port interface admin status change is synced to APPL_DB INTF_TABLE by Intfmgrd + fv_dict = { + ADMIN_STATUS: "up", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + # Verify that sub port router interface entry in ASIC_DB has the updated admin status + fv_dict = { + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V4_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_ADMIN_V6_STATE": "true", + "SAI_ROUTER_INTERFACE_ATTR_MTU": "9100", + } + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + self.check_sub_port_intf_fvs(self.asic_db, ASIC_RIF_TABLE, rif_oid, fv_dict) + + # Remove IP addresses + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + # Remove a sub port interface + self.remove_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + def test_sub_port_intf_remove_ip_addrs(self, dvs): + self.connect_dbs(dvs) + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(self.PHYSICAL_PORT_UNDER_TEST, "up") + self.create_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + + # Remove IPv4 address + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + + # Verify that IPv4 address state ok is removed from STATE_DB INTERFACE_TABLE by Intfmgrd + self.check_sub_port_intf_key_removal(self.state_db, STATE_INTERFACE_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + "|" + self.IPV4_ADDR_UNDER_TEST) + + # Verify that IPv4 address configuration is removed from APPL_DB INTF_TABLE by Intfmgrd + self.check_sub_port_intf_key_removal(self.appl_db, APP_INTF_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + ":" + self.IPV4_ADDR_UNDER_TEST) + + # Verify that IPv4 subnet route entry is removed from ASIC_DB + # Verify that IPv4 ip2me route entry is removed from ASIC_DB + removed_route_entries = set([self.IPV4_TOME_UNDER_TEST, self.IPV4_SUBNET_UNDER_TEST]) + self.check_sub_port_intf_route_entries_removal(removed_route_entries) + + # Remove IPv6 address + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + # Verify that IPv6 address state ok is removed from STATE_DB INTERFACE_TABLE by Intfmgrd + self.check_sub_port_intf_key_removal(self.state_db, STATE_INTERFACE_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + "|" + self.IPV6_ADDR_UNDER_TEST) + + # Verify that IPv6 address configuration is removed from APPL_DB INTF_TABLE by Intfmgrd + self.check_sub_port_intf_key_removal(self.appl_db, APP_INTF_TABLE_NAME, \ + self.SUB_PORT_INTERFACE_UNDER_TEST + ":" + self.IPV6_ADDR_UNDER_TEST) + + # Verify that IPv6 subnet route entry is removed from ASIC_DB + # Verify that IPv6 ip2me route entry is removed from ASIC_DB + removed_route_entries.update([self.IPV6_TOME_UNDER_TEST, self.IPV6_SUBNET_UNDER_TEST]) + self.check_sub_port_intf_route_entries_removal(removed_route_entries) + + # Verify that sub port router interface entry still exists in ASIC_DB + self.check_sub_port_intf_key_existence(self.asic_db, ASIC_RIF_TABLE, rif_oid) + + # Remove a sub port interface + self.remove_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + def test_sub_port_intf_removal(self, dvs): + self.connect_dbs(dvs) + + old_rif_oids = self.get_oids(ASIC_RIF_TABLE) + + self.set_parent_port_admin_status(self.PHYSICAL_PORT_UNDER_TEST, "up") + self.create_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.add_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + rif_oid = self.get_newly_created_oid(ASIC_RIF_TABLE, old_rif_oids) + + fv_dict = { + "state": "ok", + } + self.check_sub_port_intf_fvs(self.state_db, STATE_PORT_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + fv_dict = { + ADMIN_STATUS: "up", + } + self.check_sub_port_intf_fvs(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST, fv_dict) + + # Remove IP addresses + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV4_ADDR_UNDER_TEST) + self.remove_sub_port_intf_ip_addr(self.SUB_PORT_INTERFACE_UNDER_TEST, self.IPV6_ADDR_UNDER_TEST) + + # Remove a sub port interface + self.remove_sub_port_intf_profile(self.SUB_PORT_INTERFACE_UNDER_TEST) + + # Verify that sub port interface state ok is removed from STATE_DB by Intfmgrd + self.check_sub_port_intf_key_removal(self.state_db, STATE_PORT_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST) + + # Verify that sub port interface configuration is removed from APPL_DB INTF_TABLE by Intfmgrd + self.check_sub_port_intf_key_removal(self.appl_db, APP_INTF_TABLE_NAME, self.SUB_PORT_INTERFACE_UNDER_TEST) + + # Verify that sub port router interface entry is removed from ASIC_DB + self.check_sub_port_intf_key_removal(self.asic_db, ASIC_RIF_TABLE, rif_oid)