#!/usr/bin/env python3 # -*- encoding: utf-8 -*- import sys import json import importlib from subprocess import run, PIPE from time import sleep from pathlib import Path def wait(): print('*** Waiting', file=sys.stderr) sleep(2) def speed_parser_iperf(iperfstdout): speed, unit = iperfstdout.splitlines()[-1].split('/')[0].split(' ')[-2:] speed = float(speed) multiplier = { 'bits': 10**0, 'kbits': 10**3, 'mbits': 10**6, 'gbits': 10**9, 'tbits': 10**12, }.get(unit.lower(), 1) return int(speed*multiplier) def make_host_pairs(hosts): s = len(hosts)//2 r = hosts[-s:] rs = len(r) l = hosts[:-s][:rs] return list(zip(l, list(reversed(r)))) def test_ping_sync(src, dst): print(f'*** Pinging {src.IP()} -> {dst.IP()}', file=sys.stderr) k = src.pexec('ping', dst.IP(), '-c', str(10)) _, avg, __, dev = ( list(map( float, k[0].splitlines()[-1].split('=')[1].strip().split(' ')[0].split('/') )) ) return dict(avg=avg, mdev=dev) def test_iperf_sync(src, dst): print(f'*** Iperfing {src.IP()} -> {dst.IP()}', file=sys.stderr) s = dst.popen('iperf', '-s') k = src.pexec('iperf', '-c', dst.IP()) s.kill() if k[0]=='': raise Exception(k[1]) return speed_parser_iperf(k[0]) def do_sequential_tests(pingpair, iperfpairs): print(f'*** Performing sequential tests', file=sys.stderr) iperfres = list() for iperfpair in iperfpairs: iperfres.append(test_iperf_sync(*iperfpair)) wait() pingres = test_ping_sync(*pingpair) wait() return dict(ping=pingres, iperfs=iperfres) class IperfWaiter: def __init__(self, *x): self._x = x def __call__(self): s, k, *_ = self._x k.wait() k = (k.stdout.read().decode(), ) s.kill() return speed_parser_iperf(k[0]) def test_iperf_async(src, dst): print(f'*** Iperfing {src.IP()} -> {dst.IP()}', file=sys.stderr) s = dst.popen('iperf', '-s') k = src.popen('iperf', '-c', dst.IP()) return IperfWaiter(s, k) class PingWaiter: def __init__(self, *x): self._x = x def __call__(self): k, *_ = self._x k.wait() k = (k.stdout.read().decode(), ) _, avg, __, dev = ( list(map( float, k[0].splitlines()[-1].split('=')[1].strip().split(' ')[0].split('/') )) ) return dict(avg=avg, mdev=dev) def test_ping_async(src, dst): print(f'*** Pinging {src.IP()} -> {dst.IP()}', file=sys.stderr) k = src.popen('ping', dst.IP(), '-c', str(10)) return PingWaiter(k) def do_parallel_tests(pingpair, iperfpairs): print(f'*** Performing parallel tests', file=sys.stderr) iperfres = list() for iperfpair in iperfpairs: iperfres.append(test_iperf_async(*iperfpair)) pingres = test_ping_async(*pingpair) pingres = pingres() iperfres = [r() for r in iperfres] # wait() return dict(ping=pingres, iperfs=iperfres) def do_tests(topo, hosts, net): print(f'*** Controller will bind its socket soon', file=sys.stderr) while len(run("netstat -lant | grep :6633", shell=True, stdout=PIPE).stdout) <= 2: wait() print(f'*** Controller will register its switches', file=sys.stderr) for _ in range(max(8, len(topo[1]))//8): wait() pairs = make_host_pairs(hosts) pingpair, *iperfpairs = pairs seqres = None seqres = do_sequential_tests(pingpair, iperfpairs) pllres = do_parallel_tests(pingpair, iperfpairs) return dict( sequential=seqres, parallel=pllres ) def main(topo, mod, resultspath, returns_result=False): print(f'*** Creating network', file=sys.stderr) net = mod.myNetwork() try: x = {h.name: h for h in net.hosts} hosts = [x[h] for h in topo[0]] res = do_tests(topo, hosts, net) pingpair, *iperfpairs = make_host_pairs(topo[0]) res = dict( pairs=dict( ping=pingpair, iperf=iperfpairs ), **res ) net.stop() if returns_result: return res else: resultspath.write_text(json.dumps( res, indent=4 )) except: print("*** Aborted application", file=sys.stderr) run(['mn', '-c'], stderr=PIPE, stdout=PIPE) if returns_result: return None raise if __name__ == "__main__": if len(sys.argv) < 2: print('Usage:') print(f' {sys.argv[0]} ') print() print(' Where toponame is will be resolved to') print(' toponame.json and toponame.state') else: modname = f'{sys.argv[1]}' commentedname = modname+( '.' if len(sys.argv) > 2 else '' )+'.'.join(sys.argv[2:]) modfile = Path(f'{modname}.py') topopath = Path(f'{modname}.json') resultspath = Path(f'{commentedname}.autotest.json') if not topopath.exists(): print(f'Topology {topopath}.json does not exist.') if not modfile.exists(): print(f'Topology {topopath}.py does not exist.') print(f'You might want to use toporender.py to generate required files.') else: topo = json.loads(topopath.read_text()) mod = importlib.import_module(modname) main(topo, mod, resultspath)