def check_for_memleaks(): assert topotest_extra_config["valgrind_memleaks"] leaks = [] tgen = get_topogen() latest = [] existing = [] if tgen is not None: logdir = tgen.logdir if hasattr(tgen, "valgrind_existing_files"): existing = tgen.valgrind_existing_files latest = glob.glob(os.path.join(logdir, "*.valgrind.*")) daemons = set() for vfile in latest: if vfile in existing: continue existing.append(vfile) with open(vfile, encoding="ascii") as vf: vfcontent = vf.read() match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent) if match and match.group(1) != "0": emsg = "{} in {}".format(match.group(1), vfile) leaks.append(emsg) daemons.add( re.match(r".*\.valgrind\.(.*)\.\d+", vfile).group(1)) if tgen is not None: tgen.valgrind_existing_files = existing if leaks: logger.error("valgrind memleaks found:\n\t%s", "\n\t".join(leaks)) pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
def _print_bgp_neighbors_cfg(frr_cfg, neighbor): neigh_ip = IpAddressMsg_to_str(neighbor.ip_address) neigh_cxt = 'neighbor ' + neigh_ip + ' ' frr_cfg.bgp_neighbors.write(neigh_cxt + 'remote-as ' + str(neighbor.remote_as) + '\n') frr_cfg.bgp_neighbors.write(neigh_cxt + 'activate\n') frr_cfg.bgp_neighbors.write(neigh_cxt + 'disable-connected-check\n') if neighbor.update_source != None: frr_cfg.bgp_neighbors.write(neigh_cxt + 'update-source ' + neighbor.update_source + ' \n') keep_alive = '60' hold_down = '180' if neighbor.keep_alive_time and neighbor.hold_down_time: keep_alive = str(neighbor.keep_alive_time) hold_down = str(neighbor.hold_down_time) frr_cfg.bgp_neighbors.write(neigh_cxt + 'timers ' + keep_alive + ' ' + hold_down + '\n') if neighbor.password != None: frr_cfg.bgp_neighbors.write(neigh_cxt + 'password ' + neighbor.password + '\n') if neighbor.max_hop_limit > 1: frr_cfg.bgp_neighbors.write(neigh_cxt + 'ebgp-multihop ' + str(neighbor.max_hop_limit) + '\n') frr_cfg.bgp_neighbors.write(neigh_cxt + 'enforce-multihop\n') for addr_family in neighbor.address_families: if addr_family.type not in [IPv4_UNICAST, IPv6_UNICAST, VPNv4_UNICAST]: logger.error('unsupported address family') return False if addr_family.type == VPNv4_UNICAST and not addr_family.enabled: logger.error('vpnv4 family is not enabled') return False _print_bgp_address_family_cfg(frr_cfg, neigh_ip, addr_family)
def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened if call.excinfo is None: return parent = item.parent modname = parent.module.__name__ # Treat skips as non errors if call.excinfo.typename != "AssertionError": logger.info('assert skipped at "{}/{}": {}'.format( modname, item.name, call.excinfo.value)) return # Handle assert failures parent._previousfailed = item logger.error('assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value)) # (topogen) Set topology error to avoid advancing in the test. tgen = get_topogen() if tgen is not None: # This will cause topogen to report error on `routers_have_failure`. tgen.set_error("{}/{}".format(modname, item.name))
def create_common_configuration(tgen, router, data, config_type=None, build=False): """ API to create object of class FRRConfig and also create frr_json.conf file. It will create interface and common configurations and save it to frr_json.conf and load to router Parameters ---------- * `tgen`: tgen onject * `data`: Congiguration data saved in a list. * `router` : router id to be configured. * `config_type` : Syntactic information while writing configuration. Should be one of the value as mentioned in the config_map below. * `build` : Only for initial setup phase this is set as True Returns ------- True or False """ TMPDIR = os.path.join(LOGDIR, tgen.modname) fname = "{}/{}/{}".format(TMPDIR, router, FRRCFG_FILE) config_map = OrderedDict({ "general_config": "! FRR General Config\n", "interface_config": "! Interfaces Config\n", "static_route": "! Static Route Config\n", "prefix_list": "! Prefix List Config\n", "route_maps": "! Route Maps Config\n", "bgp": "! BGP Config\n" }) if build: mode = "a" else: mode = "w" try: frr_cfg_fd = open(fname, mode) if config_type: frr_cfg_fd.write(config_map[config_type]) for line in data: frr_cfg_fd.write("{} \n".format(str(line))) except IOError as err: logger.error("Unable to open FRR Config File. error(%s): %s" % (err.errno, err.strerror)) return False finally: frr_cfg_fd.close() # If configuration applied from build, it will done at last if not build: load_config_to_router(tgen, router) return True
def check_for_memleaks(): if not topotest_extra_config["valgrind_memleaks"]: return leaks = [] tgen = get_topogen() latest = [] existing = [] if tgen is not None: logdir = "/tmp/topotests/{}".format(tgen.modname) if hasattr(tgen, "valgrind_existing_files"): existing = tgen.valgrind_existing_files latest = glob.glob(os.path.join(logdir, "*.valgrind.*")) for vfile in latest: if vfile in existing: continue with open(vfile) as vf: vfcontent = vf.read() match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent) if match and match.group(1) != "0": emsg = '{} in {}'.format(match.group(1), vfile) leaks.append(emsg) if leaks: if leak_check_ok: pytest.fail("Memleaks found:\n\t" + "\n\t".join(leaks)) else: logger.error("Memleaks found:\n\t" + "\n\t".join(leaks))
def ltemplateTest(script, SkipIfFailed=True, CallOnFail=None, CheckFuncStr=None, KeepGoing=False): global _lt if _lt == None or _lt.prestarthooksuccess != True: return tgen = get_topogen() if not os.path.isfile(script): if not os.path.isfile(os.path.join(_lt.scriptdir, script)): logger.error('Could not find script file: ' + script) assert 'Could not find script file: ' + script logger.info("Starting template test: " + script) numEntry = luNumFail() if SkipIfFailed and tgen.routers_have_failure(): pytest.skip(tgen.errors) if numEntry > 0: if not KeepGoing: pytest.skip("Have %d errors" % numEntry) if CheckFuncStr != None: check = eval(CheckFuncStr) if check != True: pytest.skip("Check function '"+CheckFuncStr+"' returned: " + check) if CallOnFail != None: CallOnFail = eval(CallOnFail) luInclude(script, CallOnFail) numFail = luNumFail() - numEntry if numFail > 0: luShowFail() fatal_error = "%d tests failed" % numFail if not KeepGoing: assert "scripts/cleanup_all.py failed" == "See summary output above", fatal_error
def redist_cfg(frr_cfg, topo): """ To redistribute static and connected routes for given router. * `topo` : json file data * `frrcfg` : frr config file to save router config """ try: if frr_cfg.is_bgp_configured: if frr_cfg.routing_pb.redist_static_routes_flag == 'bgp': for router, number in number_to_router.iteritems(): if number == frr_cfg.router: neighbors = topo['routers']['{}'.format( router)]["bgp"]['bgp_neighbors'] for key, value in neighbors.iteritems(): peer = neighbors[key]['peer'] ADDR_TYPE = peer['addr_type'] if ADDR_TYPE == "ipv4": # IPv4 frr_cfg.bgp_address_family[IPv4_UNICAST].write( 'redistribute static\n') frr_cfg.bgp_address_family[IPv4_UNICAST].write( 'redistribute connected\n') else: # IPv6 frr_cfg.bgp_address_family[IPv6_UNICAST].write( 'redistribute static\n') frr_cfg.bgp_address_family[IPv6_UNICAST].write( 'redistribute connected\n') except Exception as e: logger.error(traceback.format_exc())
def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened if call.when == "call": pause = topotest_extra_config["pause_after"] else: pause = False if call.excinfo is None: error = False else: parent = item.parent modname = parent.module.__name__ # Treat skips as non errors, don't pause after if call.excinfo.typename != "AssertionError": pause = False error = False logger.info( 'assert skipped at "{}/{}": {}'.format( modname, item.name, call.excinfo.value ) ) else: error = True # Handle assert failures parent._previousfailed = item # pylint: disable=W0212 logger.error( 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value) ) # (topogen) Set topology error to avoid advancing in the test. tgen = get_topogen() if tgen is not None: # This will cause topogen to report error on `routers_have_failure`. tgen.set_error("{}/{}".format(modname, item.name)) if error and topotest_extra_config["shell_on_error"]: for router in tgen.routers(): pause = True tgen.net[router].runInWindow(os.getenv("SHELL", "bash")) if error and topotest_extra_config["vtysh_on_error"]: for router in tgen.routers(): pause = True tgen.net[router].runInWindow("vtysh") if error and topotest_extra_config["mininet_on_error"]: tgen.mininet_cli() if pause: try: user = raw_input('Testing paused, "pdb" to debug, "Enter" to continue: ') except NameError: user = input('Testing paused, "pdb" to debug, "Enter" to continue: ') if user.strip() == "pdb": pdb.set_trace()
def push_to_frr(self): for _ in range(3): if self._push_to_frr(): logger.info('Successfully pushed the config to FRR') return True logger.error('Unable to push the config to FRR') return False
def luShowFail(): printed = 0 sf = open(LUtil.fsum_name, "r") for line in sf: if line[-2] != "0": printed += 1 logger.error(line.rstrip()) sf.close() if printed > 0: logger.error("See %s for details of errors" % LUtil.fout_name)
def __create_ospf6_global(tgen, input_dict, router, build=False, load_config=True): """ Helper API to create ospf global configuration. Parameters ---------- * `tgen` : Topogen object * `input_dict` : Input dict data, required when configuring from testcase * `router` : router id to be configured. * `build` : Only for initial setup phase this is set as True. Returns ------- True or False """ result = False logger.debug("Entering lib API: __create_ospf_global()") try: ospf_data = input_dict[router]["ospf6"] del_ospf_action = ospf_data.setdefault("delete", False) if del_ospf_action: config_data = ["no ipv6 router ospf"] result = create_common_configuration(tgen, router, config_data, "ospf", build, load_config) return result config_data = [] cmd = "router ospf" config_data.append(cmd) router_id = ospf_data.setdefault("router_id", None) del_router_id = ospf_data.setdefault("del_router_id", False) if del_router_id: config_data.append("no ospf router-id") if router_id: config_data.append("ospf router-id {}".format(router_id)) result = create_common_configuration(tgen, router, config_data, "ospf", build, load_config) except InvalidCLIError: # Traceback errormsg = traceback.format_exc() logger.error(errormsg) return errormsg logger.debug("Exiting lib API: create_ospf_global()") return result
def run_shell_command(self, cmdline): logger.debug('executing command: %s', ' '.join(cmdline)) try: output = subprocess.check_output(cmdline, stderr=subprocess.STDOUT, close_fds=True) logger.debug('output: %s', output) return True except subprocess.CalledProcessError as cpe: logger.error('Failed to execute: rc=%s, out=%s, err=%s', cpe.returncode, cpe.output, cpe) return False
def create_config_files(tgen, CWD, topo): """ It will create BGP basic configuration(router-id only) and save it to frr.conf, then it will call APIs to create BGP and interface configuration and all configuration would be saved in frr.conf file. * `tgen` : Topogen object * `CWD` : caller's current working directory * `topo` : json file data """ try: listRouters = [] for routerN in topo['routers'].iteritems(): listRouters.append(routerN[0]) listRouters.sort() assign_number_to_routers(listRouters) logger.info('Configuring nodes') for curRouter in number_to_router.keys(): router = curRouter i = number_to_router[curRouter] if 'router_id' in topo['routers'][router]['bgp']: ip = topo['routers'][router]['bgp']['router-id'] ip = Address(ADDR_TYPE_IPv4, ip, None) rid = int(socket.inet_aton(ip.ipv4).encode('hex'), 16) router_id = Address(ADDR_TYPE_IPv4, rid, None) else: router_id = None rt_cfg = RoutingPB(router_id) fname = '%s/r%d/frr.conf' % (CWD, i) frr_cfg[i] = FRRConfig(i, rt_cfg, fname) frr_cfg[i].is_standby = False frr_cfg[i].routing_pb.interfaces_config = create_interfaces_cfg( router, topo) frr_cfg[i].routing_pb.bgp_config = create_bgp_cfg(router, topo) interfaces_cfg(frr_cfg[i]) bgp_cfg(frr_cfg[i]) static_rt_cfg(frr_cfg[i]) redist_cfg(frr_cfg[i], topo) frr_cfg[i].print_to_file(topo) # Load config to router load_config_to_router(tgen, CWD, router) except Exception as e: logger.error(traceback.format_exc()) return False else: return frr_cfg
def create_interfaces_cfg(tgen, topo, build=False): """ Create interface configuration for created topology. Basic Interface configuration is provided in input json file. Parameters ---------- * `tgen` : Topogen object * `topo` : json file data * `build` : Only for initial setup phase this is set as True. Returns ------- True or False """ result = False try: for c_router, c_data in topo.iteritems(): interface_data = [] for destRouterLink, data in sorted(c_data["links"].iteritems()): # Loopback interfaces if "type" in data and data["type"] == "loopback": interface_name = destRouterLink else: interface_name = data["interface"] interface_data.append("interface {}\n".format( str(interface_name) )) if "ipv4" in data: intf_addr = c_data["links"][destRouterLink]["ipv4"] interface_data.append("ip address {}\n".format( intf_addr )) if "ipv6" in data: intf_addr = c_data["links"][destRouterLink]["ipv6"] interface_data.append("ipv6 address {}\n".format( intf_addr )) result = create_common_configuration(tgen, c_router, interface_data, "interface_config", build=build) except InvalidCLIError: # Traceback errormsg = traceback.format_exc() logger.error(errormsg) return errormsg return result
def print_to_file(self, topo): try: frrcfg = open(self.frrcfg_file, 'w') except IOError as err: logger.error('Unable to open FRR Config File. error(%s): %s' % (err.errno, err.strerror)) return False frrcfg.write('! FRR General Config\n') frrcfg.write(self.routing_common.getvalue()) frrcfg.write('! Interfaces Config\n') frrcfg.write(self.interfaces_cfg.getvalue()) # If bgp neighborship is being done using loopback interface - # - then we have make the loopback interface reachability up - # - for that we are adding static routes - for router, number in number_to_router.iteritems(): if number == self.router: neighbors = topo['routers']['{}'.format( router)]["bgp"]['bgp_neighbors'] for key, value in neighbors.iteritems(): peer = neighbors[key]['peer'] ADDR_TYPE = peer['addr_type'] if "source" in peer and peer['source'] == 'lo': add_static_route_for_loopback_interfaces( ADDR_TYPE, router, topo, frrcfg) frrcfg.write('! Static Route Config\n') frrcfg.write(self.static_routes.getvalue()) frrcfg.write('! Access List Config\n') frrcfg.write(self.access_lists.getvalue()) frrcfg.write('! Prefix List Config\n') frrcfg.write(self.prefix_lists.getvalue()) frrcfg.write('! Route Maps Config\n') frrcfg.write(self.route_maps.getvalue()) frrcfg.write('! Community List Config\n') frrcfg.write(self.community_list.getvalue()) if self.is_bgp_configured: frrcfg.write('! BGP Config\n') frrcfg.write(self.bgp_global.getvalue()) frrcfg.write(self.bgp_neighbors.getvalue()) for addr_family in self.bgp_address_family: frrcfg.write('address-family ' + get_address_family(addr_family) + '\n') frrcfg.write(self.bgp_address_family[addr_family].getvalue()) frrcfg.write('exit-address-family\n') frrcfg.write('line vty\n') frrcfg.close() return True
def start_topology(tgen): """ Starting topology, create tmp files which are loaded to routers to start deamons and then start routers * `tgen` : topogen object """ global TMPDIR # Starting topology tgen.start_topology() # Starting deamons router_list = tgen.routers() TMPDIR = os.path.join(LOGDIR, tgen.modname) for rname, router in router_list.iteritems(): try: os.chdir(TMPDIR) # Creating rouer named dir and empty zebra.conf bgpd.conf files # inside the current directory if os.path.isdir('{}'.format(rname)): os.system("rm -rf {}".format(rname)) os.mkdir('{}'.format(rname)) os.system('chmod -R go+rw {}'.format(rname)) os.chdir('{}/{}'.format(TMPDIR, rname)) os.system('touch zebra.conf bgpd.conf') else: os.mkdir('{}'.format(rname)) os.system('chmod -R go+rw {}'.format(rname)) os.chdir('{}/{}'.format(TMPDIR, rname)) os.system('touch zebra.conf bgpd.conf') except IOError as (errno, strerror): logger.error("I/O error({0}): {1}".format(errno, strerror)) # Loading empty zebra.conf file to router, to start the zebra deamon router.load_config( TopoRouter.RD_ZEBRA, '{}/{}/zebra.conf'.format(TMPDIR, rname) # os.path.join(tmpdir, '{}/zebra.conf'.format(rname)) ) # Loading empty bgpd.conf file to router, to start the bgp deamon router.load_config( TopoRouter.RD_BGP, '{}/{}/bgpd.conf'.format(TMPDIR, rname) # os.path.join(tmpdir, '{}/bgpd.conf'.format(rname)) )
def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): """ Run `func` and compare the result with `etype`. Do it for `count` times waiting `wait` seconds between tries. By default it tries 20 times with 3 seconds delay between tries. This function is used when you want to test the return type and, optionally, the return value. Returns (True, func-return) on success or (False, func-return) on failure. """ start_time = time.time() func_name = "<unknown>" if func.__class__ == functools.partial: func_name = func.func.__name__ else: func_name = func.__name__ logger.info( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( func_name, wait, int(wait * count))) while count > 0: result = func() if not isinstance(result, etype): logger.debug("Expected result type '{}' got '{}' instead".format( etype, type(result))) time.sleep(wait) count -= 1 continue if etype != type(None) and avalue != None and result != avalue: logger.debug("Expected value '{}' got '{}' instead".format( avalue, result)) time.sleep(wait) count -= 1 continue end_time = time.time() logger.info("'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time)) return (True, result) end_time = time.time() logger.error("'{}' failed after {:.2f} seconds".format( func_name, end_time - start_time)) return (False, result)
def stop_topology(self): """ Stops the network topology. This function will call the stop() function of all gears before calling the mininet stop function, so they can have their oportunity to do a graceful shutdown. stop() is called twice. The first is a simple kill with no sleep, the second will sleep if not killed and try with a different signal. """ logger.info("stopping topology: {}".format(self.modname)) errors = "" for gear in self.gears.values(): errors += gear.stop() if len(errors) > 0: logger.error( "Errors found post shutdown - details follow: {}".format( errors)) self.net.stop()
def setup_module(mod): """ Sets up the pytest environment * `mod`: module name """ 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(TemplateTopo, mod.__name__) # ... and here it calls Mininet initialization functions. # Starting topology tgen.start_topology() # Uncomment following line to enable debug logs and comment - tgen.start_topology() #tgen.start_topology(log_level='debug') router_list = tgen.routers() for rname, router in router_list.iteritems(): try: os.chdir(CWD) # Deleting router named dirs if exists if os.path.exists('{}'.format(rname)): os.system("rm -rf {}".format(rname)) # Creating rouer named dir and emoty zebra.conf bgpd.conf files inside the current directory os.mkdir('{}'.format(rname)) os.chdir("{}/{}".format(CWD, rname)) os.system('touch zebra.conf bgpd.conf') except IOError as (errno, strerror): logger.error("I/O error({0}): {1}".format(errno, strerror)) # Loading empty zebra.conf file to router, to start the zebra deamon router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, '{}/zebra.conf'.format(rname))) # Loading empty bgpd.conf file to router, to start the bgp deamon router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, '{}/bgpd.conf'.format(rname)))
def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened if call.excinfo is None: return parent = item.parent modname = parent.module.__name__ # Treat skips as non errors if call.excinfo.typename != 'AssertionError': logger.info('assert skipped at "{}/{}": {}'.format( modname, item.name, call.excinfo.value)) return # Handle assert failures parent._previousfailed = item logger.error('assert failed at "{}/{}": {}'.format( modname, item.name, call.excinfo.value))
def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" # Nothing happened if call.excinfo is None: return parent = item.parent modname = parent.module.__name__ # Treat skips as non errors if call.excinfo.typename != 'AssertionError': logger.info('assert skipped at "{}/{}": {}'.format( modname, item.name, call.excinfo.value)) return # Handle assert failures parent._previousfailed = item logger.error('assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value))
def run_and_expect(func, what, count=20, wait=3): """ Run `func` and compare the result with `what`. Do it for `count` times waiting `wait` seconds between tries. By default it tries 20 times with 3 seconds delay between tries. Returns (True, func-return) on success or (False, func-return) on failure. --- Helper functions to use with this function: - router_output_cmp - router_json_cmp """ start_time = time.time() func_name = "<unknown>" if func.__class__ == functools.partial: func_name = func.func.__name__ else: func_name = func.__name__ logger.info( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( func_name, wait, int(wait * count))) while count > 0: result = func() if result != what: time.sleep(wait) count -= 1 continue end_time = time.time() logger.info("'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time)) return (True, result) end_time = time.time() logger.error("'{}' failed after {:.2f} seconds".format( func_name, end_time - start_time)) return (False, result)
def check_for_memleaks(): assert topotest_extra_config["valgrind_memleaks"] leaks = [] tgen = get_topogen() # pylint: disable=redefined-outer-name latest = [] existing = [] if tgen is not None: logdir = tgen.logdir if hasattr(tgen, "valgrind_existing_files"): existing = tgen.valgrind_existing_files latest = glob.glob(os.path.join(logdir, "*.valgrind.*")) latest = [x for x in latest if "core" not in x] daemons = set() for vfile in latest: if vfile in existing: continue # do not consider memleaks from parent fork (i.e., owned by root) if os.stat(vfile).st_uid == 0: existing.append(vfile) # do not check again logger.debug("Skipping valgrind file %s owned by root", vfile) continue logger.debug("Checking valgrind file %s not owned by root", vfile) with open(vfile, encoding="ascii") as vf: vfcontent = vf.read() match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent) if match: existing.append(vfile) # have summary don't check again if match and match.group(1) != "0": emsg = "{} in {}".format(match.group(1), vfile) leaks.append(emsg) daemon = re.match(r".*\.valgrind\.(.*)\.\d+", vfile).group(1) daemons.add("{}({})".format(daemon, match.group(1))) if tgen is not None: tgen.valgrind_existing_files = existing if leaks: logger.error("valgrind memleaks found:\n\t%s", "\n\t".join(leaks)) pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
def check_address_types(addr_type): """ Checks environment variable set and compares with the current address type """ global ADDRESS_TYPES if ADDRESS_TYPES is None: ADDRESS_TYPES = "dual" if ADDRESS_TYPES == "dual": ADDRESS_TYPES = ["ipv4", "ipv6"] elif ADDRESS_TYPES == "ipv4": ADDRESS_TYPES = ["ipv4"] elif ADDRESS_TYPES == "ipv6": ADDRESS_TYPES = ["ipv6"] if addr_type not in ADDRESS_TYPES: logger.error("{} not in supported/configured address types {}". format(addr_type, ADDRESS_TYPES)) return False return ADDRESS_TYPES
def test_shutdown_check_stderr(): if os.environ.get('TOPOTESTS_CHECK_STDERR') is None: pytest.skip('Skipping test for Stderr output and memory leaks') tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) logger.info("Verifying unexpected STDERR output from daemons") router_list = tgen.routers().values() for router in router_list: router.stop() log = tgen.net[router.name].getStdErr('eigrpd') if log: logger.error('EIGRPd StdErr Log:' + log) log = tgen.net[router.name].getStdErr('zebra') if log: logger.error('Zebra StdErr Log:' + log)
def teardown_module(mod): """ Teardown the pytest environment * `mod`: module name """ logger.info("Running teardown_module to delete topology") tgen = get_topogen() # This function tears down the whole topology. tgen.stop_topology() # Removing tmp dirs and files router_list = tgen.routers() for rname, router in router_list.iteritems(): try: os.chdir(CWD) os.system("rm -rf {}".format(rname)) except IOError as (errno, strerror): logger.error("I/O error({0}): {1}".format(errno, strerror))
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') hasfrr = False if not os.path.isdir(frrdir): logger.error('could not find {} directory'.format(frrdir)) ret = False else: hasfrr = True 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']: 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) ) # Assert that Quagga utilities exist quaggadir = config.get('topogen', 'quaggadir') if hasfrr: # if we have frr, don't check for quagga pass elif not os.path.isdir(quaggadir): logger.info('could not find {} directory (quagga tests will not run)'.format(quaggadir)) else: ret = True try: pwd.getpwnam('quagga')[2] except KeyError: logger.info('could not find "quagga" user') try: grp.getgrnam('quagga')[2] except KeyError: logger.info('could not find "quagga" group') try: if 'quagga' not in grp.getgrnam('quaggavty').gr_mem: logger.error('"quagga" user and group exist, but user is not under "quaggavty"') except KeyError: logger.warning('could not find "quaggavty" group') for fname in ['zebra', 'ospfd', 'ospf6d', 'bgpd', 'ripd', 'ripngd', 'isisd', 'pimd']: path = os.path.join(quaggadir, fname) if not os.path.isfile(path): logger.warning('could not find {} in {}'.format(fname, quaggadir)) ret = False else: if fname != 'zebra': continue os.system( '{} -v 2>&1 >/tmp/topotests/quagga_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 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", "pim6d", "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 create_route_maps(tgen, input_dict, build=False): """ Create route-map on the devices as per the arguments passed Parameters ---------- * `tgen` : Topogen object * `input_dict` : Input dict data, required when configuring from testcase * `build` : Only for initial setup phase this is set as True. Usage ----- # route_maps: key, value pair for route-map name and its attribute # rmap_match_prefix_list_1: user given name for route-map # action: PERMIT/DENY # match: key,value pair for match criteria. prefix_list, community-list, large-community-list or tag. Only one option at a time. # prefix_list: name of prefix list # large-community-list: name of large community list # community-ist: name of community list # tag: tag id for static routes # set: key, value pair for modifying route attributes # localpref: preference value for the network # med: metric value advertised for AS # aspath: set AS path value # weight: weight for the route # community: standard community value to be attached # large_community: large community value to be attached # community_additive: if set to "additive", adds community/large-community value to the existing values of the network prefix Example: -------- input_dict = { "r1": { "route_maps": { "rmap_match_prefix_list_1": [ { "action": "PERMIT", "match": { "ipv4": { "prefix_list": "pf_list_1" } "ipv6": { "prefix_list": "pf_list_1" } "large-community-list": "{ "id": "community_1", "exact_match": True } "community": { "id": "community_2", "exact_match": True } "tag": "tag_id" }, "set": { "localpref": 150, "med": 30, "aspath": { "num": 20000, "action": "prepend", }, "weight": 500, "community": { "num": "1:2 2:3", "action": additive } "large_community": { "num": "1:2:3 4:5;6", "action": additive }, } } ] } } } Returns ------- errormsg(str) or True """ result = False logger.debug("Entering lib API: create_route_maps()") try: for router in input_dict.keys(): if "route_maps" not in input_dict[router]: errormsg = "route_maps not present in input_dict" logger.info(errormsg) continue rmap_data = [] for rmap_name, rmap_value in \ input_dict[router]["route_maps"].iteritems(): for rmap_dict in rmap_value: del_action = rmap_dict.setdefault("delete", False) if del_action: rmap_data.append("no route-map {}".format(rmap_name)) continue if "action" not in rmap_dict: errormsg = "action not present in input_dict" logger.error(errormsg) return False rmap_action = rmap_dict.setdefault("action", "deny") seq_id = rmap_dict.setdefault("seq_id", None) if seq_id is None: seq_id = get_seq_id("route_maps", router, rmap_name) else: set_seq_id("route_maps", router, seq_id, rmap_name) rmap_data.append("route-map {} {} {}".format( rmap_name, rmap_action, seq_id )) # Verifying if SET criteria is defined if "set" in rmap_dict: set_data = rmap_dict["set"] local_preference = set_data.setdefault("localpref", None) metric = set_data.setdefault("med", None) as_path = set_data.setdefault("aspath", {}) weight = set_data.setdefault("weight", None) community = set_data.setdefault("community", {}) large_community = set_data.setdefault( "large_community", {}) set_action = set_data.setdefault("set_action", None) # Local Preference if local_preference: rmap_data.append("set local-preference {}". format(local_preference)) # Metric if metric: rmap_data.append("set metric {} \n".format(metric)) # AS Path Prepend if as_path: as_num = as_path.setdefault("as_num", None) as_action = as_path.setdefault("as_action", None) if as_action and as_num: rmap_data.append("set as-path {} {}". format(as_action, as_num)) # Community if community: num = community.setdefault("num", None) comm_action = community.setdefault("action", None) if num: cmd = "set community {}".format(num) if comm_action: cmd = "{} {}".format(cmd, comm_action) rmap_data.append(cmd) else: logger.error("In community, AS Num not" " provided") return False if large_community: num = large_community.setdefault("num", None) comm_action = large_community.setdefault("action", None) if num: cmd = "set large-community {}".format(num) if comm_action: cmd = "{} {}".format(cmd, comm_action) rmap_data.append(cmd) else: logger.errror("In large_community, AS Num not" " provided") return False # Weight if weight: rmap_data.append("set weight {} \n".format( weight)) # Adding MATCH and SET sequence to RMAP if defined if "match" in rmap_dict: match_data = rmap_dict["match"] ipv4_data = match_data.setdefault("ipv4", {}) ipv6_data = match_data.setdefault("ipv6", {}) community = match_data.setdefault("community-list", {}) large_community = match_data.setdefault( "large-community-list", {} ) tag = match_data.setdefault("tag", None) if ipv4_data: prefix_name = ipv4_data.setdefault("prefix_lists", None) if prefix_name: rmap_data.append("match ip address prefix-list" " {}".format(prefix_name)) if ipv6_data: prefix_name = ipv6_data.setdefault("prefix_lists", None) if prefix_name: rmap_data.append("match ipv6 address " "prefix-list {}". format(prefix_name)) if tag: rmap_data.append("match tag {}".format(tag)) if community: if "id" not in community: logger.error("'id' is mandatory for " "community-list in match" " criteria") return False cmd = "match community {}".format(community["id"]) exact_match = community.setdefault("exact_match", False) if exact_match: cmd = "{} exact-match".format(cmd) rmap_data.append(cmd) if large_community: if "id" not in large_community: logger.error("'num' is mandatory for " "large-community-list in match " "criteria") return False cmd = "match large-community {}".format( large_community["id"]) exact_match = large_community.setdefault( "exact_match", False) if exact_match: cmd = "{} exact-match".format(cmd) rmap_data.append(cmd) result = create_common_configuration(tgen, router, rmap_data, "route_maps", build=build) except InvalidCLIError: # Traceback errormsg = traceback.format_exc() logger.error(errormsg) return errormsg logger.debug("Exiting lib API: create_prefix_lists()") return result
def __create_ospf_global(tgen, input_dict, router, build=False, load_config=True): """ Helper API to create ospf global configuration. Parameters ---------- * `tgen` : Topogen object * `input_dict` : Input dict data, required when configuring from testcase * `router` : router to be configured. * `build` : Only for initial setup phase this is set as True. * `load_config` : Loading the config to router this is set as True. Returns ------- True or False """ result = False logger.debug("Entering lib API: __create_ospf_global()") try: ospf_data = input_dict[router]["ospf"] del_ospf_action = ospf_data.setdefault("delete", False) if del_ospf_action: config_data = ["no router ospf"] result = create_common_configuration(tgen, router, config_data, "ospf", build, load_config) return result config_data = [] cmd = "router ospf" config_data.append(cmd) # router id router_id = ospf_data.setdefault("router_id", None) del_router_id = ospf_data.setdefault("del_router_id", False) if del_router_id: config_data.append("no ospf router-id") if router_id: config_data.append("ospf router-id {}".format(router_id)) # redistribute command redistribute_data = ospf_data.setdefault("redistribute", {}) if redistribute_data: for redistribute in redistribute_data: if "redist_type" not in redistribute: logger.debug( "Router %s: 'redist_type' not present in " "input_dict", router) else: cmd = "redistribute {}".format(redistribute["redist_type"]) for red_type in redistribute_data: if "route_map" in red_type: cmd = cmd + " route-map {}".format( red_type["route_map"]) del_action = redistribute.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) # area information area_data = ospf_data.setdefault("area", {}) if area_data: for area in area_data: if "id" not in area: logger.debug( "Router %s: 'area id' not present in " "input_dict", router) else: cmd = "area {}".format(area["id"]) if "type" in area: cmd = cmd + " {}".format(area["type"]) del_action = area.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) # summary information summary_data = ospf_data.setdefault("summary-address", {}) if summary_data: for summary in summary_data: if "prefix" not in summary: logger.debug( "Router %s: 'summary-address' not present in " "input_dict", router, ) else: cmd = "summary {}/{}".format(summary["prefix"], summary["mask"]) _tag = summary.setdefault("tag", None) if _tag: cmd = "{} tag {}".format(cmd, _tag) _advertise = summary.setdefault("advertise", True) if not _advertise: cmd = "{} no-advertise".format(cmd) del_action = summary.setdefault("delete", False) if del_action: cmd = "no {}".format(cmd) config_data.append(cmd) result = create_common_configuration(tgen, router, config_data, "ospf", build, load_config) except InvalidCLIError: # Traceback errormsg = traceback.format_exc() logger.error(errormsg) return errormsg logger.debug("Exiting lib API: create_ospf_global()") return result
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(defaults=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: p = os.popen("exabgp -v") line = p.readlines() version = line[0].split() if topotest.version_cmp(version[2], "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 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') hasfrr = False if not os.path.isdir(frrdir): logger.error('could not find {} directory'.format(frrdir)) ret = False else: hasfrr = True 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']: 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) ) # Assert that Quagga utilities exist quaggadir = config.get('topogen', 'quaggadir') if hasfrr: # if we have frr, don't check for quagga pass elif not os.path.isdir(quaggadir): logger.info('could not find {} directory (quagga tests will not run)'.format(quaggadir)) else: ret = True try: pwd.getpwnam('quagga')[2] except KeyError: logger.info('could not find "quagga" user') try: grp.getgrnam('quagga')[2] except KeyError: logger.info('could not find "quagga" group') try: if 'quagga' not in grp.getgrnam('quaggavty').gr_mem: logger.error('"quagga" user and group exist, but user is not under "quaggavty"') except KeyError: logger.warning('could not find "quaggavty" group') for fname in ['zebra', 'ospfd', 'ospf6d', 'bgpd', 'ripd', 'ripngd', 'isisd', 'pimd']: path = os.path.join(quaggadir, fname) if not os.path.isfile(path): logger.warning('could not find {} in {}'.format(fname, quaggadir)) ret = False else: if fname != 'zebra': continue os.system( '{} -v 2>&1 >/tmp/topotests/quagga_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