Created
October 13, 2015 06:33
-
-
Save dariobanfi/d9807df53efce7ac5502 to your computer and use it in GitHub Desktop.
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/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