198 lines
5.4 KiB
Python
Executable File
198 lines
5.4 KiB
Python
Executable File
#!/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]} <toponame>')
|
|
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)
|