Skip to content

Instantly share code, notes, and snippets.

@dariobanfi
Created October 13, 2015 06:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dariobanfi/d9807df53efce7ac5502 to your computer and use it in GitHub Desktop.
Save dariobanfi/d9807df53efce7ac5502 to your computer and use it in GitHub Desktop.
#!/usr/bin/python
from __future__ import division
from mininet.topo import Topo
from mininet.node import CPULimitedHost
from mininet.link import TCLink
from mininet.net import Mininet
from mininet.util import dumpNodeConnections
from mininet.cli import CLI
from mininet.node import OVSController, RemoteController, OVSKernelSwitch
from bucket_test_topo import GroupTopo
import subprocess
from time import sleep, time
import numpy
import sys
import os
import csv
import re
from threading import Thread
import traceback, signal
from data_visualization import plot_barchart
# Control variables
traffic_mode = ['bulk', 'burst']
sender_ip = '10.0.0.1'
receiver_ip = '10.0.0.2'
intf_endhost = 'h2-eth0'
intf_s2 = 's2-eth1'
intf_s3 = 's3-eth1'
intf_s4 = 's4-eth1'
switches = ['s2','s3', 's4']
TESTING = True
def median(l):
"Compute median from an unsorted list of values"
s = sorted(l)
if len(s) % 2 == 1:
return s[(len(l) + 1) / 2 - 1]
else:
lower = s[len(l) / 2 - 1]
upper = s[len(l) / 2]
return float(lower + upper) / 2
def get_txbytes(iface):
f = open('/proc/net/dev', 'r')
lines = f.readlines()
for line in lines:
if iface in line:
break
f.close()
if not line:
raise Exception("could not find iface %s in /proc/net/dev:%s" %
(iface, lines))
# Returning bytes col value
return float(line.split()[9])
def get_rates(iface, nsamples=3, period=1.0, wait=1.0):
"""Returns the interface @iface's current utilization in Mb/s. It
returns @nsamples samples, and each sample is the average
utilization measured over @period time. Before measuring it waits
for @wait seconds to 'warm up'."""
# Returning nsamples requires one extra to start the timer.
nsamples += 1
last_time = 0
last_txbytes = 0
ret = []
sleep(wait)
while nsamples:
nsamples -= 1
txbytes = get_txbytes(iface)
now = time()
elapsed = now - last_time
# if last_time:
# print "elapsed: %0.4f" % (now - last_time)
last_time = now
# Get rate in Mbps; correct for elapsed time.
rate = (txbytes - last_txbytes) * 8.0 / 1e6 / elapsed
if last_txbytes != 0:
# Wait for 1 second sample
ret.append(rate)
last_txbytes = txbytes
sys.stdout.flush()
sleep(period)
return ret
def calc_average_latency(pingOutputLines):
total = 0
for line in pingOutputLines:
ping = float(line.split("time=")[1].split()[0])
total += ping
return total / 5.0
def measure_latency(net):
h1 = net.getNodeByName("h1")
h2 = net.getNodeByName("h2")
wifiRTT = h1.popen("ping %s -i 0.2 -c 6" %
(receiver_ip)).communicate()[0].split('\n')[2:7]
try:
avg_latency = calc_average_latency(wifiRTT)
print avg_latency
except:
print 'Impossible to ping hosts'
def measure_throughput(intf):
throughput = median(get_rates(intf))
print "throughput ",
print throughput
def iperf_measurement(net, dumptransfer, parsable=False,
l4Type='TCP', udpBw='150M', seconds=0, transferbytes='', monitorss=False, port=5001, parallel=1):
iperfoutput = 'Error occurred'
client = net.getNodeByName("h1")
server = net.getNodeByName("h2")
if(seconds != 0):
time_or_size = '-t %s ' % seconds
elif(transferbytes!= ''):
time_or_size = '-k %s ' % transferbytes
if l4Type == 'UDP':
clientargs = 'iperf -u -c %s -b %s %s &' % (server.IP(), udpBw, time_or_size)
serverargs = 'iperf -u -s'
else:
if dumptransfer:
iperf = 'iperf'
else:
iperf = 'iperf'
clientargs = '%s %s -V %s -c %s -P %d & ' % (iperf,
time_or_size,
'-r -y C' if parsable else '',
server.IP(), parallel)
serverargs = '%s -s -i 1 ' % iperf
try:
if(dumptransfer):
tcpdump_popen = begin_tcpdump('s5-eth4','data/pcap/out.pcap')
captcp = client.popen('captcp socketstatistic -s 10 -o /home/ubuntu/mpsdn/src/data/ss &')
server_popen = server.popen(serverargs)
sleep(0.2)
print clientargs
client_popen = client.popen(clientargs)
if(monitorss):
monitor_client = start_ss_monitoring(client, port, 'monitor_test', 1)
iperfoutput = client_popen.communicate()[0]
print iperfoutput
except:
print 'Exception in iperf_measurement'
traceback.print_exc()
finally:
if(monitorss):
end_ss_monitoring(monitor_client)
sleep(1)
server_popen.send_signal(signal.SIGINT)
if(dumptransfer):
tcpdump_popen.send_signal(signal.SIGINT)
captcp.send_signal(signal.SIGINT)
return iperfoutput
def query_ss(host, epoch, port):
command = "ss -into state established"
line = host.cmd(command)
print line
def monitor_sockets(host, stop_flag, port, filename, epoch):
errors_occured = False
try:
while stop_flag['ok']:
try:
query_ss(host, epoch, port)
except:
traceback.print_exc()
errors_occured = True # just keep going, and report later
sleep(0.1)
except:
errors_occured = True
traceback.print_exc()
sys.exit()
finally:
print 'MONITORING SS - DONE', 'WITH ERRORS' if errors_occured else ''
stop_flag['ended'] = True
stop_flag = { 'ok': True, 'ended': False }
# thread = Thread(name = 'ss_monitor', target = monitor_sockets, args=(host, stop_flag, port, filename, epoch, ) )
# thread.start()
return stop_flag
def start_ss_monitoring(host, port, filename, epoch):
stop_flag = { 'ok': True, 'ended': False }
thread = Thread(name = 'ss_monitor', target = monitor_sockets, args=(host, stop_flag, port, filename, epoch, ) )
thread.start()
return stop_flag
def end_ss_monitoring(stop_flag):
stop_flag['ok'] = False
while not stop_flag['ended']:
sleep(0.1)
#tcpdump: start
def begin_tcpdump( iface, trace_file ):
process = subprocess.Popen(['tcpdump', '-ttttt', '-n', '-i', iface, '-w', trace_file])
return process
#tcpdump: stop
def end_tcpdump( process ):
#SIGTERM
process.terminate() #process.kill()
(stdout_data, stderr_data) = process.communicate()
if stdout_data: print stdout_data
if stderr_data: print stderr_data
def parse_flow_table(switch, line):
table = subprocess.check_output(['ovs-ofctl', '-O', 'OpenFlow13', 'dump-flows' , switch])
values = table.split('\n')[line].split(',')
n_packets = values[3].split('=')[1]
n_bytes = values[4].split('=')[1]
return (float(n_bytes), float(n_packets))
def measure_transfer(net, timevalue, mode, bottleneck, dump_transfer, plot_transfer, parallel):
if(plot_transfer):
cleanup_files()
output_dir = 'output/%s_%s_%s' % (mode, timevalue, bottleneck)
subprocess.call('mkdir %s' % output_dir, shell=True)
# Used to check flow stats
initial_flow_data = []
for i, switch in enumerate(switches):
initial_flow_data.append(parse_flow_table(switch, 1))
## Generating data
iperf_measurement(net, dump_transfer, seconds=timevalue, l4Type=mode, monitorss=True, parallel=parallel)
# Printing flow tables
print "-" * 60
bytesum = 0
results = []
plotdata = {}
for i, switch in enumerate(switches):
final_data = (parse_flow_table(switch, 1)[0] - initial_flow_data[i][0],
parse_flow_table(switch, 1)[1] - initial_flow_data[i][1])
results.append(final_data)
bytesum += final_data[0]
print "packets %s bytes %s channel %s" % (final_data[1], final_data[0], switch)
if(bytesum>0):
plotdata['s2'] = round(results[0][0]/bytesum*100,2)
plotdata['s3'] = round(results[1][0]/bytesum*100,2)
plotdata['s4'] = round(results[2][0]/bytesum*100,2)
print "percentages s2: %s s3: %s s4: %s" % (plotdata['s2'] ,plotdata['s3'] , plotdata['s4'])
if(plot_transfer):
print 'plotting switch usage'
plot_barchart(plotdata, directory=output_dir)
print 'generating graphs in /output'
captcp_generate_report(output_dir=output_dir)
def captcp_generate_report(directory='data', pcap_file='data/pcap/out.pcap', output_dir='output'):
print 'generating statistics'
statistics = subprocess.check_output(['captcp', 'statistic', pcap_file])
with open("%s/statistics.txt" % output_dir, "w+") as text_file:
text_file.write(str(statistics))
subprocess.call('cp %s %s/' % (pcap_file, output_dir), shell=True)
subprocess.call('python data_visualization.py > %s/retransmissions.txt' % output_dir, shell=True)
# subprocess.call('captcp timesequence -i -o %s/timesequence/ -f 1.2 --zero -e %s' % (directory, pcap_file), shell=True)
# subprocess.call('make png -C %s/timesequence' % directory, shell=True)
# subprocess.call('mv %s/timesequence/time-sequence.png %s' % (directory, output_dir), shell=True)
# print 'generating spacing'
# subprocess.call('captcp spacing -f 1.1 -a 20 -i -o %s/spacing %s '% (directory, pcap_file), shell=True)
# subprocess.call('make png -C %s/spacing' % directory, shell=True)
# subprocess.call('mv %s/spacing/spacing.png %s' % (directory, output_dir), shell=True)
# print 'generating inflight'
# subprocess.call('captcp inflight -f 1.1 -i -o %s/inflight %s' % (directory, pcap_file), shell=True)
# subprocess.call('make png -C %s/inflight' % directory , shell=True)
# subprocess.call('mv %s/inflight/inflight.png %s' % (directory, output_dir) , shell=True)
print 'generating throughput'
subprocess.call('captcp throughput -f 1 -m transport-layer -i -o %s/throughput/ %s '% (directory, pcap_file), shell=True)
subprocess.call('make png -C %s/throughput' % directory , shell=True)
subprocess.call('mv %s/throughput/throughput.png %s' % (directory, output_dir) , shell=True)
print 'generating goodput'
subprocess.call('captcp throughput -f 1 -m goodput -i -o %s/goodput/ %s '% (directory, pcap_file), shell=True)
subprocess.call('make png -C %s/goodput' % directory, shell=True)
subprocess.call('mv %s/goodput/throughput.png %s/goodput.png' % (directory, output_dir) , shell=True)
print 'generating cwnd-ssthresh-rtt-skmem'
# iperf3 creates 2 flows.. The second one is the one needed.
dirs = os.listdir(directory + '/ss')
capture_folder = dirs[0]
subprocess.call('make png -C %s/ss/%s/rtt' % (directory, capture_folder), shell=True)
subprocess.call('mv %s/ss/%s/rtt/ss-rtt.png %s' % (directory, capture_folder, output_dir), shell=True)
subprocess.call('make png -C %s/ss/%s/cwnd-ssthresh' % (directory, capture_folder), shell=True)
subprocess.call('mv %s/ss/%s/cwnd-ssthresh/ss-cwnd-ssthresh.png %s' % (directory, capture_folder, output_dir), shell=True)
subprocess.call('make png -C %s/ss/%s/skmem' % (directory, capture_folder), shell=True)
subprocess.call('mv %s/ss/%s/skmem/ss-skmem.png %s' % (directory, capture_folder, output_dir), shell=True)
def cleanup_files(directory='./data'):
print 'cleaning up files'
subprocess.call('rm -rf %s/spacing/*' % directory, shell=True)
subprocess.call('rm -rf %s/pcap/*' % directory, shell=True)
subprocess.call('rm -rf %s/inflight/*' % directory, shell=True)
subprocess.call('rm -rf %s/throughput/*' % directory, shell=True)
subprocess.call('rm -rf %s/goodput/*' % directory, shell=True)
subprocess.call('rm -rf %s/ss/*' % directory, shell=True)
subprocess.call('rm -rf %s/rbd/*' % directory, shell=True)
subprocess.call('rm -rf ./output/*', shell=True)
def record_traffic(interface, save_file):
''' Opens a tcpdump instance on the host's interface '''
subprocess.call('tcpdump -p -i %s -s 68 -w %s &' % (interface, save_file), shell=True)
sleep(1)
def main_measure_asymmetric_throughput():
bottlenecks = [10, 10, 10]
delays = ['0ms','0ms','0ms']
with open('data/asymm_analysis/delay_wrr_unadapted.csv', 'wb') as csvfile:
delays = ['25ms','25ms','0ms']
resultswriter = csv.writer(csvfile, delimiter=' ', quotechar='|', quoting=csv.QUOTE_MINIMAL)
ratio = int(re.search(r'\d+', delays[0]).group())/int(re.search(r'\d+', delays[1]).group())
measurements = []
while ratio<15:
goodput = 0
for i in xrange(0,5):
print 'Doing sub-measuremenet %d' % i
net = Mininet(topo=GroupTopo(bottlenecks, delays), link=TCLink, controller=RemoteController)
net.start()
subprocess.call('./configure_bucket_test_topo.sh', shell=True)
outval = iperf_measurement(net, False, parsable=True, transferbytes='20M', l4Type='TCP')
goodput = int(outval.split(',')[8])
measurements.append(goodput)
net.stop()
goodput = numpy.mean(measurements)
std = numpy.std(measurements)
print '------------- delay_wps_unadapted RATIO %.2f GOODPUT %.2f STD %.2f -------------' % (ratio, goodput, std)
resultswriter.writerow(['%.2f' % ratio, '%.2f' % goodput, '%.2f' % std])
delayint = int(re.search(r'\d+', delays[0]).group())
newdelay = '%dms' % (int(delayint+20))
delays[0] = newdelay
ratio = int(re.search(r'\d+', delays[0]).group())/int(re.search(r'\d+', delays[1]).group())
def reordering_test(net, proto):
h1 = net.getNodeByName("h1")
h2 = net.getNodeByName("h2")
server = h2.popen('python reordering_tester.py -m s -p %s' % proto)
sleep(0.2)
client = h1.popen('python reordering_tester.py -c 100 -p %s' % proto)
print server.communicate()[0]
def main_report():
iperf_run_seconds = [15,30,60]
delays = ['25,25', '25,100', '25,300']
configs = ['wrr.sh', 'wrr_buffer.sh']
cleanup_files()
for iperf_run_second in iperf_run_seconds:
for delay in delays:
for config in configs:
print '--- Measuring --- ./multipath_measurement.py -p TCP -t %s -b 10,10 -d %s -config %s' % (iperf_run_second, delay, config)
subprocess.call('./multipath_measurement.py -p TCP -t %s -b 10,10 -d %s -config %s' % (iperf_run_second, delay, config), shell=True)
sleep(3)
print 'Report generation completed succesfully'
def main_testing():
iterate_cli = True
bottlenecks = [10.0, 10.0, 1.0]
delays = ['25ms','300ms','0ms']
net = Mininet(topo=GroupTopo(bottlenecks,delays), link=TCLink, switch=OVSKernelSwitch, controller=RemoteController)
net.start()
subprocess.call('./configure_bucket_test_topo.sh', shell=True)
while iterate_cli:
print "G'day, what do you want to do?"
print "1) iPerf - TCP"
print "2) iPerf - UDP"
print "3) Reordering UDP"
print "4) TCP Short"
print "5) iPerf - TCP Parallel"
print "Enter whatever else to exit."
choice = raw_input()
if choice=='1':
measure_transfer(net, 15, 'TCP', bottlenecks[0] , False, False, 1)
elif choice=='2':
measure_transfer(net, 5, 'UDP', bottlenecks[0] , False, False, 1)
elif choice=='3':
reordering_test(net, 'u')
elif choice=='4' :
reordering_test(net, 't')
elif choice=='5':
measure_transfer(net, 3, 'TCP', bottlenecks[0] , False, False, 20)
else:
iterate_cli = False
net.stop()
print 'Thanks, see ya'
if __name__ == '__main__':
try:
main_testing()
# main_report()
except:
print "Caught exception! Cleaning up..."
traceback.print_exc()
print '\n'*5
subprocess.call('mn -c', shell=True)
subprocess.call('pkill -f python', shell=True)
subprocess.call( 'killall -9 iperf', shell=True )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment