def stop_batmanadv_instances_all(remotes): rmap = get_remote_mapping(remotes) for id, remote in rmap.items(): exec( remote, f'ip netns exec "ns-{id}" batctl meshif "bat0" interface del "uplink" || true' )
def main(): parser = argparse.ArgumentParser(description='Measure mean traffic.') parser.add_argument( '--remotes', help='Distribute nodes and links on remotes described in the JSON file.' ) parser.add_argument('--interface', help='Interface to measure traffic on.') parser.add_argument('--duration', type=int, help='Measurement duration in seconds.') args = parser.parse_args() if args.remotes: if not os.path.isfile(args.remotes): eprint(f'File not found: {args.remotes}') stop_all_terminals() exit(1) with open(args.remotes) as file: args.remotes = [Remote.from_json(obj) for obj in json.load(file)] else: args.remotes = default_remotes # need root for local setup for remote in args.remotes: if remote.address is None: if os.geteuid() != 0: eprint('Need to run as root.') exit(1) rmap = get_remote_mapping(args.remotes) if args.duration: ds = args.duration ts_beg = traffic(args.remotes, interface=args.interface, rmap=rmap) time.sleep(ds) ts_end = traffic(args.remotes, interface=args.interface, rmap=rmap) ts = ts_end - ts_beg n = ds * len(rmap) print( f'rx: {format_size(ts.rx_bytes / n)}/s, {ts.rx_packets / n:.2f} packets/s, {ts.rx_dropped / n:.2f} dropped/s (avg. per node)' ) print( f'tx: {format_size(ts.tx_bytes / n)}/s, {ts.tx_packets / n:.2f} packets/s, {ts.tx_dropped / n:.2f} dropped/s (avg. per node)' ) else: ts = traffic(args.remotes, interface=args.interface, rmap=rmap) print( f'rx: {format_size(ts.rx_bytes)} / {ts.rx_packets} packets / {ts.rx_dropped} dropped' ) print( f'tx: {format_size(ts.tx_bytes)} / {ts.tx_packets} packets / {ts.tx_dropped} dropped' ) stop_all_terminals()
def traffic(remotes=default_remotes, ids=None, interface=None, rmap=None): if rmap is None: rmap = get_remote_mapping(remotes) if ids is None: ids = list(rmap.keys()) if interface is None: interface = 'uplink' ts = _Traffic() for id in ids: remote = rmap[id] stdout = exec( remote, f'ip netns exec ns-{id} ip -statistics link show dev {interface}', get_output=True)[0] lines = stdout.split('\n') link_toks = lines[1].split() rx_toks = lines[3].split() tx_toks = lines[5].split() ts.rx_bytes += int(rx_toks[0]) ts.rx_packets += int(rx_toks[1]) ts.rx_errors += int(rx_toks[2]) ts.rx_dropped += int(rx_toks[3]) ts.rx_overrun += int(rx_toks[4]) ts.rx_mcast += int(rx_toks[5]) ts.tx_bytes += int(tx_toks[0]) ts.tx_packets += int(tx_toks[1]) ts.tx_errors += int(tx_toks[2]) ts.tx_dropped += int(tx_toks[3]) ts.tx_carrier += int(tx_toks[4]) ts.tx_collsns += int(tx_toks[5]) return ts
def main(): parser = argparse.ArgumentParser(description="Ping various nodes.") parser.add_argument( "--remotes", help= "Distribute nodes and links on remotes described in the JSON file.", ) parser.add_argument("--input", help="JSON state of the network.") parser.add_argument("--interface", help="Interface to send data over (autodetected).") parser.add_argument("--min-hops", type=int, help="Minimum hops to ping. Needs --input.") parser.add_argument("--max-hops", type=int, help="Maximum hops to ping. Needs --input.") parser.add_argument( "--pings", type=int, default=10, help="Number of pings (unique, no self, no reverse paths).", ) parser.add_argument("--duration", type=int, default=1000, help="Spread pings over duration in ms.") parser.add_argument( "--deadline", type=int, default=1, help= "Specify a timeout, in seconds, before ping exits regardless of how many packets have been sent or received. In this case ping does not stop after count packet are sent, it waits either for deadline expire or until count probes are answered or for some error notification from network.", ) parser.add_argument( "--timeout", type=int, default=None, help= "Time to wait for a response, in seconds. The option affects only timeout in absence of any responses, otherwise ping waits for two RTTs.", ) parser.add_argument("--path", nargs=2, help="Send pings from a node to another.") parser.add_argument("-4", action="store_true", help="Force use of IPv4 addresses.") parser.add_argument("-6", action="store_true", help="Force use of IPv6 addresses.") args = parser.parse_args() if args.remotes: if not os.path.isfile(args.remotes): eprint(f"File not found: {args.remotes}") stop_all_terminals() exit(1) with open(args.remotes) as file: args.remotes = [Remote.from_json(obj) for obj in json.load(file)] else: args.remotes = default_remotes # need root for local setup for remote in args.remotes: if remote.address is None: if os.geteuid() != 0: eprint("Need to run as root.") exit(1) paths = None if args.path: for ns in args.path: if not namespace_exists(args.remotes, ns): eprint(f"Namespace ns-{ns} does not exist") stop_all_terminals() exit(1) paths = [args.path] elif args.input: state = json.load(args.input) paths = get_random_paths(network=state, count=args.pings) paths = filter_paths(state, paths, min_hops=args.min_hops, max_hops=args.max_hops) else: if args.min_hops is not None or args.max_hops is not None: eprint( "No min/max hops available without topology information (--input)" ) stop_all_terminals() exit(1) rmap = get_remote_mapping(args.remotes) all = list(rmap.keys()) paths = _get_random_paths(nodes=all, count=args.pings) address_type = None if getattr(args, "4"): address_type = "4" if getattr(args, "6"): address_type = "6" ping( paths=paths, remotes=args.remotes, duration_ms=args.duration, interface=args.interface, verbosity="verbose", address_type=address_type, ping_deadline=args.deadline, ping_timeout=args.timeout, ) stop_all_terminals()
def ping( paths, duration_ms=1000, remotes=default_remotes, interface=None, verbosity="normal", address_type=None, ping_deadline=1, ping_timeout=None, ): ping_count = 1 rmap = get_remote_mapping(remotes) path_count = len(paths) # prepare ping tasks tasks = [] for (source, target) in paths: source_remote = rmap[source] target_remote = rmap[target] if interface is None: interface = _get_interface(source_remote, source) target_addr = _get_ip_address(target_remote, target, interface, address_type) if target_addr is None: eprint(f"Cannot get address of {interface} in ns-{target}") else: debug = f"ping {source:>4} => {target:>4} ({target_addr:<18} / {interface})" command = ( f"ip netns exec ns-{source} ping -c {ping_count} " + (f"-w {ping_deadline} " if ping_deadline is not None else "") + (f"-W {ping_timeout} " if ping_timeout is not None else "") + f"-D -I {interface} {target_addr}") tasks.append((source_remote, command, debug)) processes = [] started = 0 def process_results(): for (process, started_ms, debug, result) in processes: if not result.processed and process.poll() is not None: process.wait() (output, err) = process.communicate() _parse_ping(result, output.decode()) result.processed = True # keep track of status ouput lines to delete them for updates lines_printed = 0 def print_processes(): nonlocal lines_printed # delete previous printed lines for _ in range(lines_printed): sys.stdout.write("\x1b[1A\x1b[2K") lines_printed = 0 process_counter = 0 for (process, started_ms, debug, result) in processes: process_counter += 1 status = "???" if result.processed: if result.packet_loss == 0.0: status = "success" elif result.packet_loss == 100.0: status = "failed" else: status = f"mixed ({result.packet_loss:0.2f}% loss)" else: status = "running" print( f"[{process_counter:03}:{started_ms:06}] {debug} => {status}") lines_printed += 1 # start tasks in the given time frame start_ms = millis() last_processed = millis() tasks_count = len(tasks) while started < tasks_count: started_expected = math.ceil(tasks_count * ((millis() - start_ms) / duration_ms)) if started_expected > started: for _ in range(0, started_expected - started): if len(tasks) == 0: break (remote, command, debug) = tasks.pop() process = create_process(remote, command) started_ms = millis() - start_ms processes.append( (process, started_ms, debug, _PingResult(ping_count))) # process results and print updates once per second if (last_processed + 1000) < millis(): last_processed = millis() process_results() if verbosity != "quiet": print_processes() started += 1 else: # sleep a small amount time.sleep(duration_ms / tasks_count / 1000.0 / 10.0) stop1_ms = millis() # wait until rest fraction of duration_ms is over if (stop1_ms - start_ms) < duration_ms: time.sleep((duration_ms - (stop1_ms - start_ms)) / 1000.0) stop2_ms = millis() process_results() if verbosity != "quiet": print_processes() # collect results rtt_avg_ms_count = 0 ret = _PingStats() for (process, started_ms, debug, result) in processes: ret.send += result.send if result.processed: ret.received += int(result.send * (1.0 - (result.packet_loss / 100.0))) # failing ping outputs do not have rtt values if not math.isnan(result.rtt_avg): ret.rtt_avg_ms += result.rtt_avg rtt_avg_ms_count += 1 if rtt_avg_ms_count > 0: ret.rtt_avg_ms /= float(rtt_avg_ms_count) result_duration_ms = stop1_ms - start_ms result_filler_ms = stop2_ms - stop1_ms if verbosity != "quiet": print( "pings send: {}, received: {} ({}), measurement span: {}ms".format( ret.send, ret.received, "-" if (ret.send == 0) else f"{100.0 * (ret.received / ret.send):0.2f}%", result_duration_ms + result_filler_ms, )) return ret
def stop(protocol, remotes=default_remotes): rmap = get_remote_mapping(remotes) ids = list(rmap.keys()) stop_routing_protocol(protocol, rmap, ids)
def main(): parser = argparse.ArgumentParser() parser.add_argument( '--remotes', help='Distribute nodes and links on remotes described in the JSON file.' ) subparsers = parser.add_subparsers(dest='action', required=True) parser_traffic = subparsers.add_parser('traffic', help='Measure mean traffic.') parser_traffic.add_argument('--interface', help='Interface to measure traffic on.') parser_traffic.add_argument('--duration', type=int, help='Measurement duration in seconds.') parser_ping = subparsers.add_parser('ping', help='Ping various nodes.') parser_ping.add_argument('--input', help='JSON state of the network.') parser_ping.add_argument( '--interface', help='Interface to send data over (autodetected).') parser_ping.add_argument('--min-hops', type=int, help='Minimum hops to ping. Needs --input.') parser_ping.add_argument('--max-hops', type=int, help='Maximum hops to ping. Needs --input.') parser_ping.add_argument( '--pings', type=int, default=10, help='Number of pings (unique, no self, no reverse paths).') parser_ping.add_argument('--duration', type=int, default=1000, help='Spread pings over duration in ms.') args = parser.parse_args() if args.remotes: with open(args.remotes) as file: args.remotes = json.load(file) else: args.remotes = default_remotes # need root for local setup for remote in args.remotes: if remote.get('address') is None: if os.geteuid() != 0: eprint('Need to run as root.') exit(1) if args.action == 'traffic': rmap = get_remote_mapping(args.remotes) if args.duration: ds = args.duration ts_beg = traffic(args.remotes, interface=args.interface, rmap=rmap) time.sleep(ds) ts_end = traffic(args.remotes, interface=args.interface, rmap=rmap) ts = ts_end - ts_beg n = ds * len(rmap) print( f'rx: {format_size(ts.rx_bytes / n)}/s, {ts.rx_packets / n:.2f} packets/s, {ts.rx_dropped / n:.2f} dropped/s (avg. per node)' ) print( f'tx: {format_size(ts.tx_bytes / n)}/s, {ts.tx_packets / n:.2f} packets/s, {ts.tx_dropped / n:.2f} dropped/s (avg. per node)' ) else: ts = traffic(args.remotes, interface=args.interface, rmap=rmap) print( f'rx: {format_size(ts.rx_bytes)} / {ts.rx_packets} packets / {ts.rx_dropped} dropped' ) print( f'tx: {format_size(ts.tx_bytes)} / {ts.tx_packets} packets / {ts.tx_dropped} dropped' ) elif args.action == 'ping': paths = None if args.input: state = json.load(args.input) paths = get_random_paths(network=state, count=args.pings) paths = filter_paths(state, paths, min_hops=args.min_hops, max_hops=args.max_hops) else: if args.min_hops is not None or args.max_hops is not None: eprint( 'No min/max hops available without topology information (--input)' ) stop_all_terminals() exit(1) rmap = get_remote_mapping(args.remotes) all = list(rmap.keys()) paths = _get_random_paths(nodes=all, count=args.pings) ping_paths(paths=paths, remotes=args.remotes, duration_ms=args.duration, interface=args.interface, verbosity='verbose') else: eprint(f'Unknown action: {args.action}') exit(1) stop_all_terminals()
def ping_paths(paths, duration_ms=1000, remotes=default_remotes, interface=None, verbosity='normal'): ping_deadline = 1 ping_count = 1 processes = [] start_ms = millis() started = 0 rmap = get_remote_mapping(remotes) path_count = len(paths) while started < path_count: # number of expected tests to have been run started_expected = math.ceil(path_count * ((millis() - start_ms) / duration_ms)) if started_expected > started: for _ in range(0, started_expected - started): if len(paths) == 0: break (source, target) = paths.pop() source_remote = rmap[source] target_remote = rmap[target] if interface is None: interface = _get_interface(source_remote, source) target_addr = _get_ip_address(target_remote, target, interface) if target_addr is None: eprint(f'Cannot get address of {interface} in ns-{target}') # count as started started += 1 else: debug = '[{:06}] Ping {} => {} ({} / {})'.format( millis() - start_ms, source, target, target_addr, interface) process = create_process( source_remote, f'ip netns exec ns-{source} ping -c {ping_count} -w {ping_deadline} -D {target_addr}' ) processes.append((process, debug)) started += 1 else: # sleep a small amount time.sleep(duration_ms / path_count / 1000.0 / 10.0) stop1_ms = millis() # wait until duration_ms is over if (stop1_ms - start_ms) < duration_ms: time.sleep((duration_ms - (stop1_ms - start_ms)) / 1000.0) stop2_ms = millis() ret = _PingResult() # wait/collect for results from pings (prolongs testing up to 1 second!) for (process, debug) in processes: process.wait() (output, err) = process.communicate() result = _parse_ping(output.decode()) result.send = ping_count # TODO: nicer ret.send += result.send ret.transmitted += result.transmitted ret.received += result.received ret.rtt_avg += result.rtt_avg if verbosity != 'quiet': if result.send != result.received: print(f'{debug} => failed') else: # success print(f'{debug}') ret.rtt_avg = 0 if ret.received == 0 else int(ret.rtt_avg / ret.received) result_duration_ms = stop1_ms - start_ms result_filler_ms = stop2_ms - stop1_ms if verbosity != 'quiet': print('send: {}, received: {}, arrived: {}%, measurement span: {}ms'. format( ret.send, ret.received, '-' if (ret.send == 0) else '{:0.2f}'.format(100.0 * (ret.received / ret.send)), result_duration_ms + result_filler_ms)) return ret
def main(): parser = argparse.ArgumentParser(description='Ping various nodes.') parser.add_argument( '--remotes', help='Distribute nodes and links on remotes described in the JSON file.' ) parser.add_argument('--input', help='JSON state of the network.') parser.add_argument('--interface', help='Interface to send data over (autodetected).') parser.add_argument('--min-hops', type=int, help='Minimum hops to ping. Needs --input.') parser.add_argument('--max-hops', type=int, help='Maximum hops to ping. Needs --input.') parser.add_argument( '--pings', type=int, default=10, help='Number of pings (unique, no self, no reverse paths).') parser.add_argument('--duration', type=int, default=1000, help='Spread pings over duration in ms.') parser.add_argument('-4', action='store_true', help='Force use of IPv4 addresses.') parser.add_argument('-6', action='store_true', help='Force use of IPv6 addresses.') args = parser.parse_args() if args.remotes: if not os.path.isfile(args.remotes): eprint(f'File not found: {args.remotes}') stop_all_terminals() exit(1) with open(args.remotes) as file: args.remotes = [Remote.from_json(obj) for obj in json.load(file)] else: args.remotes = default_remotes # need root for local setup for remote in args.remotes: if remote.address is None: if os.geteuid() != 0: eprint('Need to run as root.') exit(1) paths = None if args.input: state = json.load(args.input) paths = get_random_paths(network=state, count=args.pings) paths = filter_paths(state, paths, min_hops=args.min_hops, max_hops=args.max_hops) else: if args.min_hops is not None or args.max_hops is not None: eprint( 'No min/max hops available without topology information (--input)' ) stop_all_terminals() exit(1) rmap = get_remote_mapping(args.remotes) all = list(rmap.keys()) paths = _get_random_paths(nodes=all, count=args.pings) address_type = None if getattr(args, '4'): address_type = '4' if getattr(args, '6'): address_type = '6' ping(paths=paths, remotes=args.remotes, duration_ms=args.duration, interface=args.interface, verbosity='verbose', address_type=address_type) stop_all_terminals()
def start(protocol, remotes=default_remotes): rmap = get_remote_mapping(remotes) ids = list(rmap.keys()) _start_protocol(protocol, rmap, ids)