def run(self, dckr_net_name='', rm=True): if rm and ctn_exists(self.name): print('remove container:', self.name) dckr.remove_container(self.name, force=True) host_config = dckr.create_host_config(binds=[ '{0}:{1}'.format(os.path.abspath(self.host_dir), self.guest_dir) ], privileged=True, network_mode='bridge', cap_add=['NET_ADMIN']) ctn = dckr.create_container(image=self.image, entrypoint='bash', detach=True, name=self.name, stdin_open=True, volumes=[self.guest_dir], host_config=host_config) self.ctn_id = ctn['Id'] ipv4_addresses = self.get_ipv4_addresses() net_id = None for network in dckr.networks(names=[dckr_net_name]): if network['Name'] != dckr_net_name: continue net_id = network['Id'] if not 'IPAM' in network: print('can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM'.format( dckr_net_name)) break ipam = network['IPAM'] if not 'Config' in ipam: print('can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM.Config'. format(dckr_net_name)) break ip_ok = False network_subnets = [ item['Subnet'] for item in ipam['Config'] if 'Subnet' in item ] for ip in ipv4_addresses: for subnet in network_subnets: ip_ok = netaddr.IPAddress(ip) in netaddr.IPNetwork(subnet) if not ip_ok: print( 'the container\'s IP address {} is not valid for Docker network {} ' 'since it\'s not part of any of its subnets ({})'. format(ip, dckr_net_name, ', '.join(network_subnets))) print( 'Please consider removing the Docket network {net} ' 'to allow bgperf to create it again using the ' 'expected subnet:\n' ' docker network rm {net}'.format(net=dckr_net_name)) sys.exit(1) break if net_id is None: print('Docker network "{}" not found!'.format(dckr_net_name)) return dckr.connect_container_to_network(self.ctn_id, net_id, ipv4_address=ipv4_addresses[0]) dckr.start(container=self.name) if len(ipv4_addresses) > 1: # get the interface used by the first IP address already added by Docker dev = None res = self.local('ip addr') for line in res.split(b'\n'): if ipv4_addresses[0].encode('utf-8') in line: dev = line.split(b' ')[-1].strip() if not dev: dev = "eth0" for ip in ipv4_addresses[1:]: self.local('ip addr add {} dev {}'.format(ip, dev)) return ctn
def run_relay(name, ip, listen, talk, dckr_net_name='', rm=True): if rm and ctn_exists(name): print 'remove relay containers:', name dckr.remove_container(name, force=True) ctn = dckr.create_container( image='sproxy', detach=True, name=name, environment=['LISTEN={0}'.format(listen), 'TALK={0}'.format(talk)]) ctn_id = ctn['Id'] ipv4_addresses = [ip] net_id = None for network in dckr.networks(names=[dckr_net_name]): if network['Name'] != dckr_net_name: continue net_id = network['Id'] if not 'IPAM' in network: print( 'can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM'.format( dckr_net_name)) break ipam = network['IPAM'] if not 'Config' in ipam: print( 'can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM.Config'. format(dckr_net_name)) break ip_ok = False network_subnets = [ item['Subnet'] for item in ipam['Config'] if 'Subnet' in item ] for ip in ipv4_addresses: for subnet in network_subnets: ip_ok = netaddr.IPAddress(ip) in netaddr.IPNetwork(subnet) if not ip_ok: print( 'the container\'s IP address {} is not valid for Docker network {} ' 'since it\'s not part of any of its subnets ({})'. format(ip, dckr_net_name, ', '.join(network_subnets))) print( 'Please consider removing the Docket network {net} ' 'to allow bgperf to create it again using the ' 'expected subnet:\n' ' docker network rm {net}'.format(net=dckr_net_name)) sys.exit(1) break if net_id is None: print 'Docker network "{}" not found!'.format(dckr_net_name) return dckr.connect_container_to_network(ctn_id, net_id, ipv4_address=ipv4_addresses[0]) dckr.start(container=name) return ctn
def bench(args): config_dir = '{0}/{1}'.format(args.dir, args.bench_name) dckr_net_name = args.docker_network_name or args.bench_name + '-br' for target_class in [BIRDTarget, GoBGPTarget, QuaggaTarget]: if ctn_exists(target_class.CONTAINER_NAME): print 'removing target container', target_class.CONTAINER_NAME dckr.remove_container(target_class.CONTAINER_NAME, force=True) if not args.repeat: if ctn_exists(Monitor.CONTAINER_NAME): print 'removing monitor container', Monitor.CONTAINER_NAME dckr.remove_container(Monitor.CONTAINER_NAME, force=True) for ctn_name in get_ctn_names(): if ctn_name.startswith(ExaBGPTester.CONTAINER_NAME_PREFIX) or \ ctn_name.startswith(ExaBGPMrtTester.CONTAINER_NAME_PREFIX) or \ ctn_name.startswith(GoBGPMRTTester.CONTAINER_NAME_PREFIX): print 'removing tester container', ctn_name dckr.remove_container(ctn_name, force=True) if os.path.exists(config_dir): shutil.rmtree(config_dir) if args.file: with open(args.file) as f: conf = yaml.load(Template(f.read()).render()) else: conf = gen_conf(args) if not os.path.exists(config_dir): os.makedirs(config_dir) with open('{0}/scenario.yaml'.format(config_dir), 'w') as f: f.write(conf) conf = yaml.load(Template(conf).render()) bridge_found = False for network in dckr.networks(names=[dckr_net_name]): if network['Name'] == dckr_net_name: print 'Docker network "{}" already exists'.format(dckr_net_name) bridge_found = True break if not bridge_found: subnet = conf['local_prefix'] print 'creating Docker network "{}" with subnet {}'.format( dckr_net_name, subnet) ipam = IPAMConfig(pool_configs=[IPAMPool(subnet=subnet)]) network = dckr.create_network(dckr_net_name, driver='bridge', ipam=ipam) num_tester = sum( len(t.get('neighbors', [])) for t in conf.get('testers', [])) if num_tester > gc_thresh3(): print 'gc_thresh3({0}) is lower than the number of peer({1})'.format( gc_thresh3(), num_tester) print 'type next to increase the value' print '$ echo 16384 | sudo tee /proc/sys/net/ipv4/neigh/default/gc_thresh3' print 'run monitor' m = Monitor(config_dir + '/monitor', conf['monitor']) m.run(conf, dckr_net_name) is_remote = True if 'remote' in conf['target'] and conf['target'][ 'remote'] else False if is_remote: print 'target is remote ({})'.format(conf['target']['local-address']) ip = IPRoute() # r: route to the target r = ip.get_routes(dst=conf['target']['local-address'], family=AF_INET) if len(r) == 0: print 'no route to remote target {0}'.format( conf['target']['local-address']) sys.exit(1) # intf: interface used to reach the target idx = [t[1] for t in r[0]['attrs'] if t[0] == 'RTA_OIF'][0] intf = ip.get_links(idx)[0] intf_name = intf.get_attr('IFLA_IFNAME') # raw_bridge_name: Linux bridge name of the Docker bridge # TODO: not sure if the linux bridge name is always given by # "br-<first 12 characters of Docker network ID>". raw_bridge_name = args.bridge_name or 'br-{}'.format( network['Id'][0:12]) # raw_bridges: list of Linux bridges that match raw_bridge_name raw_bridges = ip.link_lookup(ifname=raw_bridge_name) if len(raw_bridges) == 0: if not args.bridge_name: print( 'can\'t determine the Linux bridge interface name starting ' 'from the Docker network {}'.format(dckr_net_name)) else: print('the Linux bridge name provided ({}) seems nonexistent'. format(raw_bridge_name)) print( 'Since the target is remote, the host interface used to ' 'reach the target ({}) must be part of the Linux bridge ' 'used by the Docker network {}, but without the correct Linux ' 'bridge name it\'s impossible to verify if that\'s true'. format(intf_name, dckr_net_name)) if not args.bridge_name: print( 'Please supply the Linux bridge name corresponding to the ' 'Docker network {} using the --bridge-name argument.'. format(dckr_net_name)) sys.exit(1) # intf_bridge: bridge interface that intf is already member of intf_bridge = intf.get_attr('IFLA_MASTER') # if intf is not member of the bridge, add it if intf_bridge not in raw_bridges: if intf_bridge is None: print( 'Since the target is remote, the host interface used to ' 'reach the target ({}) must be part of the Linux bridge ' 'used by the Docker network {}'.format( intf_name, dckr_net_name)) sys.stdout.write('Do you confirm to add the interface {} ' 'to the bridge {}? [yes/NO] '.format( intf_name, raw_bridge_name)) try: answer = raw_input() except: print 'aborting' sys.exit(1) answer = answer.strip() if answer.lower() != 'yes': print 'aborting' sys.exit(1) print 'adding interface {} to the bridge {}'.format( intf_name, raw_bridge_name) br = raw_bridges[0] try: ip.link('set', index=idx, master=br) except Exception as e: print('Something went wrong: {}'.format(str(e))) print( 'Please consider running the following command to ' 'add the {iface} interface to the {br} bridge:\n' ' sudo brctl addif {br} {iface}'.format( iface=intf_name, br=raw_bridge_name)) print('\n\n\n') raise else: curr_bridge_name = ip.get_links(intf_bridge)[0].get_attr( 'IFLA_IFNAME') print( 'the interface used to reach the target ({}) ' 'is already member of the bridge {}, which is not ' 'the one used in this configuration'.format( intf_name, curr_bridge_name)) print( 'Please consider running the following command to ' 'remove the {iface} interface from the {br} bridge:\n' ' sudo brctl addif {br} {iface}'.format( iface=intf_name, br=curr_bridge_name)) sys.exit(1) else: if args.target == 'gobgp': target_class = GoBGPTarget elif args.target == 'bird': target_class = BIRDTarget elif args.target == 'quagga': target_class = QuaggaTarget print 'run', args.target if args.image: target = target_class('{0}/{1}'.format(config_dir, args.target), conf['target'], image=args.image) else: target = target_class('{0}/{1}'.format(config_dir, args.target), conf['target']) target.run(conf, dckr_net_name) time.sleep(1) print 'waiting bgp connection between {0} and monitor'.format(args.target) m.wait_established(conf['target']['local-address']) if not args.repeat: for idx, tester in enumerate(conf['testers']): if 'name' not in tester: name = 'tester{0}'.format(idx) else: name = tester['name'] if 'type' not in tester: tester_type = 'normal' else: tester_type = tester['type'] if tester_type == 'normal': tester_class = ExaBGPTester elif tester_type == 'mrt': if 'mrt_injector' not in tester: mrt_injector = 'gobgp' else: mrt_injector = tester['mrt_injector'] if mrt_injector == 'gobgp': tester_class = GoBGPMRTTester elif mrt_injector == 'exabgp': tester_class = ExaBGPMrtTester else: print 'invalid mrt_injector:', mrt_injector sys.exit(1) else: print 'invalid tester type:', tester_type sys.exit(1) t = tester_class(name, config_dir + '/' + name, tester) print 'run tester', name, 'type', tester_type t.run(conf['target'], dckr_net_name) start = datetime.datetime.now() q = Queue() m.stats(q) if not is_remote: target.stats(q) def mem_human(v): if v > 1000 * 1000 * 1000: return '{0:.2f}GB'.format(float(v) / (1000 * 1000 * 1000)) elif v > 1000 * 1000: return '{0:.2f}MB'.format(float(v) / (1000 * 1000)) elif v > 1000: return '{0:.2f}KB'.format(float(v) / 1000) else: return '{0:.2f}B'.format(float(v)) f = open(args.output, 'w') if args.output else None cpu = 0 mem = 0 cooling = -1 while True: info = q.get() if not is_remote and info['who'] == target.name: cpu = info['cpu'] mem = info['mem'] if info['who'] == m.name: now = datetime.datetime.now() elapsed = now - start recved = info['state']['adj-table'][ 'accepted'] if 'accepted' in info['state']['adj-table'] else 0 if elapsed.seconds > 0: rm_line() print 'elapsed: {0}sec, cpu: {1:>4.2f}%, mem: {2}, recved: {3}'.format( elapsed.seconds, cpu, mem_human(mem), recved) f.write('{0}, {1}, {2}, {3}\n'.format(elapsed.seconds, cpu, mem, recved)) if f else None f.flush() if f else None if cooling == args.cooling: f.close() if f else None return if cooling >= 0: cooling += 1 if info['checked']: cooling = 0
def two_peer_test(args): args.neighbor_num = 2 RELAY_PREFIX = 'bgperf_relay_' config_dir = '{0}/{1}'.format(args.dir, args.bench_name) dckr_net_name = args.docker_network_name or args.bench_name + '-br' for target_class in [ BIRDTarget, GoBGPTarget, QuaggaTarget, FRRoutingTarget, MIRAGETarget, MIRAGESTTarget ]: if ctn_exists(target_class.CONTAINER_NAME): print 'removing target container', target_class.CONTAINER_NAME dckr.remove_container(target_class.CONTAINER_NAME, force=True) if not args.repeat: if ctn_exists(Monitor.CONTAINER_NAME): print 'removing monitor container', Monitor.CONTAINER_NAME dckr.remove_container(Monitor.CONTAINER_NAME, force=True) for ctn_name in get_ctn_names(): if ctn_name.startswith("bgperf_"): print 'removing container', ctn_name dckr.remove_container(ctn_name, force=True) for ctn_name in get_ctn_names(): if ctn_name.startswith(RELAY_PREFIX): print 'removing relay container', ctn_name dckr.remove_container(ctn_name, force=True) for ctn_name in get_ctn_names(): if ctn_name.startswith("bgperf_throughput_tester"): print 'removing throughput tester container', ctn_name dckr.remove_container(ctn_name, force=True) if os.path.exists(config_dir): shutil.rmtree(config_dir) if args.file: with open(args.file) as f: conf = yaml.load(Template(f.read()).render()) else: conf = gen_conf(args) if not os.path.exists(config_dir): os.makedirs(config_dir) with open('{0}/scenario.yaml'.format(config_dir), 'w') as f: f.write(conf) conf = yaml.load(Template(conf).render()) bridge_found = False for network in dckr.networks(names=[dckr_net_name]): if network['Name'] == dckr_net_name: print 'Docker network "{}" already exists'.format(dckr_net_name) bridge_found = True break if not bridge_found: print "subnet does not exist" exit(1) if args.target == 'gobgp': target_class = GoBGPTarget elif args.target == 'bird': target_class = BIRDTarget elif args.target == 'quagga': target_class = QuaggaTarget elif args.target == 'frr': target_class = FRRoutingTarget elif args.target == 'mirage': target_class = MIRAGETarget elif args.target == 'mirage_st': target_class = MIRAGESTTarget print 'run', args.target target = target_class('{0}/{1}'.format(config_dir, args.target), conf['target']) target.run(conf, dckr_net_name) time.sleep(3) print 'run relays' def run_relay(name, ip, listen, talk, dckr_net_name='', rm=True): if rm and ctn_exists(name): print 'remove relay containers:', name dckr.remove_container(name, force=True) ctn = dckr.create_container( image='sproxy', detach=True, name=name, environment=['LISTEN={0}'.format(listen), 'TALK={0}'.format(talk)]) ctn_id = ctn['Id'] ipv4_addresses = [ip] net_id = None for network in dckr.networks(names=[dckr_net_name]): if network['Name'] != dckr_net_name: continue net_id = network['Id'] if not 'IPAM' in network: print( 'can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM'.format( dckr_net_name)) break ipam = network['IPAM'] if not 'Config' in ipam: print( 'can\'t verify if container\'s IP addresses ' 'are valid for Docker network {}: missing IPAM.Config'. format(dckr_net_name)) break ip_ok = False network_subnets = [ item['Subnet'] for item in ipam['Config'] if 'Subnet' in item ] for ip in ipv4_addresses: for subnet in network_subnets: ip_ok = netaddr.IPAddress(ip) in netaddr.IPNetwork(subnet) if not ip_ok: print( 'the container\'s IP address {} is not valid for Docker network {} ' 'since it\'s not part of any of its subnets ({})'. format(ip, dckr_net_name, ', '.join(network_subnets))) print( 'Please consider removing the Docket network {net} ' 'to allow bgperf to create it again using the ' 'expected subnet:\n' ' docker network rm {net}'.format(net=dckr_net_name)) sys.exit(1) break if net_id is None: print 'Docker network "{}" not found!'.format(dckr_net_name) return dckr.connect_container_to_network(ctn_id, net_id, ipv4_address=ipv4_addresses[0]) dckr.start(container=name) return ctn run_relay(RELAY_PREFIX + '1', '10.10.0.3', ':50001', '{}:179'.format(conf['target']['local-address']), 'bgperf-br') run_relay(RELAY_PREFIX + '2', '10.10.0.4', ':50002', '{}:179'.format(conf['target']['local-address']), 'bgperf-br') q = Queue() target.stats(q) print 'run throughput tester' throughput = ThroughputTarget('{0}/{1}'.format(config_dir, 'throughput'), {}) throughput.run(conf, dckr_net_name) def mem_human(v): if v > 1000 * 1000 * 1000: return '{0:.2f}GB'.format(float(v) / (1000 * 1000 * 1000)) elif v > 1000 * 1000: return '{0:.2f}MB'.format(float(v) / (1000 * 1000)) elif v > 1000: return '{0:.2f}KB'.format(float(v) / 1000) else: return '{0:.2f}B'.format(float(v)) cpu = 0.0 max_mem = 0.0 mem = 0.0 prev_cpu = 0.0 while True: if q.empty() and prev_cpu < 0.01: break info = q.get() prev_cpu = info['cpu'] cpu += info['cpu'] mem += info['mem'] max_mem = max(info['mem'], max_mem) if args.verbose: print 'cpu: {0:>4.2f}%, mem: {1}'.format(info['cpu'], mem_human(info['mem'])) print 'total CPU: {0:>4.2f}, max MEM: {1}, total mem {2}'.format( cpu, mem_human(max_mem), mem_human(mem)) return