def report_memory_leaks(self, filename_prefix, testscript): "Report Memory Leaks to file prefixed with given string" leakfound = False filename = filename_prefix + re.sub(r"\.py", "", testscript) + ".txt" for daemon in self.daemons: if (self.daemons[daemon] == 1): log = self.getStdErr(daemon) if "memstats" in log: # Found memory leak logger.info('\nRouter {} {} StdErr Log:\n{}'.format( self.name, daemon, log)) if not leakfound: leakfound = True # Check if file already exists fileexists = os.path.isfile(filename) leakfile = open(filename, "a") if not fileexists: # New file - add header leakfile.write("# Memory Leak Detection for topotest %s\n\n" % testscript) leakfile.write("## Router %s\n" % self.name) leakfile.write("### Process %s\n" % daemon) log = re.sub("core_handler: ", "", log) log = re.sub(r"(showing active allocations in memory group [a-zA-Z0-9]+)", r"\n#### \1\n", log) log = re.sub("memstats: ", " ", log) leakfile.write(log) leakfile.write("\n") if leakfound: leakfile.close()
def test_bgp_fast_reconvergence(): "Assert that BGP is converging after setting a link down." tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info('waiting for BGP re convergence') # Check that BGP converged quickly. for router in tgen.routers().values(): ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name) expected = json.loads(open(ref_file).read()) # Load the same file as previous test, but set networks to None # to test absence. if router.name == 'r1': expected['routes']['10.254.254.2/32'] = None expected['routes']['10.254.254.3/32'] = None expected['routes']['10.254.254.4/32'] = None else: expected['routes']['10.254.254.1/32'] = None test_func = partial(topotest.router_json_cmp, router, 'show ip bgp vrf {}-cust1 json'.format(router.name), expected) _, res = topotest.run_and_expect( test_func, None, count=3, wait=1 ) assertmsg = '{}: bgp did not converge'.format(router.name) assert res is None, assertmsg
def test_bfd_comes_back_again(): """ Assert that BFD notices the bfd link up and that ipv6 entries appear back """ tgen = get_topogen() logger.info('re-adding IPv6 address from r2 to simulate connectivity is back') # adds back r2-eth0 ipv6 address cmd = 'vtysh -c \"configure terminal\" -c \"interface r2-eth1\" -c "ipv6 address 2001:db8:4::2/64\"' tgen.net['r2'].cmd(cmd) # Wait the minimum time we can before checking that BGP/BFD # converged. logger.info('waiting for BFD to converge up') # Check that BGP converged quickly. for router in tgen.routers().values(): if router.name == 'r2': continue json_file = '{}/{}/peers.json'.format(CWD, router.name) expected = json.loads(open(json_file).read()) test_func = partial(topotest.router_json_cmp, router, 'show bfd peers json', expected) _, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg
def test_bgp_convergence(): "Test for BGP topology convergence" tgen = get_topogen() # uncomment if you want to troubleshoot # tgen.mininet_cli() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info('waiting for bgp convergence') # Expected result router = tgen.gears['r1'] if router.has_version('<', '3.0'): reffile = os.path.join(CWD, 'r1/summary20.txt') else: reffile = os.path.join(CWD, 'r1/summary.txt') expected = json.loads(open(reffile).read()) test_func = functools.partial(topotest.router_json_cmp, router, 'show bgp vrf r1-cust1 summary json', expected) _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) assertmsg = 'BGP router network did not converge' assert res is None, assertmsg
def test_protocols_convergence(): """ Assert that all protocols have converged before checking for the BFD statuses as they depend on it. """ tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) # Check IPv6 routing tables. logger.info("Checking IPv6 routes for convergence") for router in tgen.routers().values(): if router.name == 'r2': continue json_file = '{}/{}/ipv6_routes.json'.format(CWD, router.name) if not os.path.isfile(json_file): logger.info('skipping file {}'.format(json_file)) continue expected = json.loads(open(json_file).read()) test_func = partial(topotest.router_json_cmp, router, 'show ipv6 route json', expected) _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg
def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(ISISTopo1, mod.__name__) tgen.start_topology() # For all registered routers, load the zebra configuration file for rname, router in tgen.routers().iteritems(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) ) router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, '{}/isisd.conf'.format(rname)) ) # After loading the configurations, this function loads configured daemons. tgen.start_router() has_version_20 = False for router in tgen.routers().values(): if router.has_version('<', '3'): has_version_20 = True if has_version_20: logger.info('Skipping ISIS tests for FRR 2.0') tgen.set_error('ISIS has convergence problems with IPv6')
def checkRouterVersion(self, cmpop, version): """ Compares router version using operation `cmpop` with `version`. Valid `cmpop` values: * `>=`: has the same version or greater * '>': has greater version * '=': has the same version * '<': has a lesser version * '<=': has the same version or lesser Usage example: router.checkRouterVersion('>', '1.0') """ # Make sure we have version information first if self.version == None: self.version = self.cmd(os.path.join(self.daemondir, 'bgpd')+' -v').split()[2] logger.info('{}: running version: {}'.format(self.name,self.version)) rversion = self.version if rversion is None: return False result = version_cmp(rversion, version) if cmpop == '>=': return result >= 0 if cmpop == '>': return result > 0 if cmpop == '=': return result == 0 if cmpop == '<': return result < 0 if cmpop == '<': return result < 0 if cmpop == '<=': return result <= 0
def test_ospf_link_down(): "Test OSPF convergence after a link goes down" tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip('skipped because of router(s) failure') # Simulate a network down event on router3 switch3 interface. router3 = tgen.gears['r3'] topotest.interface_set_status(router3, 'r3-eth0', ifaceaction=False, vrf_name='r3-cust1') # Expect convergence on all routers for rname, router in tgen.routers().iteritems(): logger.info('Waiting for router "%s" convergence after link failure', rname) # Load expected results from the command reffile = os.path.join(CWD, '{}/ospfroute_down.txt'.format(rname)) expected = open(reffile).read() # Run test function until we get an result. Wait at most 60 seconds. test_func = partial(topotest.router_output_cmp, router, 'show ip ospf vrf {0}-cust1 route'.format(rname), expected) result, diff = topotest.run_and_expect(test_func, '', count=140, wait=0.5) assertmsg = 'OSPF did not converge on {}:\n{}'.format(rname, diff) assert result, assertmsg
def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() tgen.stop_topology() logger.info('\n\n---- OSPF Segment Routing tests End ----\n')
def loadConf(self, daemon, source=None, param=None): # print "Daemons before:", self.daemons if daemon in self.daemons.keys(): self.daemons[daemon] = 1 if param is not None: self.daemons_options[daemon] = param if source is None: self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon)) self.waitOutput() else: self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon)) self.waitOutput() self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon)) self.waitOutput() self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon)) self.waitOutput() if (daemon == 'zebra') and (self.daemons['staticd'] == 0): # Add staticd with zebra - if it exists staticd_path = os.path.join(self.daemondir, 'staticd') if os.path.isfile(staticd_path): self.daemons['staticd'] = 1 self.daemons_options['staticd'] = '' # Auto-Started staticd has no config, so it will read from zebra config else: logger.info('No daemon {} known'.format(daemon))
def _init_topo(self, cls): """ Initialize the topogily provided by the user. The user topology class must call get_topogen() during build() to get the topogen object. """ # Set the global variable so the test cases can access it anywhere set_topogen(self) # Test for MPLS Kernel modules available self.hasmpls = False if not topotest.module_present('mpls-router'): logger.info('MPLS tests will not run (missing mpls-router kernel module)') elif not topotest.module_present('mpls-iptunnel'): logger.info('MPLS tests will not run (missing mpls-iptunnel kernel module)') else: self.hasmpls = True # Load the default topology configurations self._load_config() # Initialize the API self._mininet_reset() cls() self.net = Mininet(controller=None, topo=self.topo) for gear in self.gears.values(): gear.net = self.net
def setup_module(module): tgen = Topogen(BGPECMPTopo1, module.__name__) tgen.start_topology() # Starting Routers router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) ) router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, '{}/bgpd.conf'.format(rname)) ) router.start() # Starting Hosts and init ExaBGP on each of them topotest.sleep(10, 'starting BGP on all {} peers'.format(total_ebgp_peers)) peer_list = tgen.exabgp_peers() for pname, peer in peer_list.iteritems(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, 'exabgp.env') peer.start(peer_dir, env_file) logger.info(pname)
def test_isis_convergence(): "Wait for the protocol to converge before starting to test" tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info("waiting for ISIS protocol to converge") # Code to generate the json files. # for rname, router in tgen.routers().iteritems(): # open('/tmp/{}_topology.json'.format(rname), 'w').write( # json.dumps(show_isis_topology(router), indent=2, sort_keys=True) # ) for rname, router in tgen.routers().iteritems(): filename = '{0}/{1}/{1}_topology.json'.format(CWD, rname) expected = json.loads(open(filename).read()) def compare_isis_topology(router, expected): "Helper function to test ISIS topology convergence." actual = show_isis_topology(router) return topotest.json_cmp(actual, expected) test_func = functools.partial(compare_isis_topology, router, expected) (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120) assert result, 'ISIS did not converge on {}:\n{}'.format(rname, diff)
def test_pim_send_mcast_stream(): "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate" logger.info("Establish a Mcast stream from r2->r1 and then ensure S,G created") tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) r2 = tgen.gears['r2'] r1 = tgen.gears['r1'] # Let's establish a S,G stream from r2 -> r1 CWD = os.path.dirname(os.path.realpath(__file__)) r2.run("{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r2-eth0 > /tmp/bar".format(CWD)) # Let's see that it shows up and we have established some basic state out = r1.vtysh_cmd("show ip pim upstream json", isjson=True) expected = { '229.1.1.1': { '10.0.20.2': { 'firstHopRouter': 1, 'joinState': 'NotJoined', 'regState': 'RegPrune', 'inboundInterface': 'r1-eth0', } } } assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
def test_pim_igmp_report(): "Send a igmp report from r2->r1 and ensure that the *,G state is created on r1" logger.info("Send a igmp report from r2-r1 and ensure *,G created") tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) r2 = tgen.gears['r2'] r1 = tgen.gears['r1'] # Let's send a igmp report from r2->r1 CWD = os.path.dirname(os.path.realpath(__file__)) r2.run("{}/mcast-rx.py 229.1.1.2 r2-eth0 &".format(CWD)) out = r1.vtysh_cmd("show ip pim upstream json", isjson=True) expected = { '229.1.1.2': { '*': { 'sourceIgmp': 1, 'joinState': 'Joined', 'regState': 'RegNoInfo', 'sptBit': 0, } } } assert topotest.json_cmp(out, expected) is None, 'failed to converge pim'
def setup_module(mod): "Sets up the pytest environment" logger.info('\n\n---- Starting OSPF Segment Routing tests ----\n') tgen = Topogen(OspfSrTopo, mod.__name__) tgen.start_topology() router_list = tgen.routers() for rname, router in router_list.iteritems(): router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname)) ) router.load_config( TopoRouter.RD_OSPF, os.path.join(CWD, '{}/ospfd.conf'.format(rname)) ) # Initialize all routers. tgen.start_router() # Verify that version, MPLS and Segment Routing are OK for router in router_list.values(): # Check for Version if router.has_version('<', '4'): tgen.set_error('Unsupported FRR version') break # Check that Segment Routing is available output = tgen.gears[router.name].vtysh_cmd( "show ip ospf database segment-routing json") if output.find("Unknown") != -1: tgen.set_error('Segment Routing is not available')
def test_isis_route6_installation(): "Check whether all expected routes are present" 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 routers for installed ISIS IPv6 routes') # Check for routes in 'show ip route json' for rname, router in tgen.routers().iteritems(): filename = '{0}/{1}/{1}_route6.json'.format(CWD, rname) expected = json.loads(open(filename, 'r').read()) actual = router.vtysh_cmd('show ipv6 route json', isjson=True) # Older FRR versions don't list interfaces in some ISIS routes if router.has_version('<', '3.1'): for network, routes in expected.iteritems(): for route in routes: # Older versions display different metrics for IPv6 routes route.pop('metric', None) if route['protocol'] != 'isis': continue for nexthop in route['nexthops']: nexthop.pop('interfaceIndex', None) nexthop.pop('interfaceName', None) assertmsg = "Router '{}' routes mismatch".format(rname) assert topotest.json_cmp(actual, expected) is None, assertmsg
def test_ospf_convergence(): logger.info("Test: check OSPF adjacencies") tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) # Old output (before FRR PR1383) didn't show a list of neighbors. # Check for dict object and compare to old output if this is the case tgen = get_topogen() router = tgen.gears['r1'] output = router.vtysh_cmd("show ip ospf neighbor json", isjson=True) # We could have either old format (without "neighbors" and direct list # of IP's or new format from PR1659 with "neighbors". # Trying old formats first and fall back to new format # # New format: neighbors have dict instead of list of dicts (PR1723). if output.has_key('neighbors'): if isinstance(output['neighbors'], dict): reffile = "show_ip_ospf_neighbor.json" else: reffile = "show_ip_ospf_neighbor.ref" else: if isinstance(output["2.2.2.2"], dict): reffile = "show_ip_ospf_neighbor.ref-old-nolist" else: reffile = "show_ip_ospf_neighbor.ref-no-neigh" for rname in ['r1', 'r2', 'r3']: router_compare_json_output(rname, "show ip ospf neighbor json", reffile)
def test_call_mininet_cli(): "Dummy test that just calls mininet CLI so we can interact with the build." tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info('calling mininet CLI') tgen.mininet_cli()
def ltemplatePreRouterStartHook(): cc = ltemplateRtrCmd() tgen = get_topogen() logger.info('pre router-start hook') #check for normal init if len(tgen.net) == 1: logger.info('Topology not configured, skipping setup') return False return True
def test_ldp_pseudowires(): logger.info("Test: verify LDP pseudowires") tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) for rname in ['r1', 'r2', 'r3']: router_compare_json_output(rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref")
def set_error(self, message, code=None): "Sets an error message and signal other tests to skip." logger.info(message) # If no code is defined use a sequential number if code is None: code = len(self.errorsd) self.errorsd[code] = message self.errors += '\n{}: {}'.format(code, message)
def test_rib(): logger.info("Test: verify RIB") tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) for rname in ['r1', 'r2', 'r3']: router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
def test_ldp_bindings(): logger.info("Test: verify LDP bindings") tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) for rname in ['r1', 'r2', 'r3']: router_compare_json_output(rname, "show mpls ldp binding json", "show_ldp_binding.ref")
def sleep(amount, reason=None): """ Sleep wrapper that registers in the log the amount of sleep """ if reason is None: logger.info('Sleeping for {} seconds'.format(amount)) else: logger.info(reason + ' ({} seconds)'.format(amount)) time.sleep(amount)
def test_ospf_json(): "Test 'show ip ospf json' output for coherency." tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip('skipped because of router(s) failure') for rname, router in tgen.routers().iteritems(): logger.info('Comparing router "%s" "show ip ospf vrf %s-cust1 json" output', router.name, router.name) expected = { '{}-cust1'.format(router.name) : { 'vrfName': '{}-cust1'.format(router.name), 'routerId': '10.0.255.{}'.format(rname[1:]), 'tosRoutesOnly': True, 'rfc2328Conform': True, 'spfScheduleDelayMsecs': 0, 'holdtimeMinMsecs': 50, 'holdtimeMaxMsecs': 5000, 'lsaMinIntervalMsecs': 5000, 'lsaMinArrivalMsecs': 1000, 'writeMultiplier': 20, 'refreshTimerMsecs': 10000, 'asbrRouter': 'injectingExternalRoutingInformation', 'attachedAreaCounter': 1, 'areas': {} } } # Area specific additional checks if router.name == 'r1' or router.name == 'r2' or router.name == 'r3': expected['{}-cust1'.format(router.name)]['areas']['0.0.0.0'] = { 'areaIfActiveCounter': 2, 'areaIfTotalCounter': 2, 'authentication': 'authenticationNone', 'backbone': True, 'lsaAsbrNumber': 0, 'lsaNetworkNumber': 1, 'lsaNssaNumber': 0, 'lsaNumber': 4, 'lsaOpaqueAreaNumber': 0, 'lsaOpaqueLinkNumber': 0, 'lsaRouterNumber': 3, 'lsaSummaryNumber': 0, 'nbrFullAdjacentCounter': 2, } test_func = partial(topotest.router_json_cmp, router, 'show ip ospf vrf {0}-cust1 json'.format(rname), expected) _, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(rname) assert diff is None, assertmsg
def ltemplatePreRouterStartHook(): cc = ltemplateRtrCmd() tgen = get_topogen() logger.info('pre router-start hook') #check for mpls if tgen.hasmpls != True: logger.info('MPLS not available, skipping setup') return False #check for normal init if len(tgen.net) == 1: logger.info('Topology not configured, skipping setup') return False #configure r2 mpls interfaces intfs = ['lo', 'r2-eth0', 'r2-eth1', 'r2-eth2'] for intf in intfs: cc.doCmd(tgen, 'r2', 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) #configure MPLS rtrs = ['r1', 'r3', 'r4'] cmds = ['echo 1 > /proc/sys/net/mpls/conf/lo/input'] for rtr in rtrs: router = tgen.gears[rtr] for cmd in cmds: cc.doCmd(tgen, rtr, cmd) intfs = ['lo', rtr+'-eth0', rtr+'-eth4'] for intf in intfs: cc.doCmd(tgen, rtr, 'echo 1 > /proc/sys/net/mpls/conf/{}/input'.format(intf)) logger.info('setup mpls input') return True
def vrf_setup(router, eth_in, vrf, vrf_table): cmds = ['ip link set dev lo up', 'echo 10000 > /proc/sys/net/mpls/platform_labels', 'ip link add dev ' + vrf + ' type vrf table ' + vrf_table, 'ip link set ' + vrf + ' up', 'ip link set ' + eth_in + ' vrf ' + vrf, 'echo 1 > /proc/sys/net/mpls/conf/' + vrf + '/input' ] vrf_destroy(router, vrf) for cmd in cmds: logger.info('[vrf_setup] cmd: ' + cmd) out = router.run(cmd) if out != None and len(out) > 0: logger.info('[vrf_setup] "{}" error: out="{}"'.format(cmd, out))
def test_bgp_vrf_learn(): "Test daemon learnt VRF context" tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) # Expected result output = tgen.gears['r1'].vtysh_cmd("show vrf", isjson=False) logger.info('output is: {}'.format(output)) output = tgen.gears['r1'].vtysh_cmd("show bgp vrfs", isjson=False) logger.info('output is: {}'.format(output))
def test_ldp_pseudowires_after_link_down(): logger.info("Test: verify LDP pseudowires after r1-r2 link goes down") tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) # Shut down r1-r2 link */ tgen = get_topogen() tgen.gears['r1'].peer_link_enable('r1-eth1', False) # check if the pseudowire is still up (using an alternate path for nexthop resolution) for rname in ['r1', 'r2', 'r3']: router_compare_json_output(rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref")
def _check_srv6_locator(router, expected_locator_file): logger.info("checking zebra locator status") output = json.loads( router.vtysh_cmd("show segment-routing srv6 locator json")) expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) return topotest.json_cmp(output, expected)
def setup_module(module): tgen = Topogen(BGPVRFNETNSTopo1, module.__name__) tgen.start_topology() # Get r1 reference router = tgen.gears["r1"] # check for zebra capability if CustomizeVrfWithNetns == True: if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False: return pytest.skip( "Skipping BGP VRF NETNS Test. VRF NETNS backend not available on FRR" ) if os.system("ip netns list") != 0: return pytest.skip( "Skipping BGP VRF NETNS Test. NETNS not available on System") # retrieve VRF backend kind if CustomizeVrfWithNetns == True: logger.info("Testing with VRF Namespace support") # create VRF r1-cust1 # move r1-eth0 to VRF r1-cust1 cmds = [ "if [ -e /var/run/netns/{0}-cust1 ] ; then ip netns del {0}-cust1 ; fi", "ip netns add {0}-cust1", "ip link set dev {0}-eth0 netns {0}-cust1", "ip netns exec {0}-cust1 ifconfig {0}-eth0 up", ] for cmd in cmds: cmd = cmd.format("r1") logger.info("cmd: " + cmd) output = router.run(cmd.format("r1")) if output != None and len(output) > 0: logger.info( 'Aborting due to unexpected output: cmd="{}" output=\n{}'. format(cmd, output)) return pytest.skip( "Skipping BGP VRF NETNS Test. Unexpected output to command: " + cmd) # run daemons router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")), "--vrfwnetns", ) router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))) logger.info("Launching BGP and ZEBRA") # BGP and ZEBRA start without underlying VRF router.start() # Starting Hosts and init ExaBGP on each of them logger.info("starting exaBGP on peer1") peer_list = tgen.exabgp_peers() for pname, peer in peer_list.items(): peer_dir = os.path.join(CWD, pname) env_file = os.path.join(CWD, "exabgp.env") logger.info("Running ExaBGP peer") peer.start(peer_dir, env_file) logger.info(pname)
def test_bgp_flowspec(): tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) router = tgen.gears["r1"] logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP") output = router.vtysh_cmd( "show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd" ) logger.info(output) if ( "NH 50.0.0.2" not in output or "FS:redirect IP" not in output or "Packet Length < 200" not in output ): assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK" assert 0, assertmsg else: logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK") logger.info("Check BGP FS entry for 3::3 with redirect IP") output = router.vtysh_cmd( "show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd" ) logger.info(output) if ( "NH 50::2" not in output or "FS:redirect IP" not in output or "Packet Length < 200" not in output ): assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK" assert 0, assertmsg else: logger.info("Check BGP FS entry for 3::3 with redirect IP OK")
def diagnose_env_linux(rundir): """ Run diagnostics in the running environment. Returns `True` when everything is ok, otherwise `False`. """ ret = True # Load configuration config = configparser.ConfigParser(defaults=tgen_defaults) pytestini_path = os.path.join(CWD, "../pytest.ini") config.read(pytestini_path) # Test log path exists before installing handler. os.system("mkdir -p " + rundir) # Log diagnostics to file so it can be examined later. fhandler = logging.FileHandler( filename="{}/diagnostics.txt".format(rundir)) fhandler.setLevel(logging.DEBUG) fhandler.setFormatter(logging.Formatter(fmt=topolog.FORMAT)) logger.addHandler(fhandler) logger.info("Running environment diagnostics") # Assert that we are running as root if os.getuid() != 0: logger.error("you must run topotest as root") ret = False # Assert that we have mininet # if os.system("which mn >/dev/null 2>/dev/null") != 0: # logger.error("could not find mininet binary (mininet is not installed)") # ret = False # Assert that we have iproute installed if os.system("which ip >/dev/null 2>/dev/null") != 0: logger.error("could not find ip binary (iproute is not installed)") ret = False # Assert that we have gdb installed if os.system("which gdb >/dev/null 2>/dev/null") != 0: logger.error("could not find gdb binary (gdb is not installed)") ret = False # Assert that FRR utilities exist frrdir = config.get("topogen", "frrdir") if not os.path.isdir(frrdir): logger.error("could not find {} directory".format(frrdir)) ret = False else: try: pwd.getpwnam("frr")[2] except KeyError: logger.warning('could not find "frr" user') try: grp.getgrnam("frr")[2] except KeyError: logger.warning('could not find "frr" group') try: if "frr" not in grp.getgrnam("frrvty").gr_mem: logger.error( '"frr" user and group exist, but user is not under "frrvty"' ) except KeyError: logger.warning('could not find "frrvty" group') for fname in [ "zebra", "ospfd", "ospf6d", "bgpd", "ripd", "ripngd", "isisd", "pimd", "ldpd", "pbrd", ]: path = os.path.join(frrdir, fname) if not os.path.isfile(path): # LDPd is an exception if fname == "ldpd": logger.info( "could not find {} in {}".format(fname, frrdir) + "(LDPd tests will not run)") continue logger.warning("could not find {} in {}".format(fname, frrdir)) ret = False else: if fname != "zebra": continue os.system("{} -v 2>&1 >{}/frr_zebra.txt".format(path, rundir)) # Test MPLS availability krel = platform.release() if topotest.version_cmp(krel, "4.5") < 0: logger.info( 'LDPd tests will not run (have kernel "{}", but it requires 4.5)'. format(krel)) # Test for MPLS Kernel modules available if not topotest.module_present("mpls-router", load=False) != 0: logger.info( "LDPd tests will not run (missing mpls-router kernel module)") if not topotest.module_present("mpls-iptunnel", load=False) != 0: logger.info( "LDPd tests will not run (missing mpls-iptunnel kernel module)") if not get_exabgp_cmd(): logger.warning("Failed to find exabgp < 4") logger.removeHandler(fhandler) fhandler.close() return ret
def start_topology(self): """Starts the topology class.""" logger.info("starting topology: {}".format(self.modname)) self.net.start()
def setup_module(mod): """ Sets up the pytest environment. * `mod`: module name """ global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC global ADDR_TYPES testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) logger.info("Running setup_module to create topology") # This function initiates the topology build with Topogen... tgen = Topogen(CreateTopo, mod.__name__) # Starting topology, create tmp files which are loaded to routers # to start deamons and then start routers start_topology(tgen) # Creating configuration from JSON build_config_from_json(tgen, topo) # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) # tgen.mininet_cli() # Api call verify whether BGP is converged ADDR_TYPES = check_address_types() for addr_type in ADDR_TYPES: BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format( BGP_CONVERGENCE) link_data = [ val for links, val in topo["routers"]["r2"]["links"].iteritems() if "r3" in links ] for adt in ADDR_TYPES: NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] if adt == "ipv4": NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) elif adt == "ipv6": NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16)) INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) link_data = [ val for links, val in topo["routers"]["r3"]["links"].iteritems() if "r2" in links ] INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) # STATIC_ROUTE = True logger.info("Running setup_module() done")
def start(self): "Basic start function that just reports equipment start" logger.info('starting "{}"'.format(self.name))
def test_bgp_remove_metric_rmaps(): "Test removing route-maps with metric changes again" tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info("Test absolute metric") # Remove metric route-maps and relevant comfiguration tgen.net['r1'].cmd( 'vtysh -c "conf t" -c "router bgp 65000" ' + '-c "address-family ipv4 unicast" ' + '-c "no neighbor 192.168.0.2 route-map addmetric-in in" ' + '-c "no neighbor 192.168.0.2 route-map addmetric-out out" ' + '-c "no neighbor 192.168.101.2 route-map setmetric-in in" ' + '-c "no neighbor 192.168.101.2 route-map setmetric-out out" ') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "no ip prefix-list net1" ' + '-c "no ip prefix-list net2"') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ') tgen.net['r2'].cmd( 'vtysh -c "conf t" -c "router bgp 65000" ' + '-c "address-family ipv4 unicast" ' + '-c "no neighbor 192.168.0.1 route-map subtractmetric-in in" ' + '-c "no neighbor 192.168.0.1 route-map subtractmetric-out out" ' + '-c "no neighbor 192.168.201.2 route-map setmetric-in in" ' + '-c "no neighbor 192.168.201.2 route-map setmetric-out out" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "no ip prefix-list net1" ' + '-c "no ip prefix-list net2" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ') # Clear IN the bgp neighbors to make sure the route-maps are applied tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"') tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"') # tgen.mininet_cli() # Check BGP Summary on local and remote routers for rtrNum in [1, 2]: logger.info("Checking metrics of BGP router on r{}".format(rtrNum)) router = tgen.gears["r{}".format(rtrNum)] reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtrNum)) expected = json.loads(open(reffile).read()) test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp json", expected) _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) assertmsg = "BGP routes on router r{} are wrong after removing metric route-maps".format( rtrNum) assert res is None, assertmsg
def test_bgp_metric_config(): "Test BGP Changing metric values in route-maps" tgen = get_topogen() # Skip if previous fatal error condition is raised if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info( "Configuring bgp route-maps on router r1 and r2 to update metric") # # Adding the following configuration to r1: # router bgp 65000 # address-family ipv4 unicast # neighbor 192.168.0.2 route-map addmetric-in in # neighbor 192.168.0.2 route-map addmetric-out out # neighbor 192.168.101.2 route-map setmetric-in in # neighbor 192.168.101.2 route-map setmetric-out out # exit-address-family # ! # ip prefix-list net1 seq 10 permit 192.168.101.0/24 # ip prefix-list net2 seq 20 permit 192.168.1.0/24 # ! # route-map setmetric-in permit 10 # match ip address prefix-list net1 # set metric 111 # ! # route-map setmetric-in permit 20 # ! # route-map setmetric-out permit 10 # match ip address prefix-list net2 # set metric 1011 # ! # route-map setmetric-out permit 20 # ! # route-map addmetric-in permit 10 # set metric +11 # ! # route-map addmetric-out permit 10 # set metric +12 # ! tgen.net['r1'].cmd( 'vtysh -c "conf t" -c "router bgp 65000" ' + '-c "address-family ipv4 unicast" ' + '-c "neighbor 192.168.0.2 route-map addmetric-in in" ' + '-c "neighbor 192.168.0.2 route-map addmetric-out out" ' + '-c "neighbor 192.168.101.2 route-map setmetric-in in" ' + '-c "neighbor 192.168.101.2 route-map setmetric-out out" ') tgen.net['r1'].cmd( 'vtysh -c "conf t" ' + '-c "ip prefix-list net1 seq 10 permit 192.168.101.0/24" ' + '-c "ip prefix-list net2 seq 20 permit 192.168.1.0/24"') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "route-map setmetric-in permit 10" ' + '-c "match ip address prefix-list net1" ' + '-c "set metric 111" ' + '-c "route-map setmetric-in permit 20"') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "route-map setmetric-out permit 10" ' + '-c "match ip address prefix-list net2" ' + '-c "set metric 1011" ' + '-c "route-map setmetric-out permit 20"') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "route-map addmetric-in permit 10" ' + '-c "set metric +11"') tgen.net['r1'].cmd('vtysh -c "conf t" ' + '-c "route-map addmetric-out permit 10" ' + '-c "set metric +12"') # # Adding the following configuration to r2: # router bgp 65000 # address-family ipv4 unicast # neighbor 192.168.0.1 route-map subtractmetric-in in # neighbor 192.168.0.1 route-map subtractmetric-out out # neighbor 192.168.201.2 route-map setmetric-in in # neighbor 192.168.201.2 route-map setmetric-out out # exit-address-family # ! # ip prefix-list net1 seq 10 permit 192.168.201.0/24 # ip prefix-list net2 seq 20 permit 192.168.2.0/24 # ! # route-map setmetric-in permit 10 # match ip address prefix-list net1 # set metric 222 # ! # route-map setmetric-in permit 20 # ! # route-map setmetric-out permit 10 # match ip address prefix-list net2 # set metric 2022 # ! # route-map setmetric-out permit 20 # ! # route-map subtractmetric-in permit 10 # set metric -22 # ! # route-map subtractmetric-out permit 10 # set metric -23 # ! tgen.net['r2'].cmd( 'vtysh -c "conf t" -c "router bgp 65000" ' + '-c "address-family ipv4 unicast" ' + '-c "neighbor 192.168.0.1 route-map subtractmetric-in in" ' + '-c "neighbor 192.168.0.1 route-map subtractmetric-out out" ' + '-c "neighbor 192.168.201.2 route-map setmetric-in in" ' + '-c "neighbor 192.168.201.2 route-map setmetric-out out" ') tgen.net['r2'].cmd( 'vtysh -c "conf t" ' + '-c "ip prefix-list net1 seq 10 permit 192.168.201.0/24" ' + '-c "ip prefix-list net2 seq 20 permit 192.168.2.0/24" ') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "route-map setmetric-in permit 10" ' + '-c "match ip address prefix-list net1" ' + '-c "set metric 222" ' + '-c "route-map setmetric-in permit 20"') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "route-map setmetric-out permit 10" ' + '-c "match ip address prefix-list net2" ' + '-c "set metric 2022" ' + '-c "route-map setmetric-out permit 20"') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "route-map subtractmetric-in permit 10" ' + '-c "set metric -22"') tgen.net['r2'].cmd('vtysh -c "conf t" ' + '-c "route-map subtractmetric-out permit 10" ' + '-c "set metric -23"') # Clear IN the bgp neighbors to make sure the route-maps are applied tgen.net['r1'].cmd('vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"') tgen.net['r2'].cmd('vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"') # tgen.mininet_cli() # Checking BGP config - should show the bgp metric settings in the route-maps logger.info("Checking BGP configuration for correct 'set metric' values") setmetric111 = tgen.net['r1'].cmd( 'vtysh -c "show running" | grep "^ set metric 111"').rstrip() assertmsg = "'set metric 111' configuration applied to R1, but not visible in configuration" assert setmetric111 == ' set metric 111', assertmsg setmetric222 = tgen.net['r2'].cmd( 'vtysh -c "show running" | grep "^ set metric 222"').rstrip() assertmsg = "'set metric 222' configuration applied to R2, but not visible in configuration" assert setmetric222 == ' set metric 222', assertmsg
def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): """ Verify static route functionality with 2 next hop & different AD value """ tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) reset_config_on_routers(tgen) NEXT_HOP_IP = populate_nh() step("Configure IPv4 static route (10.1.1.1) in R2 with next hop N1" "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop" "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & " "ip route 10.1.1.1/24 29.1.1.2 20") reset_config_on_routers(tgen) NEXT_HOP_IP = populate_nh() for addr_type in ADDR_TYPES: input_dict_4 = { "r2": { "static_routes": [ { "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }, { "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }, ] } } logger.info("Configure static routes") result = create_static_routes(tgen, input_dict_4) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step("On R2, static route installed in RIB using " "show ip route with 2 next hop , lowest AD nexthop is active ") rte1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }] } } nh = [NEXT_HOP_IP["nh1"][addr_type]] dut = "r2" protocol = "static" result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }] } } nh = [NEXT_HOP_IP["nh2"][addr_type]] dut = "r2" protocol = "static" result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) step("Configure IBGP IPv4 peering between R2 and R3 router.") step("Explicit route is added in R3 for R2 nexthop rechability") rt3_rtes = { "r3": { "static_routes": [ { "network": NEXT_HOP_IP["nh1"][addr_type] + "/32", "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], }, { "network": NEXT_HOP_IP["nh2"][addr_type] + "/32", "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type], }, ] } } logger.info("Configure static routes") result = create_static_routes(tgen, rt3_rtes) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step("Configure redistribute static in BGP on R2 router") input_dict_2 = { "r2": { "bgp": { "address_family": { addr_type: { "unicast": { "redistribute": [{ "redist_type": "static" }] } } } } } } result = create_router_bgp(tgen, topo, input_dict_2) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step( "Remove the static route configured with nexthop N1 from running config" ) rt1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, "delete": True, }] } } logger.info("Configure static routes") result = create_static_routes(tgen, rt1_nh1) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step("On R2, after removing the static route with N1 , " "route become active with nexthop N2 and vice versa.") rte1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }] } } nh = [NEXT_HOP_IP["nh1"][addr_type]] dut = "r2" protocol = "static" result = verify_rib( tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }] } } nh = [NEXT_HOP_IP["nh2"][addr_type]] result = verify_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) step("Configure the static route with nexthop N1") rte1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }] } } logger.info("Configure static routes") result = create_static_routes(tgen, rte1_nh1) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step( "Remove the static route configured with nexthop N2 from running config" ) rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, "delete": True, }] } } logger.info("Configure static routes") result = create_static_routes(tgen, rte2_nh2) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step("On R2, after removing the static route with N2 , " "route become active with nexthop N1 and vice versa.") nh = NEXT_HOP_IP["nh2"][addr_type] result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(tc_name) nh = [NEXT_HOP_IP["nh1"][addr_type]] result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol) assert result is True, "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) step("Configure the static route with nexthop N2") rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }] } } logger.info("Configure static routes") result = create_static_routes(tgen, rte2_nh2) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) step("Shut nexthop interface N1") intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"] shutdown_bringup_interface(tgen, dut, intf, False) step("after shut of nexthop N1 , route become active with nexthop N2") nh = NEXT_HOP_IP["nh1"][addr_type] result = verify_rib( tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(tc_name) nh = [NEXT_HOP_IP["nh2"][addr_type]] result = verify_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) step("No shut the nexthop interface N1") shutdown_bringup_interface(tgen, dut, intf, True) step("after shut of nexthop N1 , route become active " "with nexthop N2 and vice versa.") nh = [NEXT_HOP_IP["nh1"][addr_type]] result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) step("Shut nexthop interface N2") intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] shutdown_bringup_interface(tgen, dut, intf, False) step(" after shut of nexthop N1 , route become active with " "nexthop N2 and vice versa.") nh = NEXT_HOP_IP["nh2"][addr_type] result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(tc_name) nh = [NEXT_HOP_IP["nh1"][addr_type]] result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol) assert result is True, "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) step("No shut nexthop interface N2") shutdown_bringup_interface(tgen, dut, intf, True) step("after shut of nexthop N1 , route become active " "with nexthop N2 and vice versa.") rte1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }] } } nh = [NEXT_HOP_IP["nh1"][addr_type]] dut = "r2" protocol = "static" result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }] } } nh = [NEXT_HOP_IP["nh2"][addr_type]] dut = "r2" protocol = "static" result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) dut = "r2" step("Reload the FRR router") # stop/start -> restart FRR router and verify stop_router(tgen, "r2") start_router(tgen, "r2") step("After reload of FRR router , static route installed" " in RIB and FIB properly .") rte1_nh1 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh1"][addr_type], "admin_distance": 10, }] } } nh = [NEXT_HOP_IP["nh1"][addr_type]] dut = "r2" protocol = "static" result = verify_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True) assert result is True, "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh) assert result is True, "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) rte2_nh2 = { "r2": { "static_routes": [{ "network": NETWORK2[addr_type], "next_hop": NEXT_HOP_IP["nh2"][addr_type], "admin_distance": 20, }] } } nh = [NEXT_HOP_IP["nh2"][addr_type]] dut = "r2" protocol = "static" result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh) assert result is True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True, expected=False, ) assert result is not True, "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) write_test_footer(tc_name)
def test_RT_verification_auto_p0(request): """ RT verification(auto) """ tgen = get_topogen() tc_name = request.node.name write_test_header(tc_name) check_router_status(tgen) reset_config_on_routers(tgen) add_default_routes(tgen) if tgen.routers_have_failure(): pytest.skip(tgen.errors) step( "Advertise overlapping prefixes from VNFs R1 and R2 in all VRFs " "RED, GREEN and BLUE 100.1.1.1/32 and 100::100/128" ) for addr_type in ADDR_TYPES: input_dict_1 = { "r1": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "RED", } ] }, "r2": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "BLUE", }, { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "GREEN", }, ] }, } result = create_static_routes(tgen, input_dict_1) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) step( "Verify that Edge-1 receives same prefixes in all 3 VRFs via " "corresponding next-hop in associated VRF sh bgp vrf all" ) for addr_type in ADDR_TYPES: input_routes = { "r1": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "RED", } ] }, "r2": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "BLUE", }, { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "GREEN", }, ] }, } result = verify_rib(tgen, addr_type, "e1", input_routes) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) step( "Configure 4-byte local AS number on Edge-1 and establish EVPN " "neighborship with DCG-1 & DCG-2." ) topo_local = deepcopy(topo) step("Delete BGP config for vrf RED.") input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "no_vni": VNI_1}, {"name": "BLUE", "no_vni": VNI_2}, {"name": "GREEN", "no_vni": VNI_3}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_2 = {} for dut in ["e1"]: temp = {dut: {"bgp": []}} input_dict_2.update(temp) INDEX = [0, 1, 2, 3] VRFS = ["RED", "BLUE", "GREEN", None] AS_NUM = [100, 100, 100, 100] for index, vrf, as_num in zip(INDEX, VRFS, AS_NUM): topo_local["routers"][dut]["bgp"][index]["local_as"] = 4294967293 if vrf: temp[dut]["bgp"].append( {"local_as": as_num, "vrf": vrf, "delete": True} ) else: temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) result = create_router_bgp(tgen, topo, input_dict_2) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) result = create_router_bgp(tgen, topo_local["routers"]) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "vni": VNI_1}, {"name": "BLUE", "vni": VNI_2}, {"name": "GREEN", "vni": VNI_3}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step( "Verify that all overlapping prefixes across different VRFs are " "advertised in EVPN with unique RD value(auto derived)." ) step( "Verify that FRR uses only the lower 2 bytes of ASN+VNI for auto " "derived RT value." ) for addr_type in ADDR_TYPES: input_routes_1 = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } input_routes_2 = { "r2": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "BLUE"}]} } input_routes_3 = { "r2": { "static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "GREEN"}] } } result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_1, rd="auto", rd_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_1, rt="auto", rt_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_2, rd="auto", rd_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_2, rt="auto", rt_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_3, rd="auto", rd_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes_3, rt="auto", rt_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) step( "Verify that DCG-1(iBGP peer) automatically imports the prefixes" " from EVPN address-family to respective VRFs." ) step( "Verify if DCG-2(eBGP peer) automatically imports the prefixes " "from EVPN address-family to respective VRFs or not." ) for addr_type in ADDR_TYPES: input_routes = { "r1": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "RED", } ] }, "r2": { "static_routes": [ { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "BLUE", }, { "network": NETWORK4_1[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "vrf": "GREEN", }, ] }, } result = verify_rib(tgen, addr_type, "d1", input_routes) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) result = verify_rib(tgen, addr_type, "d2", input_routes) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) step( "Change the VNI number for all 3 VRFs on Edge-1 as:" "RED : 75400, GREEN: 75500, BLUE: 75600" ) input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "no_vni": VNI_1}, {"name": "BLUE", "no_vni": VNI_2}, {"name": "GREEN", "no_vni": VNI_3}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "vni": 75400}, {"name": "BLUE", "vni": 75500}, {"name": "GREEN", "vni": 75600}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Delete configured vxlan") dut = "e1" vxlan_input = { dut: { "vxlan": [ { "vxlan_name": VXLAN["vxlan_name"], "vxlan_id": VXLAN["vxlan_id"], "dstport": VXLAN["dstport"], "local_addr": VXLAN["local_addr"][dut], "learning": VXLAN["learning"], "delete": True, } ] } } result = configure_vxlan(tgen, vxlan_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Configured vxlan") VXLAN["vxlan_id"] = [75400, 75500, 75600] vxlan_input = { dut: { "vxlan": [ { "vxlan_name": VXLAN["vxlan_name"], "vxlan_id": VXLAN["vxlan_id"], "dstport": VXLAN["dstport"], "local_addr": VXLAN["local_addr"][dut], "learning": VXLAN["learning"], } ] } } result = configure_vxlan(tgen, vxlan_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Configure bridge interface") brctl_input = { dut: { "brctl": [ { "brctl_name": BRCTL["brctl_name"], "addvxlan": BRCTL["addvxlan"], "vrf": BRCTL["vrf"], "stp": BRCTL["stp"], } ] } } result = configure_brctl(tgen, topo, brctl_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step( "Verify on Edge-1 that auto derived RT value has changed for " "each VRF based on VNI number.." ) input_dict = { "e1": { "vrfs": [ {"RED": {"vni": 75400}}, {"BLUE": {"vni": 75500}}, {"GREEN": {"vni": 75600}}, ] } } result = verify_vrf_vni(tgen, input_dict) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step( "Verify on Edge-1 that auto derived RT value has changed for " "each VRF based on VNI number." ) for addr_type in ADDR_TYPES: input_routes = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) step( "Verify on DCG-2 that prefixes are not imported from EVPN " "address-family to VRFs as RT values are different on sending(" "edge-1) and receiving(DCG-2) end." ) for addr_type in ADDR_TYPES: input_routes = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } result = verify_rib(tgen, addr_type, "d2", input_routes, expected=False) assert result is not True, "Testcase {} :Failed \n " "Routes are still present: {}".format(tc_name, result) logger.info("Expected Behavior: {}".format(result)) step( "Revert back to original VNI number for all 3 VRFs on Edge-1 " "as: RED : 75100, GREEN: 75200, BLUE: 75300" ) input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "no_vni": 75400}, {"name": "BLUE", "no_vni": 75500}, {"name": "GREEN", "no_vni": 75600}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_vni = { "e1": { "vrfs": [ {"name": "RED", "vni": VNI_1}, {"name": "BLUE", "vni": VNI_2}, {"name": "GREEN", "vni": VNI_3}, ] } } result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Delete configured vxlan") dut = "e1" vxlan_input = { dut: { "vxlan": [ { "vxlan_name": VXLAN["vxlan_name"], "vxlan_id": VXLAN["vxlan_id"], "dstport": VXLAN["dstport"], "local_addr": VXLAN["local_addr"][dut], "learning": VXLAN["learning"], "delete": True, } ] } } result = configure_vxlan(tgen, vxlan_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Configured vxlan") VXLAN["vxlan_id"] = [75100, 75200, 75300] vxlan_input = { dut: { "vxlan": [ { "vxlan_name": VXLAN["vxlan_name"], "vxlan_id": VXLAN["vxlan_id"], "dstport": VXLAN["dstport"], "local_addr": VXLAN["local_addr"][dut], "learning": VXLAN["learning"], } ] } } result = configure_vxlan(tgen, vxlan_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Configure bridge interface") brctl_input = { dut: { "brctl": [ { "brctl_name": BRCTL["brctl_name"], "addvxlan": BRCTL["addvxlan"], "vrf": BRCTL["vrf"], "stp": BRCTL["stp"], } ] } } result = configure_brctl(tgen, topo, brctl_input) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step( "Verify on Edge-1 that auto derived RT value has changed for " "each VRF based on VNI number." ) step( "Verify that DCG-1(iBGP peer) automatically imports the prefixes" " from EVPN address-family to respective VRFs." ) for addr_type in ADDR_TYPES: input_routes = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } result = verify_attributes_for_evpn_routes( tgen, topo, "e1", input_routes, rt="auto", rt_peer="e1" ) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) result = verify_rib(tgen, addr_type, "d1", input_routes) assert result is True, "Testcase {} :Failed \n Error: {}".format( tc_name, result ) step("Test with smaller VNI numbers (1-75000)") input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": VNI_1}]}} result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 111}]}} result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step( "Verify that DCG-2 receives EVPN prefixes along with auto " "derived RT values(based on smaller VNI numbers)" ) for addr_type in ADDR_TYPES: input_routes_1 = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } result = verify_attributes_for_evpn_routes( tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False ) assert result is not True, "Testcase {} :Failed \n " "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) logger.info("Expected Behavior: {}".format(result)) step("Configure VNI number more than boundary limit (16777215)") input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 111}]}} result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "vni": 16777215}]}} result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("CLI error for malformed VNI.") input_dict = { "e1": { "vrfs": [{"RED": {"vni": 16777215, "routerMac": "None", "state": "Down"}}] } } result = verify_vrf_vni(tgen, input_dict) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: input_routes_1 = { "r1": {"static_routes": [{"network": NETWORK4_1[addr_type], "vrf": "RED"}]} } result = verify_attributes_for_evpn_routes( tgen, topo, "d2", input_routes_1, rt="auto", rt_peer="e1", expected=False ) assert result is not True, "Testcase {} :Failed \n " "Malfaromed Auto-RT value accepted: {}".format(tc_name, result) logger.info("Expected Behavior: {}".format(result)) step("Un-configure VNI number more than boundary limit (16777215)") input_dict_vni = {"e1": {"vrfs": [{"name": "RED", "no_vni": 16777215}]}} result = create_vrf_cfg(tgen, topo, input_dict=input_dict_vni) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) write_test_footer(tc_name)
def setup_module(mod): "Sets up the pytest environment" tgen = Topogen(build_topo, mod.__name__) tgen.start_topology() router_list = tgen.routers() krel = platform.release() if topotest.version_cmp(krel, "4.18") < 0: logger.info( 'BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format( krel ) ) return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported") # create VRF vrf-101 on R1 and R2 # create loop101 cmds_vrflite = [ "ip link add {}-vrf-101 type vrf table 101", "ip ru add oif {}-vrf-101 table 101", "ip ru add iif {}-vrf-101 table 101", "ip link set dev {}-vrf-101 up", "ip link add loop101 type dummy", "ip link set dev loop101 master {}-vrf-101", "ip link set dev loop101 up", ] cmds_r2 = [ # config routing 101 "ip link add name bridge-101 up type bridge stp_state 0", "ip link set bridge-101 master {}-vrf-101", "ip link set dev bridge-101 up", "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41", "ip link set dev vxlan-101 master bridge-101", "ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off", ] # cmds_r1_netns_method3 = [ # "ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21", # "ip link set dev vxlan-{1} netns {0}-vrf-{1}", # "ip netns exec {0}-vrf-{1} ip li set dev lo up", # "ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0", # "ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}", # "ip netns exec {0}-vrf-{1} ip link set bridge-{1} up", # "ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up", # ] router = tgen.gears["r1"] ns = "r1-vrf-101" tgen.net["r1"].add_netns(ns) tgen.net["r1"].cmd_raises("ip link add loop101 type dummy") tgen.net["r1"].set_intf_netns("loop101", ns, up=True) router = tgen.gears["r2"] for cmd in cmds_vrflite: logger.info("cmd to r2: " + cmd.format("r2")) output = router.cmd_raises(cmd.format("r2")) logger.info("result: " + output) for cmd in cmds_r2: logger.info("cmd to r2: " + cmd.format("r2")) output = router.cmd_raises(cmd.format("r2")) logger.info("result: " + output) tgen.net["r1"].cmd_raises( "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r1-eth0 local 192.168.100.21" ) tgen.net["r1"].set_intf_netns("vxlan-101", "r1-vrf-101", up=True) tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set lo up") tgen.net["r1"].cmd_raises( "ip -n r1-vrf-101 link add name bridge-101 up type bridge stp_state 0" ) tgen.net["r1"].cmd_raises( "ip -n r1-vrf-101 link set dev vxlan-101 master bridge-101" ) tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set bridge-101 up") tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set vxlan-101 up") for rname, router in router_list.items(): if rname == "r1": router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)), "--vrfwnetns", ) else: router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) ) router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) ) # Initialize all routers. tgen.start_router()
def ltemplatePostRouterStartHook(): logger.info('post router-start hook') return True
def test_vrf_route_leak(): logger.info("Ensure that routes are leaked back and forth") tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) r1 = tgen.gears["r1"] # Test DONNA VRF. expect = { "10.0.0.0/24": [{ "protocol": "connected", }], "10.0.1.0/24": [{ "protocol": "bgp", "selected": True, "nexthops": [{ "fib": True }] }], "10.0.2.0/24": [{ "protocol": "connected" }], "10.0.3.0/24": [{ "protocol": "bgp", "selected": True, "nexthops": [{ "fib": True }] }], } test_func = partial(topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect) result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result, "BGP VRF DONNA check failed:\n{}".format(diff) # Test EVA VRF. expect = { "10.0.0.0/24": [{ "protocol": "bgp", "selected": True, "nexthops": [{ "fib": True }] }], "10.0.1.0/24": [{ "protocol": "connected", }], "10.0.2.0/24": [{ "protocol": "bgp", "selected": True, "nexthops": [{ "fib": True }] }], "10.0.3.0/24": [{ "protocol": "connected", }], } test_func = partial(topotest.router_json_cmp, r1, "show ip route vrf EVA json", expect) result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) assert result, "BGP VRF EVA check failed:\n{}".format(diff)
def test_ecmp_remove_nw_advertise(request): """ Verify routes are cleared from BGP and RIB table of DUT, when advertise network configuration is removed """ tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() # Verifying RIB routes dut = "r3" protocol = "bgp" reset_config_on_routers(tgen) static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) input_dict_3 = { "r2": { "bgp": { "address_family": { "ipv4": { "unicast": { "advertise_networks": [{ "network": NETWORK["ipv4"], "delete": True }] } }, "ipv6": { "unicast": { "advertise_networks": [{ "network": NETWORK["ipv6"], "delete": True }] } }, } } } } logger.info("Withdraw advertised networks") result = create_router_bgp(tgen, topo, input_dict_3) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, next_hop=[], protocol=protocol, expected=False, ) assert ( result is not True ), "Testcase {} : Failed \n Routes still" " present in RIB".format( tc_name) static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result)
def stop(self, wait=True, assertOnError=True): "Basic start function that just reports equipment stop" logger.info('stopping "{}"'.format(self.name)) return ""
def stop(self, wait=True, assertOnError=True): "Basic stop function that just reports equipment stop" logger.info('"{}" base stop called'.format(self.name)) return ""
def diagnose_env_linux(): """ Run diagnostics in the running environment. Returns `True` when everything is ok, otherwise `False`. """ ret = True # Test log path exists before installing handler. if not os.path.isdir("/tmp"): logger.warning("could not find /tmp for logs") else: os.system("mkdir /tmp/topotests") # Log diagnostics to file so it can be examined later. fhandler = logging.FileHandler( filename="/tmp/topotests/diagnostics.txt") fhandler.setLevel(logging.DEBUG) fhandler.setFormatter( logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s")) logger.addHandler(fhandler) logger.info("Running environment diagnostics") # Load configuration config = configparser.ConfigParser(tgen_defaults) pytestini_path = os.path.join(CWD, "../pytest.ini") config.read(pytestini_path) # Assert that we are running as root if os.getuid() != 0: logger.error("you must run topotest as root") ret = False # Assert that we have mininet if os.system("which mn >/dev/null 2>/dev/null") != 0: logger.error( "could not find mininet binary (mininet is not installed)") ret = False # Assert that we have iproute installed if os.system("which ip >/dev/null 2>/dev/null") != 0: logger.error("could not find ip binary (iproute is not installed)") ret = False # Assert that we have gdb installed if os.system("which gdb >/dev/null 2>/dev/null") != 0: logger.error("could not find gdb binary (gdb is not installed)") ret = False # Assert that FRR utilities exist frrdir = config.get("topogen", "frrdir") if not os.path.isdir(frrdir): logger.error("could not find {} directory".format(frrdir)) ret = False else: try: pwd.getpwnam("frr")[2] except KeyError: logger.warning('could not find "frr" user') try: grp.getgrnam("frr")[2] except KeyError: logger.warning('could not find "frr" group') try: if "frr" not in grp.getgrnam("frrvty").gr_mem: logger.error( '"frr" user and group exist, but user is not under "frrvty"' ) except KeyError: logger.warning('could not find "frrvty" group') for fname in [ "zebra", "ospfd", "ospf6d", "bgpd", "ripd", "ripngd", "isisd", "pimd", "ldpd", "pbrd" ]: path = os.path.join(frrdir, fname) if not os.path.isfile(path): # LDPd is an exception if fname == "ldpd": logger.info( "could not find {} in {}".format(fname, frrdir) + "(LDPd tests will not run)") continue logger.warning("could not find {} in {}".format(fname, frrdir)) ret = False else: if fname != "zebra": continue os.system( "{} -v 2>&1 >/tmp/topotests/frr_zebra.txt".format(path)) # Test MPLS availability krel = platform.release() if topotest.version_cmp(krel, "4.5") < 0: logger.info( 'LDPd tests will not run (have kernel "{}", but it requires 4.5)'. format(krel)) # Test for MPLS Kernel modules available if not topotest.module_present("mpls-router", load=False) != 0: logger.info( "LDPd tests will not run (missing mpls-router kernel module)") if not topotest.module_present("mpls-iptunnel", load=False) != 0: logger.info( "LDPd tests will not run (missing mpls-iptunnel kernel module)") # TODO remove me when we start supporting exabgp >= 4 try: output = subprocess.check_output(["exabgp", "-v"]) line = output.split("\n")[0] version = line.split(" ")[2] if topotest.version_cmp(version, "4") >= 0: logger.warning( "BGP topologies are still using exabgp version 3, expect failures" ) # We want to catch all exceptions # pylint: disable=W0702 except: logger.warning("failed to find exabgp or returned error") # After we logged the output to file, remove the handler. logger.removeHandler(fhandler) return ret
def _init_topo(self, topodef): """ Initialize the topogily provided by the user. The user topology class must call get_topogen() during build() to get the topogen object. """ # Set the global variable so the test cases can access it anywhere set_topogen(self) # Increase host based limits topotest.fix_host_limits() # Test for MPLS Kernel modules available self.hasmpls = False if not topotest.module_present("mpls-router"): logger.info( "MPLS tests will not run (missing mpls-router kernel module)") elif not topotest.module_present("mpls-iptunnel"): logger.info( "MPLS tests will not run (missing mpls-iptunnel kernel module)" ) else: self.hasmpls = True # Load the default topology configurations self._load_config() # Create new log directory self.logdir = topotest.get_logs_path(g_extra_config["rundir"]) subprocess.check_call("mkdir -p {0} && chmod 1777 {0}".format( self.logdir), shell=True) try: routertype = self.config.get(self.CONFIG_SECTION, "routertype") # Only allow group, if it exist. gid = grp.getgrnam(routertype)[2] os.chown(self.logdir, 0, gid) os.chmod(self.logdir, 0o775) except KeyError: # Allow anyone, but set the sticky bit to avoid file deletions os.chmod(self.logdir, 0o1777) # Remove old twisty way of creating sub-classed topology object which has it's # build method invoked which calls Topogen methods which then call Topo methods # to create a topology within the Topo object, which is then used by # Mininet(Micronet) to build the actual topology. assert not inspect.isclass(topodef) self.net = Mininet(controller=None) # New direct way: Either a dictionary defines the topology or a build function # is supplied, or a json filename all of which build the topology by calling # Topogen methods which call Mininet(Micronet) methods to create the actual # topology. if not inspect.isclass(topodef): if callable(topodef): topodef(self) self.net.configure_hosts() elif is_string(topodef): # topojson imports topogen in one function too, # switch away from this use here to the topojson # fixutre and remove this case from lib.topojson import build_topo_from_json with open(topodef, "r") as topof: self.json_topo = json.load(topof) build_topo_from_json(self, self.json_topo) self.net.configure_hosts() elif topodef: self.add_topology_from_dict(topodef)
def test_ecmp_fast_convergence(request, test_type, tgen, topo): """This test is to verify bgp fast-convergence cli functionality""" tc_name = request.node.name write_test_header(tc_name) # Verifying RIB routes dut = "r3" protocol = "bgp" reset_config_on_routers(tgen) static_or_nw(tgen, topo, tc_name, test_type, "r2") for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) intf1 = topo["routers"]["r2"]["links"]["r3-link1"]["interface"] intf2 = topo["routers"]["r2"]["links"]["r3-link2"]["interface"] logger.info("Shutdown one of the link b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf1, False) logger.info("Verify bgp neighbors are still up") result = verify_bgp_convergence(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) logger.info("Shutdown another link b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf2, False) logger.info("Wait for 10 sec and make sure bgp neighbors are still up") sleep(10) result = verify_bgp_convergence(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) logger.info("No shut links b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf1, True) shutdown_bringup_interface(tgen, "r2", intf2, True) logger.info("Ensure that the links are still up") result = verify_bgp_convergence(tgen, topo) logger.info("Enable bgp fast-convergence cli") raw_config = { "r2": { "raw_config": [ "router bgp {}".format( topo["routers"]["r2"]["bgp"]["local_as"]), "bgp fast-convergence", ] } } result = apply_raw_config(tgen, raw_config) assert result is True, "Testcase {} : Failed Error: {}".format( tc_name, result) logger.info("Ensure BGP has processed the cli") r2 = tgen.gears["r2"] output = r2.vtysh_cmd("show run") verify = re.search(r"fast-convergence", output) assert verify is not None, ( "r2 does not have the fast convergence command yet") logger.info("Shutdown one link b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf1, False) logger.info("Verify bgp neighbors goes down immediately") result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) logger.info("Shutdown second link b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf2, False) logger.info("Verify bgp neighbors goes down immediately") result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name)
def static_or_nw(tgen, topo, tc_name, test_type, dut): if test_type == "redist_static": input_dict_static = { dut: { "static_routes": [ { "network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"] }, { "network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"] }, ] } } logger.info("Configuring static route on router %s", dut) result = create_static_routes(tgen, input_dict_static) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) input_dict_2 = { dut: { "bgp": { "address_family": { "ipv4": { "unicast": { "redistribute": [{ "redist_type": "static" }] } }, "ipv6": { "unicast": { "redistribute": [{ "redist_type": "static" }] } }, } } } } logger.info("Configuring redistribute static route on router %s", dut) result = create_router_bgp(tgen, topo, input_dict_2) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) elif test_type == "advertise_nw": input_dict_nw = { dut: { "bgp": { "address_family": { "ipv4": { "unicast": { "advertise_networks": [{ "network": NETWORK["ipv4"] }] } }, "ipv6": { "unicast": { "advertise_networks": [{ "network": NETWORK["ipv6"] }] } }, } } } } logger.info( "Advertising networks %s %s from router %s", NETWORK["ipv4"], NETWORK["ipv6"], dut, ) result = create_router_bgp(tgen, topo, input_dict_nw) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result)
def test_modify_ecmp_max_paths(request, ecmp_num, test_type): """ Verify routes installed as per maximum-paths configuration (8/16/32). """ tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() reset_config_on_routers(tgen) static_or_nw(tgen, topo, tc_name, test_type, "r2") input_dict = { "r3": { "bgp": { "address_family": { "ipv4": { "unicast": { "maximum_paths": { "ibgp": ecmp_num, } } }, "ipv6": { "unicast": { "maximum_paths": { "ibgp": ecmp_num, } } }, } } } } logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) result = create_router_bgp(tgen, topo, input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) # Verifying RIB routes dut = "r3" protocol = "bgp" for addr_type in ADDR_TYPES: input_dict_1 = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_1, next_hop=NEXT_HOPS[addr_type][:int(ecmp_num)], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name)
def test_bgp_peer_type_multipath_relax(): tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) def exabgp_cmd(peer, cmd): pipe = open("/run/exabgp_{}.in".format(peer), "w") with pipe: pipe.write(cmd) pipe.close() # Prefixes used in the test prefix1 = "203.0.113.0/30" prefix2 = "203.0.113.4/30" prefix3 = "203.0.113.8/30" # Next hops used for iBGP/confed routes resolved_nh1 = "198.51.100.1" resolved_nh2 = "198.51.100.2" # BGP route used for recursive resolution bgp_resolving_prefix = "198.51.100.0/24" # Next hop that will require non-connected recursive resolution ebgp_resolved_nh = "198.51.100.10" # Send a non-connected route to resolve others exabgp_cmd( "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix)) router = tgen.gears["r1"] # It seems that if you write to the exabgp socket too quickly in # succession, requests get lost. So verify prefix1 now instead of # after all the prefixes are advertised. logger.info("Create and verify mixed-type multipaths") exabgp_cmd( "peer1", "announce route {} next-hop {} as-path [ 64499 ]\n".format( prefix1, resolved_nh1), ) exabgp_cmd( "peer2", "announce route {} next-hop {} as-path [ 64499 ]\n".format( prefix1, resolved_nh2), ) exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1)) reffile = os.path.join(CWD, "r1/prefix1.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip bgp {} json".format(prefix1), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Mixed-type multipath not found" assert res is None, assertMsg logger.info("Create and verify eBGP and iBGP+confed multipaths") exabgp_cmd( "peer1", "announce route {} next-hop {} as-path [ 64499 ]\n".format( prefix2, resolved_nh1), ) exabgp_cmd( "peer2", "announce route {} next-hop {} as-path [ 64499 ]\n".format( prefix2, resolved_nh2), ) exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3)) exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3)) reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp json", expected) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Not all expected multipaths found" assert res is None, assertMsg logger.info("Toggle peer-type multipath-relax and verify the changes") router.vtysh_cmd( "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" ) # This file verifies "multipath" is not set reffile = os.path.join(CWD, "r1/not-multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp json", expected) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Disabling peer-type multipath-relax did not take effect" assert res is None, assertMsg router.vtysh_cmd( "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n") reffile = os.path.join(CWD, "r1/multipath.json") expected = json.loads(open(reffile).read()) test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp json", expected) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Reenabling peer-type multipath-relax did not take effect" assert res is None, assertMsg logger.info("Check recursive resolution of eBGP next hops is not affected") # eBGP next hop resolution rejects recursively resolved next hops by # default, even with peer-type multipath-relax exabgp_cmd( "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh)) reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip bgp {} json".format(prefix3), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Recursive eBGP next hop not as expected for {}".format( prefix3) assert res is None, assertMsg exabgp_cmd( "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh)) reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip bgp {} json".format(prefix1), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Recursive eBGP next hop not as expected for {}".format( prefix1) assert res is None, assertMsg # When other config allows recursively resolved eBGP next hops, # such next hops in all-eBGP multipaths should be valid router.vtysh_cmd( "conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") reffile = os.path.join(CWD, "r1/prefix3-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip bgp {} json".format(prefix3), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Recursive eBGP next hop not as expected for {}".format( prefix3) assert res is None, assertMsg reffile = os.path.join(CWD, "r1/prefix1-recursive.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip bgp {} json".format(prefix1), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "Recursive eBGP next hop not as expected for {}".format( prefix1) assert res is None, assertMsg logger.info( "Check mixed-type multipath next hop recursive resolution in FIB") # There are now two eBGP-learned routes with a recursively resolved next; # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ # confed-external. The peer-type multipath-relax feature only enables # recursive resolution in FIB if any next hop is iBGP/confed-learned. The # all-eBGP multipath will have only one valid next hop in the FIB. reffile = os.path.join(CWD, "r1/prefix3-ip-route.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip route {} json".format(prefix3), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "FIB next hops mismatch for all-eBGP multipath" assert res is None, assertMsg # check confed-external enables recursively resolved next hops by itself exabgp_cmd( "peer1", "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( prefix1, resolved_nh1), ) reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip route {} json".format(prefix1), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" assert res is None, assertMsg # check iBGP by itself exabgp_cmd( "peer1", "announce route {} next-hop {} as-path [ 64499 ]\n".format( prefix1, resolved_nh1), ) exabgp_cmd( "peer2", "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( prefix1, resolved_nh2), ) reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json") expected = json.loads(open(reffile).read()) test_func = functools.partial( topotest.router_json_cmp, router, "show ip route {} json".format(prefix1), expected, ) _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath" assert res is None, assertMsg
def test_ecmp_after_clear_bgp(request, test_type): """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() reset_config_on_routers(tgen) # Verifying RIB routes dut = "r3" protocol = "bgp" static_or_nw(tgen, topo, tc_name, test_type, "r2") for addr_type in ADDR_TYPES: input_dict_1 = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_1, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) # Clear BGP for addr_type in ADDR_TYPES: clear_bgp(tgen, addr_type, dut) # Verify BGP convergence result = verify_bgp_convergence(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) for addr_type in ADDR_TYPES: input_dict_1 = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_1, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name)
def _check_sharpd_chunk(router, expected_chunk_file): logger.info("checking sharpd locator chunk status") output = json.loads( router.vtysh_cmd("show sharp segment-routing srv6 json")) expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) return topotest.json_cmp(output, expected)
def test_ecmp_shut_bgp_neighbor(request, test_type): """ Shut BGP neigbors one by one and verify BGP and routing table updated accordingly in DUT """ tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() logger.info(INTF_LIST_R2) # Verifying RIB routes dut = "r3" protocol = "bgp" reset_config_on_routers(tgen) static_or_nw(tgen, topo, tc_name, test_type, "r2") for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) for intf_num in range(len(INTF_LIST_R2) + 1, 16): intf_val = INTF_LIST_R2[intf_num:intf_num + 16] input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}} logger.info( "Shutting down neighbor interface {} on r2".format(intf_val)) result = interface_status(tgen, topo, input_dict_1) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) for addr_type in ADDR_TYPES: if intf_num + 16 < 32: check_hops = NEXT_HOPS[addr_type] else: check_hops = [] input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}} logger.info("Enabling all neighbor interface {} on r2") result = interface_status(tgen, topo, input_dict_1) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) static_or_nw(tgen, topo, tc_name, test_type, "r2") for addr_type in ADDR_TYPES: input_dict = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name)
def test_srv6(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) router = tgen.gears['r1'] def _check_srv6_locator(router, expected_locator_file): logger.info("checking zebra locator status") output = json.loads( router.vtysh_cmd("show segment-routing srv6 locator json")) expected = open_json_file("{}/{}".format(CWD, expected_locator_file)) return topotest.json_cmp(output, expected) def _check_sharpd_chunk(router, expected_chunk_file): logger.info("checking sharpd locator chunk status") output = json.loads( router.vtysh_cmd("show sharp segment-routing srv6 json")) expected = open_json_file("{}/{}".format(CWD, expected_chunk_file)) return topotest.json_cmp(output, expected) def check_srv6_locator(router, expected_file): func = functools.partial(_check_srv6_locator, router, expected_file) success, result = topotest.run_and_expect(func, None, count=5, wait=0.5) assert result is None, 'Failed' def check_sharpd_chunk(router, expected_file): func = functools.partial(_check_sharpd_chunk, router, expected_file) success, result = topotest.run_and_expect(func, None, count=5, wait=0.5) assert result is None, 'Failed' logger.info("Test1 for Locator Configuration") check_srv6_locator(router, "expected_locators1.json") check_sharpd_chunk(router, "expected_chunks1.json") logger.info("Test2 get chunk for locator loc1") router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1") check_srv6_locator(router, "expected_locators2.json") check_sharpd_chunk(router, "expected_chunks2.json") logger.info("Test3 release chunk for locator loc1") router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1") check_srv6_locator(router, "expected_locators3.json") check_sharpd_chunk(router, "expected_chunks3.json") logger.info("Test4 get chunk for non-exist locator by zclient") router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc3") check_srv6_locator(router, "expected_locators4.json") check_sharpd_chunk(router, "expected_chunks4.json") logger.info("Test5 Test for Zclient. after locator loc3 was configured") router.vtysh_cmd(""" configure terminal segment-routing srv6 locators locator loc3 prefix 2001:db8:3:3::/64 """) check_srv6_locator(router, "expected_locators5.json") check_sharpd_chunk(router, "expected_chunks5.json")
def test_ecmp_remove_static_route(request): """ Delete static routes and verify routers are cleared from BGP table, and RIB of DUT. """ tc_name = request.node.name write_test_header(tc_name) tgen = get_topogen() # Verifying RIB routes dut = "r3" protocol = "bgp" reset_config_on_routers(tgen) static_or_nw(tgen, topo, tc_name, "redist_static", "r2") for addr_type in ADDR_TYPES: input_dict_1 = { "r3": { "static_routes": [{ "network": NETWORK[addr_type] }] } } logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_1, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) for addr_type in ADDR_TYPES: input_dict_2 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type], "delete": True, }] } } logger.info("Remove static routes") result = create_static_routes(tgen, input_dict_2) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) logger.info("Verifying %s routes on r3 are removed", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_2, next_hop=[], protocol=protocol, expected=False, ) assert ( result is not True ), "Testcase {} : Failed \n Routes still" " present in RIB".format( tc_name) for addr_type in ADDR_TYPES: # Enable static routes input_dict_4 = { "r2": { "static_routes": [{ "network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type] }] } } logger.info("Enable static route") result = create_static_routes(tgen, input_dict_4) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) logger.info("Verifying %s routes on r3", addr_type) result = verify_rib( tgen, addr_type, dut, input_dict_4, next_hop=NEXT_HOPS[addr_type], protocol=protocol, ) assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result) write_test_footer(tc_name)
def test_protocols_convergence(): """ Assert that all protocols have converged statuses as they depend on it. """ tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) topotest.sleep(4, "waiting 4 seconds for bgp convergence") # Check IPv4/IPv6 routing tables. output = tgen.gears["r1"].vtysh_cmd("show bgp l2vpn evpn", isjson=False) logger.info("==== result from show bgp l2vpn evpn") logger.info(output) output = tgen.gears["r1"].vtysh_cmd( "show bgp l2vpn evpn route detail", isjson=False ) logger.info("==== result from show bgp l2vpn evpn route detail") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101 ipv4", isjson=False) logger.info("==== result from show bgp vrf r1-vrf-101 ipv4") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101", isjson=False) logger.info("==== result from show bgp vrf r1-vrf-101 ") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show ip route vrf r1-vrf-101", isjson=False) logger.info("==== result from show ip route vrf r1-vrf-101") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show evpn vni detail", isjson=False) logger.info("==== result from show evpn vni detail") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show evpn next-hops vni all", isjson=False) logger.info("==== result from show evpn next-hops vni all") logger.info(output) output = tgen.gears["r1"].vtysh_cmd("show evpn rmac vni all", isjson=False) logger.info("==== result from show evpn next-hops vni all") logger.info(output) # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn) pingrouter = tgen.gears["r1"] logger.info( "Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)" ) output = pingrouter.run("ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000") logger.info(output) if "1000 packets transmitted, 1000 received" not in output: assertmsg = ( "expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok" ) assert 0, assertmsg else: logger.info("Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK")
def test_bgp_gshut(): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) r1 = tgen.gears["r1"] r2 = tgen.gears["r2"] r3 = tgen.gears["r3"] r4 = tgen.gears["r4"] r5 = tgen.gears["r5"] # Verify initial route states logger.info("\nVerify initial route states") _, result = _run_cmd_and_check(r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json") assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json") assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json") assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg logger.info("\nInitial route states are as expected") # "Test #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers" logger.info( "\nTest #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers" ) r2.vtysh_cmd(""" configure terminal bgp graceful-shutdown """) # R1, R3 and R5 should see routes from R2 with GSHUT. In addition, # R1 should see LOCAL_PREF of 0 _, result = _run_cmd_and_check(r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_2.json") assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_2.json") assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json") assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg logger.info( "\nTest #1: Successful, routes have GSHUT and/or LPREF of 0 as expected" ) # "Test #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers" logger.info( "\nTest #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers" ) r2.vtysh_cmd(""" configure terminal no bgp graceful-shutdown """) # R1, R3 and R5 should see routes from R2 with their original attributes _, result = _run_cmd_and_check(r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json") assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json") assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json") assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg logger.info( "\nTest #2: Successful, routes have their original attributes with default LPREF and without GSHUT" ) # "Test #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers" logger.info( "\nTest #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers" ) r2.vtysh_cmd(""" configure terminal router bgp 65001 vrf vrf1 bgp graceful-shutdown """) # R1 and R3 should see no change to their routes _, result = _run_cmd_and_check(r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json") assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json") assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg # R5 should see routes from R2 with GSHUT. _, result = _run_cmd_and_check(r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json") assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg logger.info( "\nTest #3: Successful, only VRF peers like R5 see routes with GSHUT") # "Test #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1" logger.info( "\nTest #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1" ) ret = r2.vtysh_cmd(""" configure terminal bgp graceful-shutdown """) # This should fail assertmsg = "R2: BGP-wide graceful-shutdown config not rejected even though it is enabled in VRF1" assert (re.search("global graceful-shutdown not permitted", ret) is not None), assertmsg logger.info( "\nTest #4: Successful, BGP-wide graceful-shutdown rejected as it is enabled in VRF" ) # "Test #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers" logger.info( "\nTest #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers" ) r2.vtysh_cmd(""" configure terminal router bgp 65001 vrf vrf1 no bgp graceful-shutdown """) # R1 and R3 should see no change to their routes _, result = _run_cmd_and_check(r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json") assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg _, result = _run_cmd_and_check(r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json") assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg # R5 should see routes from R2 with original attributes. _, result = _run_cmd_and_check(r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json") assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params" assert result is None, assertmsg logger.info( "\nTest #5: Successful, routes have their original attributes with default LPREF and without GSHUT" )