Created
January 2, 2018 17:43
-
-
Save bturrubiates/47ad688581ddef4c5801b8369025bed5 to your computer and use it in GitHub Desktop.
Script to run UDP/TCP testing with iperf in IPv4 and IPv6 mode.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import subprocess | |
import threading | |
import itertools | |
import argparse | |
import shlex | |
import time | |
import re | |
def find_neighbor(address, direction): | |
unmasked = address.split('/')[0] | |
delimiter = '.' if '.' in unmasked else ':' | |
value = int(unmasked.split(delimiter)[-1]) | |
index = unmasked.rfind(delimiter) | |
rest = unmasked[:index + 1] | |
value = value + 1 if direction == 'up' else value - 1 | |
return rest + str(value) | |
def find_address(iface, ipv): | |
ip_opt = '-6' if ipv == 'ipv6' else '-4' | |
cmd = 'ip -o {0} addr show dev {1} scope global'.format( | |
ip_opt, iface) | |
sh = shlex.split(cmd) | |
proc = subprocess.Popen(sh, stdout=subprocess.PIPE) | |
out, _ = proc.communicate() | |
address = re.search(r'.*inet(?:.?) (.*)/.*', out.decode()).groups(1)[0] | |
return address | |
def generate_log_name(address, port, proto, client=False): | |
safe_addr = address.replace('.', '_') | |
safe_addr = address.replace(':', '_') | |
composite = '{0}_{1}_{2}.log'.format(safe_addr, port, proto) | |
if client: | |
composite = 'client_' + composite | |
return composite | |
def find_offset(ipv, proto): | |
offsets = { | |
'ipv4': { | |
'udp': 100, | |
'tcp': 200 | |
}, | |
'ipv6': { | |
'udp': 300, | |
'tcp': 400 | |
} | |
} | |
return offsets[ipv][proto] | |
def start_server(args, iface, port, ipv, proto): | |
address = find_address(iface, ipv) | |
cmd = 'iperf -s -i {0}'.format(args.interval) | |
cmd += ' -B {0}'.format(address) | |
if ipv == 'ipv6': | |
cmd += ' -V' | |
if proto == 'udp': | |
cmd += ' -u' | |
port += find_offset(ipv, proto) | |
cmd += ' -p {0}'.format(port) | |
sh = shlex.split(cmd) | |
filename = generate_log_name(address, port, proto) | |
print('Running {0} and redirecting to {1}'.format(cmd, filename)) | |
with open(filename, 'w') as log: | |
proc = subprocess.Popen(sh, stdout=log, stderr=log) | |
proc.wait() | |
def start_client(args, iface, port, ipv, proto): | |
address = find_address(iface, ipv) | |
neighbor = find_neighbor(address, args.neighbor) | |
cmd = 'iperf -c {0} -t 86400 -P4 -i {1}'.format(neighbor, args.interval) | |
if ipv == 'ipv6': | |
cmd += ' -V' | |
if proto == 'udp': | |
cmd += ' -u -b 10G -l 32k' | |
else: | |
cmd += ' -l 128k' | |
port += find_offset(ipv, proto) | |
cmd += ' -p {0}'.format(port) | |
sh = shlex.split(cmd) | |
filename = generate_log_name(address, port, proto, client=True) | |
print('Running {0} and redirecting to {1}'.format(cmd, filename)) | |
with open(filename, 'w') as log: | |
proc = subprocess.Popen(sh, stdout=log, stderr=log) | |
proc.wait() | |
def interfaces(iface, count): | |
# Expect interfaces to follow the naming convention of | |
# eno5, this should break into ('eno', '5') | |
# enp138s0f0, this should break into ('enp', '138', 's0f0') | |
iface_re = r'([a-z]+)([0-9]+)' | |
prefix, number_str = re.search(iface_re, iface).groups() | |
number = int(number_str) | |
for i in range(count): | |
yield '{0}{1}'.format(prefix, number + i) | |
def dispatch_tests(target, args, interface, port): | |
threads = [] | |
# Temporarily disable ipv6 as there is some sort of issue | |
ipv = ['ipv4'] | |
# Temporarily disable udp as there is some sort of issue with TSO. | |
proto = ['tcp'] | |
for pair in itertools.product(ipv, proto): | |
v, p = pair | |
t = threading.Thread(target=target, args=(args, interface, port, v, p)) | |
t.start() | |
threads.append(t) | |
time.sleep(1) | |
return threads | |
def dispatch(target, args): | |
threads = [] | |
port = args.starting_port | |
for interface in interfaces(args.iface, args.count): | |
pending = dispatch_tests(target, args, interface, port) | |
threads.extend(pending) | |
port = port + 1000 | |
print('Done dispatching...now we wait.') | |
for thread in threads: | |
thread.join() | |
def handle_args(): | |
parser = argparse.ArgumentParser(description="Start iperf clients") | |
parser.add_argument('--servers', action='store_true', | |
help='Start iperf servers') | |
parser.add_argument('--starting-port', type=int, default=5000, | |
help='Port to start iperf range') | |
parser.add_argument('--neighbor', choices=['up', 'down'], | |
help='Direction to find neighbor address based on IP', | |
default='up') | |
parser.add_argument('--interval', type=int, default=5, | |
help='Print interval for iperf') | |
parser.add_argument('iface', help='Interface to start from') | |
parser.add_argument('count', type=int, help='Interfaces to start on') | |
return parser.parse_args() | |
def main(): | |
args = handle_args() | |
if args.servers: | |
dispatch(start_server, args) | |
else: | |
dispatch(start_client, args) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment