diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 23238d314a20..c2ada459eb1c 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -231,7 +231,8 @@ static int process_p2p_hello(struct iih_info *iih) return ISIS_OK; } } - if (!adj || adj->level != iih->calculated_type) { + if (!adj || adj->level != iih->calculated_type || + !(iih->circuit->is_type & iih->circ_type)) { if (!adj) { adj = isis_new_adj(iih->sys_id, NULL, iih->calculated_type, iih->circuit); diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index fe3d8655657b..e02fb07dafe2 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -12,6 +12,7 @@ """ test_isis_topo1.py: Test ISIS topology. """ +import time import datetime import functools import json @@ -314,6 +315,107 @@ def test_isis_neighbor_json(): ), assertmsg +def test_isis_neighbor_state(): + "Check that the neighbor states remain normal when the ISIS type is switched." + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking 'show isis neighbor state on a p2p link'") + + # Establish a P2P link + # When the IS-IS type of r3 is set to level-1-2 and the IS-IS type of r5 is set to level-1, + # it is expected that all neighbors exist and are in the Up state + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + """ + configure + router isis 1 + no redistribute ipv4 connected level-1 + no redistribute ipv4 connected level-2 + no redistribute ipv6 connected level-1 + no redistribute ipv6 connected level-2 + interface r3-eth1 + no isis circuit-type + isis network point-to-point + end + """ + ) + r5 = tgen.gears["r5"] + r5.vtysh_cmd( + """ + configure + router isis 1 + no redistribute ipv4 connected level-1 + no redistribute ipv6 connected level-1 + no redistribute ipv4 table 20 level-1 + interface r5-eth0 + no isis circuit-type + isis network point-to-point + end + """ + ) + result = _check_isis_neighbor_json("r3", "r5", True, "Up") + assert result is True, result + result = _check_isis_neighbor_json("r5", "r3", True, "Up") + assert result is True, result + + # Remove the configuration that affects the switch of IS-IS type. + # Configure the IS-IS type of r3 to transition from level-1-2 to level-2-only, + # while maintaining the IS-IS type of r5 as level-1. + # In this scenario, + # the expectation is that some neighbors do not exist or are in the Initializing state + r3.vtysh_cmd( + """ + configure + router isis 1 + is-type level-2-only + end + """ + ) + result = _check_isis_neighbor_json("r3", "r5", False, "Initializing") + assert result is True, result + result = _check_isis_neighbor_json("r5", "r3", False, "Initializing") + assert result is True, result + + # Restore to initial configuration + logger.info("Checking 'restore to initial configuration'") + r3.vtysh_cmd( + """ + configure + interface r3-eth1 + isis circuit-type level-1 + no isis network point-to-point + router isis 1 + no is-type + redistribute ipv4 connected level-1 + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-1 + redistribute ipv6 connected level-2 + end + """ + ) + r5.vtysh_cmd( + """ + configure + interface r5-eth0 + isis circuit-type level-1 + no isis network point-to-point + router isis 1 + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 + redistribute ipv4 table 20 level-1 + end + """ + ) + result = _check_isis_neighbor_json("r3", "r5", True, "Up") + assert result is True, result + result = _check_isis_neighbor_json("r5", "r3", True, "Up") + assert result is True, result + + def test_isis_database_json(): "Check json struct in show isis database json" @@ -623,6 +725,65 @@ def test_isis_hello_padding_during_adjacency_formation(): assert result is True, result +def _check_isis_neighbor_json( + self, neighbor, neighbor_expected, neighbor_state_expected +): + tgen = get_topogen() + router = tgen.gears[self] + logger.info( + f"check_isis_neighbor_json {router} {neighbor} {neighbor_expected} {neighbor_state_expected}" + ) + + result = _check_isis_neighbor_exist(self, neighbor) + if result == True: + return _check_isis_neighbor_state(self, neighbor, neighbor_state_expected) + elif neighbor_expected == True: + return "{} with expected neighbor {} got none ".format(router.name, neighbor) + else: + return True + + +@retry(retry_timeout=60) +def _check_isis_neighbor_exist(self, neighbor): + tgen = get_topogen() + router = tgen.gears[self] + logger.info(f"check_isis_neighbor_exist {router} {neighbor}") + neighbor_json = router.vtysh_cmd("show isis neighbor json", isjson=True) + + circuits = neighbor_json.get("areas", [])[0].get("circuits", []) + for circuit in circuits: + if "adj" in circuit and circuit["adj"] == neighbor: + return True + + return "The neighbor {} of router {} has not been learned yet ".format( + neighbor, router.name + ) + + +@retry(retry_timeout=5) +def _check_isis_neighbor_state(self, neighbor, neighbor_state_expected): + tgen = get_topogen() + router = tgen.gears[self] + logger.info( + f"check_isis_neighbor_state {router} {neighbor} {neighbor_state_expected}" + ) + neighbor_json = router.vtysh_cmd( + "show isis neighbor {} json".format(neighbor), isjson=True + ) + + circuits = neighbor_json.get("areas", [])[0].get("circuits", []) + for circuit in circuits: + interface = circuit.get("interface", {}) + if "state" in interface: + neighbor_state = interface["state"] + if neighbor_state == neighbor_state_expected: + return True + + return "{} peer with expected neighbor_state {} got {} ".format( + router.name, neighbor_state_expected, neighbor_state + ) + + @retry(retry_timeout=10) def check_last_iih_packet_for_padding(router, expect_padding): logfilename = "{}/{}".format(router.gearlogdir, "isisd.log")