diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 0c390c0cba..c379035d02 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -133,6 +133,15 @@ bool NeighOrch::addNextHop(const IpAddress &ipAddress, const string &alias) ipAddress.to_string().c_str(), alias.c_str()); return false; } + if (p.m_type == Port::SUBPORT) + { + if (!gPortsOrch->getPort(p.m_parent_port_id, p)) + { + SWSS_LOG_ERROR("Neighbor %s seen on sub interface %s whose parent port doesn't exist", + ipAddress.to_string().c_str(), alias.c_str()); + return false; + } + } NextHopKey nexthop = { ipAddress, alias }; assert(!hasNextHop(nexthop)); @@ -185,7 +194,7 @@ bool NeighOrch::addNextHop(const IpAddress &ipAddress, const string &alias) gFgNhgOrch->validNextHopInNextHopGroup(nexthop); // For nexthop with incoming port which has down oper status, NHFLAGS_IFDOWN - // flag Should be set on it. + // flag should be set on it. // This scenario may happen under race condition where buffered neighbor event // is processed after incoming port is down. if (p.m_oper_status == SAI_PORT_OPER_STATUS_DOWN) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 60b3aca063..0a6c3f4b4e 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -635,7 +635,7 @@ bool PortsOrch::addSubPort(Port &port, const string &alias, const bool &adminUp, } if (vlan_id > MAX_VALID_VLAN_ID) { - SWSS_LOG_ERROR("sub interface %s Port object creation failed: invalid VLAN id %u", alias.c_str(), vlan_id); + SWSS_LOG_ERROR("Sub interface %s Port object creation failed: invalid VLAN id %u", alias.c_str(), vlan_id); return false; } @@ -2815,7 +2815,6 @@ void PortsOrch::doLagTask(Consumer &consumer) // Retrieve attributes uint32_t mtu = 0; string learn_mode; - bool operation_status_changed = false; string operation_status; for (auto i : kfvFieldsValues(t)) @@ -2837,16 +2836,6 @@ void PortsOrch::doLagTask(Consumer &consumer) it++; continue; } - - gNeighOrch->ifChangeInformNextHop(alias, - (operation_status == "up")); - Port lag; - if (getPort(alias, lag)) - { - operation_status_changed = - (string_oper_status.at(operation_status) != - lag.m_oper_status); - } } } @@ -2868,19 +2857,13 @@ void PortsOrch::doLagTask(Consumer &consumer) } else { - if (!operation_status.empty()) { - l.m_oper_status = string_oper_status.at(operation_status); + updatePortOperStatus(l, string_oper_status.at(operation_status)); + m_portList[alias] = l; } - if (operation_status_changed) - { - PortOperStateUpdate update; - update.port = l; - update.operStatus = string_oper_status.at(operation_status); - notify(SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, static_cast(&update)); - } + if (mtu != 0) { l.m_mtu = mtu; @@ -4124,22 +4107,35 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) oper_status_strings.at(status).c_str()); if (status == port.m_oper_status) { - return ; + return; } - updateDbPortOperStatus(port, status); + if (port.m_type == Port::PHY) + { + updateDbPortOperStatus(port, status); + } port.m_oper_status = status; bool isUp = status == SAI_PORT_OPER_STATUS_UP; - if (!setHostIntfsOperStatus(port, isUp)) + if (port.m_type == Port::PHY) { - SWSS_LOG_ERROR("Failed to set host interface %s operational status %s", port.m_alias.c_str(), - isUp ? "up" : "down"); + if (!setHostIntfsOperStatus(port, isUp)) + { + SWSS_LOG_ERROR("Failed to set host interface %s operational status %s", port.m_alias.c_str(), + isUp ? "up" : "down"); + } } if (!gNeighOrch->ifChangeInformNextHop(port.m_alias, isUp)) { SWSS_LOG_WARN("Inform nexthop operation failed for interface %s", port.m_alias.c_str()); } + for (const auto &child_port : port.m_child_ports) + { + if (!gNeighOrch->ifChangeInformNextHop(child_port, isUp)) + { + SWSS_LOG_WARN("Inform nexthop operation failed for sub interface %s", child_port.c_str()); + } + } PortOperStateUpdate update = {port, status}; notify(SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, static_cast(&update)); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 4d7ebb25b1..cff208399e 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -37,10 +37,10 @@ static const map oper_status_strings = static const unordered_map string_oper_status = { - { "unknown", SAI_PORT_OPER_STATUS_UNKNOWN }, - { "up", SAI_PORT_OPER_STATUS_UP }, - { "down", SAI_PORT_OPER_STATUS_DOWN }, - { "testing", SAI_PORT_OPER_STATUS_TESTING }, + { "unknown", SAI_PORT_OPER_STATUS_UNKNOWN }, + { "up", SAI_PORT_OPER_STATUS_UP }, + { "down", SAI_PORT_OPER_STATUS_DOWN }, + { "testing", SAI_PORT_OPER_STATUS_TESTING }, { "not present", SAI_PORT_OPER_STATUS_NOT_PRESENT } }; diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py index 124514ec07..ec1b88cb8c 100644 --- a/tests/test_sub_port_intf.py +++ b/tests/test_sub_port_intf.py @@ -1,7 +1,9 @@ import json +import time from dvslib.dvs_common import wait_for_result from dvslib.dvs_database import DVSDatabase +from swsscommon import swsscommon DEFAULT_MTU = "9100" @@ -14,9 +16,15 @@ STATE_INTERFACE_TABLE_NAME = "INTERFACE_TABLE" APP_INTF_TABLE_NAME = "INTF_TABLE" +APP_ROUTE_TABLE_NAME = "ROUTE_TABLE" +APP_PORT_TABLE_NAME = "PORT_TABLE" +APP_LAG_TABLE_NAME = "LAG_TABLE" ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" ASIC_ROUTE_ENTRY_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" +ASIC_NEXT_HOP_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" +ASIC_NEXT_HOP_GROUP_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" +ASIC_NEXT_HOP_GROUP_MEMBER_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" ADMIN_STATUS = "admin_status" @@ -45,6 +53,24 @@ def connect_dbs(self, dvs): self.state_db = dvs.get_state_db() dvs.setup_db() + def get_parent_port_index(self, port_name): + if port_name.startswith(ETHERNET_PREFIX): + idx = int(port_name[len(ETHERNET_PREFIX):]) + else: + assert port_name.startswith(LAG_PREFIX) + idx = int(port_name[len(LAG_PREFIX):]) + return idx + + def set_parent_port_oper_status(self, dvs, port_name, status): + if port_name.startswith(ETHERNET_PREFIX): + 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) + dvs.runcmd("bash -c 'echo " + ("1" if status == "up" else "0") + \ + " > /sys/class/net/" + port_name + "/carrier'") + time.sleep(1) + def set_parent_port_admin_status(self, dvs, port_name, status): fvs = {ADMIN_STATUS: status} @@ -55,10 +81,11 @@ def set_parent_port_admin_status(self, dvs, port_name, status): tbl_name = CFG_LAG_TABLE_NAME self.config_db.create_entry(tbl_name, port_name, fvs) - # follow the treatment in TestRouterInterface::set_admin_status - if tbl_name == CFG_LAG_TABLE_NAME: - dvs.runcmd("bash -c 'echo " + ("1" if status == "up" else "0") + \ - " > /sys/class/net/" + port_name + "/carrier'") + if port_name.startswith(ETHERNET_PREFIX): + self.set_parent_port_oper_status(dvs, port_name, "down") + self.set_parent_port_oper_status(dvs, port_name, "up") + else: + self.set_parent_port_oper_status(dvs, port_name, "up") def create_sub_port_intf_profile(self, sub_port_intf_name): fvs = {ADMIN_STATUS: "up"} @@ -98,6 +125,29 @@ def get_newly_created_oid(self, table, old_oids): oid = [ids for ids in new_oids if ids not in old_oids] return oid[0] + def get_ip_prefix_nhg_oid(self, ip_prefix): + def _access_function(): + route_entry_found = False + + raw_route_entry_keys = self.asic_db.get_keys(ASIC_ROUTE_ENTRY_TABLE) + for raw_route_entry_key in raw_route_entry_keys: + route_entry_key = json.loads(raw_route_entry_key) + if route_entry_key["dest"] == ip_prefix: + route_entry_found = True + break + + return (route_entry_found, raw_route_entry_key) + + (route_entry_found, raw_route_entry_key) = wait_for_result(_access_function, DVSDatabase.DEFAULT_POLLING_CONFIG) + + fvs = self.asic_db.get_entry(ASIC_ROUTE_ENTRY_TABLE, raw_route_entry_key) + + nhg_oid = fvs.get( "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID", "") + assert nhg_oid != "" + assert nhg_oid != "oid:0x0" + + return nhg_oid + def check_sub_port_intf_key_existence(self, db, table_name, key): db.wait_for_matching_keys(table_name, [key]) @@ -468,6 +518,319 @@ def test_sub_port_intf_mtu(self, dvs): self._test_sub_port_intf_mtu(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST) self._test_sub_port_intf_mtu(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST) + def create_nhg_router_intfs(self, dvs, parent_port_prefix, parent_port_idx_base, vlan_id, nhop_num): + ifnames = [] + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + if vlan_id != 0: + port_name = "{}{}.{}".format(parent_port_prefix, parent_port_idx, vlan_id) + else: + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + ip_addr = "10.{}.{}.0/31".format(parent_port_idx, vlan_id) + if vlan_id != 0: + self.create_sub_port_intf_profile(port_name) + self.add_sub_port_intf_ip_addr(port_name, ip_addr) + else: + dvs.add_ip_address(port_name, ip_addr) + + ifnames.append(port_name) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + return ifnames + + def create_nhg_next_hop_objs(self, dvs, parent_port_prefix, parent_port_idx_base, vlan_id, nhop_num): + nhop_ips = [] + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + nhop_ip = "10.{}.{}.1".format(parent_port_idx, vlan_id) + nhop_mac = "00:00:00:{:02d}:{}:01".format(parent_port_idx, vlan_id) + dvs.runcmd("arp -s " + nhop_ip + " " + nhop_mac) + + nhop_ips.append(nhop_ip) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + return nhop_ips + + def remove_nhg_router_intfs(self, dvs, parent_port_prefix, parent_port_idx_base, vlan_id, nhop_num): + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + if vlan_id != 0: + port_name = "{}{}.{}".format(parent_port_prefix, parent_port_idx, vlan_id) + else: + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + ip_addr = "10.{}.{}.0/31".format(parent_port_idx, vlan_id) + if vlan_id != 0: + # Remove IP address on sub port interface + self.remove_sub_port_intf_ip_addr(port_name, ip_addr) + # Remove sub port interface + self.remove_sub_port_intf_profile(port_name) + else: + dvs.remove_ip_address(port_name, ip_addr) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + def remove_nhg_next_hop_objs(self, dvs, parent_port_prefix, parent_port_idx_base, vlan_id, nhop_num): + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + nhop_ip = "10.{}.{}.1".format(parent_port_idx, vlan_id) + dvs.runcmd("arp -d " + nhop_ip) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + def check_nhg_members_on_parent_port_oper_status_change(self, dvs, parent_port_prefix, parent_port_idx_base, status, \ + nhg_oid, nhop_num, create_intf_on_parent_port): + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_oper_status(dvs, port_name, status) + + # Verify parent port oper status + fv_dict = { + "oper_status" : status, + } + if parent_port_prefix == ETHERNET_PREFIX: + self.check_sub_port_intf_fvs(self.app_db, APP_PORT_TABLE_NAME, port_name, fv_dict) + else: + self.check_sub_port_intf_fvs(self.app_db, APP_LAG_TABLE_NAME, port_name, fv_dict) + + # Verify next hop group member # in ASIC_DB + if status == "up": + nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, \ + 1 + i if create_intf_on_parent_port == False else (1 + i) * 2) + else: + assert status == "down" + nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, \ + (nhop_num - 1) - i if create_intf_on_parent_port == False else \ + ((nhop_num - 1) - i) * 2) + + # Verify that next hop group members in ASIC_DB all + # belong to the next hop group of the specified oid + fv_dict = { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": nhg_oid, + } + for nhg_member_oid in nhg_member_oids: + self.check_sub_port_intf_fvs(self.asic_db, ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, nhg_member_oid, fv_dict) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + def _test_sub_port_intf_nhg_accel(self, dvs, sub_port_intf_name, nhop_num=3, create_intf_on_parent_port=False): + substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = substrs[0] + 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) + parent_port_prefix = LAG_PREFIX + parent_port_idx_base = self.get_parent_port_index(parent_port) + + # Set parent ports admin status up + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_admin_status(dvs, port_name, "up") + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + ifnames = [] + # Create sub port interfaces + ifnames.extend(self.create_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num)) + + # Create router interfaces on parent ports + if create_intf_on_parent_port == True: + ifnames.extend(self.create_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num)) + + nhop_ips = [] + nhop_cnt = len(self.asic_db.get_keys(ASIC_NEXT_HOP_TABLE)) + # Create next hop objects on sub port interfaces + nhop_ips.extend(self.create_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num)) + + # Create next hop objects on router interfaces + if create_intf_on_parent_port == True: + nhop_ips.extend(self.create_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num)) + + self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_TABLE, nhop_cnt + nhop_num if create_intf_on_parent_port == False else nhop_cnt + nhop_num * 2) + + # Create multi-next-hop route entry + rt_tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_ROUTE_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([("nexthop", ",".join(nhop_ips)), ("ifname", ",".join(ifnames))]) + ip_prefix = "2.2.2.0/24" + rt_tbl.set(ip_prefix, fvs) + + # Verify route entry created in ASIC_DB and get next hop group oid + nhg_oid = self.get_ip_prefix_nhg_oid(ip_prefix) + + # Verify next hop group of the specified oid created in ASIC_DB + self.check_sub_port_intf_key_existence(self.asic_db, ASIC_NEXT_HOP_GROUP_TABLE, nhg_oid) + + # Verify next hop group members created in ASIC_DB + nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, \ + nhop_num if create_intf_on_parent_port == False else nhop_num * 2) + + # Verify that next hop group members all belong to the next hop group of the specified oid + fv_dict = { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": nhg_oid, + } + for nhg_member_oid in nhg_member_oids: + self.check_sub_port_intf_fvs(self.asic_db, ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, nhg_member_oid, fv_dict) + + # Bring parent ports oper status down one at a time, and verify next hop group members + self.check_nhg_members_on_parent_port_oper_status_change(dvs, parent_port_prefix, parent_port_idx_base, "down", \ + nhg_oid, nhop_num, create_intf_on_parent_port) + + # Bring parent ports oper status up one at a time, and verify next hop group members + self.check_nhg_members_on_parent_port_oper_status_change(dvs, parent_port_prefix, parent_port_idx_base, "up", \ + nhg_oid, nhop_num, create_intf_on_parent_port) + + # Clean up + rif_cnt = len(self.asic_db.get_keys(ASIC_RIF_TABLE)) + + # Remove sub port interfaces + self.remove_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num) + + # Remove router interfaces on parent ports + if create_intf_on_parent_port == True: + self.remove_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num) + + # Remove ecmp route entry + rt_tbl._del(ip_prefix) + + # Removal of router interfaces indicates the proper removal of nhg, nhg members, next hop objects, and neighbor entries + self.asic_db.wait_for_n_keys(ASIC_RIF_TABLE, rif_cnt - nhop_num if create_intf_on_parent_port == False else rif_cnt - nhop_num * 2) + + # Make sure parent port is oper status up + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_oper_status(dvs, port_name, "up") + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + def test_sub_port_intf_nhg_accel(self, dvs): + self.connect_dbs(dvs) + + self._test_sub_port_intf_nhg_accel(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST) + self._test_sub_port_intf_nhg_accel(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST, create_intf_on_parent_port=True) + self._test_sub_port_intf_nhg_accel(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST) + self._test_sub_port_intf_nhg_accel(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST, create_intf_on_parent_port=True) + + def _test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(self, dvs, sub_port_intf_name, nhop_num=3, create_intf_on_parent_port=False): + substrs = sub_port_intf_name.split(VLAN_SUB_INTERFACE_SEPARATOR) + parent_port = substrs[0] + 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) + parent_port_prefix = LAG_PREFIX + parent_port_idx_base = self.get_parent_port_index(parent_port) + + # Set parent ports admin status up + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_admin_status(dvs, port_name, "up") + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + ifnames = [] + # Create sub port interfaces + ifnames.extend(self.create_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num)) + # Create router interfaces on parent ports + if create_intf_on_parent_port == True: + ifnames.extend(self.create_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num)) + + # Bring parent port oper status down one at a time + # Verify next hop group members created after processing pending tasks + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_oper_status(dvs, port_name, "down") + + # Verify parent port oper status down + fv_dict = { + "oper_status" : "down", + } + if parent_port_prefix == ETHERNET_PREFIX: + self.check_sub_port_intf_fvs(self.app_db, APP_PORT_TABLE_NAME, port_name, fv_dict) + else: + self.check_sub_port_intf_fvs(self.app_db, APP_LAG_TABLE_NAME, port_name, fv_dict) + + # Mimic pending neighbor task + nhop_ips = [] + nhop_cnt = len(self.asic_db.get_keys(ASIC_NEXT_HOP_TABLE)) + # Create next hop objects on sub port interfaces + nhop_ips.extend(self.create_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num)) + # Create next hop objects on router interfaces + if create_intf_on_parent_port == True: + nhop_ips.extend(self.create_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num)) + self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_TABLE, nhop_cnt + nhop_num if create_intf_on_parent_port == False else nhop_cnt + nhop_num * 2) + + # Mimic pending multi-next-hop route entry task + rt_tbl = swsscommon.ProducerStateTable(self.app_db.db_connection, APP_ROUTE_TABLE_NAME) + fvs = swsscommon.FieldValuePairs([("nexthop", ",".join(nhop_ips)), ("ifname", ",".join(ifnames))]) + ip_prefix = "2.2.2.0/24" + rt_tbl.set(ip_prefix, fvs) + + # Verify route entry created in ASIC_DB and get next hop group oid + nhg_oid = self.get_ip_prefix_nhg_oid(ip_prefix) + + # Verify next hop group of the specified oid created in ASIC_DB + self.check_sub_port_intf_key_existence(self.asic_db, ASIC_NEXT_HOP_GROUP_TABLE, nhg_oid) + + # Verify next hop group member # created in ASIC_DB + nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, \ + (nhop_num - 1) - i if create_intf_on_parent_port == False else ((nhop_num - 1) - i) * 2) + + # Verify that next hop group members all belong to the next hop group of the specified oid + fv_dict = { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": nhg_oid, + } + for nhg_member_oid in nhg_member_oids: + self.check_sub_port_intf_fvs(self.asic_db, ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, nhg_member_oid, fv_dict) + + nhop_cnt = len(self.asic_db.get_keys(ASIC_NEXT_HOP_TABLE)) + # Remove next hop objects on sub port interfaces + self.remove_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num) + # Remove next hop objects on router interfaces + if create_intf_on_parent_port == True: + self.remove_nhg_next_hop_objs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num) + # Remove ecmp route entry + rt_tbl._del(ip_prefix) + # Removal of next hop objects indicates the proper removal of route entry, nhg, and nhg members + self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_TABLE, nhop_cnt - nhop_num if create_intf_on_parent_port == False else nhop_cnt - nhop_num * 2) + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + # Clean up + rif_cnt = len(self.asic_db.get_keys(ASIC_RIF_TABLE)) + # Remove sub port interfaces + self.remove_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, int(vlan_id), nhop_num) + # Remove router interfaces on parent ports + if create_intf_on_parent_port == True: + self.remove_nhg_router_intfs(dvs, parent_port_prefix, parent_port_idx_base, 0, nhop_num) + self.asic_db.wait_for_n_keys(ASIC_RIF_TABLE, rif_cnt - nhop_num if create_intf_on_parent_port == False else rif_cnt - nhop_num * 2) + + # Make sure parent port oper status is up + parent_port_idx = parent_port_idx_base + for i in range(0, nhop_num): + port_name = "{}{}".format(parent_port_prefix, parent_port_idx) + self.set_parent_port_oper_status(dvs, port_name, "up") + + parent_port_idx += (4 if parent_port_prefix == ETHERNET_PREFIX else 1) + + def test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(self, dvs): + self.connect_dbs(dvs) + + self._test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST) + self._test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(dvs, self.SUB_PORT_INTERFACE_UNDER_TEST, create_intf_on_parent_port=True) + self._test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST) + self._test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(dvs, self.LAG_SUB_PORT_INTERFACE_UNDER_TEST, create_intf_on_parent_port=True) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying