brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | ''' |
| 3 | Script that tests Flow Manager performance |
| 4 | Author: Brian O'Connor <bocon@onlab.us> |
| 5 | |
| 6 | ''' |
| 7 | |
| 8 | import csv |
| 9 | import os |
| 10 | import sys |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 11 | import re |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 12 | from time import sleep, strftime |
| 13 | from subprocess import Popen, call, check_output, PIPE |
| 14 | from datetime import datetime |
| 15 | |
| 16 | try: |
| 17 | import pexpect |
| 18 | except: |
| 19 | # install pexpect if it cannot be found and re-import |
| 20 | print '* Installing Pexpect' |
| 21 | call( 'apt-get install -y python-pexpect', stdout=PIPE, shell=True ) |
| 22 | import pexpect |
| 23 | |
| 24 | ONOS_HOME = '..' |
| 25 | ONOS_LOG = '%s/onos-logs/onos.%s.log' % ( ONOS_HOME, check_output( 'hostname').strip() ) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 26 | ONOS_URL = 'http://127.0.0.1:8080/wm/onos/flows/get/%d/json' |
| 27 | ONOS_LOG = '/tmp/onos-0.logs/onos.onos-vm.log' |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 28 | print "ONOS Log File:", ONOS_LOG |
| 29 | |
| 30 | PORT = 's1-eth2' |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 31 | N = 1 |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 32 | |
| 33 | # ----------------- Running the test and output ------------------------- |
| 34 | |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 35 | class Result(object): |
| 36 | def __init__(self, tsharkTime, flowmods, onosTime, overhead, details): |
| 37 | self.tsharkTime = tsharkTime |
| 38 | self.flowmods = flowmods |
| 39 | self.onosTime = onosTime |
| 40 | self.overhead = overhead |
| 41 | # sorted by start time |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 42 | self.tags = sorted(details, key=lambda x: float(x[2])) |
| 43 | self.details = sorted(details, key=lambda x: float(x[2]), reverse=True) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 44 | |
| 45 | def __repr__(self): |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 46 | return '%f %f %f %d %s' % (self.tsharkTime, self.onosTime, self.overhead, self.flowmods, self.tags) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 47 | |
| 48 | |
| 49 | def clearResults(): |
| 50 | cmd = 'curl %s' % ONOS_URL % -200 |
| 51 | call( cmd, shell=True ) |
| 52 | pass |
| 53 | |
| 54 | def reportResults(): |
| 55 | cmd = 'curl %s' % ONOS_URL % -100 |
| 56 | call( cmd, shell=True ) |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 57 | |
| 58 | def test(): |
| 59 | # Start tailing the onos log |
| 60 | tail = pexpect.spawn( "tail -0f %s" % ONOS_LOG ) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 61 | tshark = pexpect.spawn( 'tshark -i lo -R "of.type == 12 || of.type == 14"' ) |
| 62 | tshark.expect('Capturing on lo') |
| 63 | sleep(1) # wait for tshark to start |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 64 | |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 65 | clearResults() # REST call to ONOS |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 66 | # Take link down |
| 67 | call( 'ifconfig %s down' % PORT, shell=True ) |
| 68 | |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 69 | # collect openflow packets using tshark |
| 70 | count = 0 |
| 71 | timeout = 6000 |
| 72 | start = -1 |
| 73 | end = -1 |
| 74 | while True: |
| 75 | i = tshark.expect( ['(\d+\.\d+)', pexpect.TIMEOUT], timeout=timeout ) |
| 76 | if i == 1: |
| 77 | break |
| 78 | time = float(tshark.match.group(1)) |
| 79 | if start == -1: |
| 80 | start = time |
| 81 | if time > end: |
| 82 | end = time |
| 83 | i = tshark.expect( ['Port Status', 'Flow Mod'] ) |
| 84 | if i == 1: |
| 85 | count += 1 |
| 86 | timeout = 3 #sec |
| 87 | elapsed = (end - start) * 1000 |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 88 | |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 89 | sleep(2) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 90 | # read the performance results from ONOS |
| 91 | reportResults() # REST call |
| 92 | |
| 93 | # Wait for performance results in the log |
| 94 | tail.expect('Performance Results: \(avg/start/stop/count\)', timeout=10) |
| 95 | i = tail.expect('TotalTime:([\d\.]+)/Overhead:([\d\.]+)') |
| 96 | totalTime = float(tail.match.group(1)) |
| 97 | overhead = float(tail.match.group(2)) |
| 98 | tags = re.findall( r'([\w\.]+)=([\d\.]+)/([\d\.]+)/([\d\.]+)/(\d+)', tail.before ) |
| 99 | result = Result(elapsed, count, totalTime, overhead, tags) |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 100 | # Output results |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 101 | print result |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 102 | plot(result) |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 103 | |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 104 | # Bring port back up |
| 105 | call( 'ifconfig %s up' % PORT, shell=True ) |
| 106 | |
| 107 | tail.terminate() |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 108 | tshark.terminate() |
| 109 | return [] |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 110 | |
| 111 | def outputResults(filename, results): |
| 112 | with open(filename, 'a') as csvfile: |
| 113 | writer = csv.writer(csvfile) |
| 114 | writer.writerow(results) |
| 115 | |
| 116 | def runPerf(n): |
| 117 | filename = 'results-flowmanager-%s.csv' % strftime( '%Y%m%d-%H%M%S' ) |
| 118 | print 'Starting experiments:' |
| 119 | start = datetime.now() |
| 120 | for i in range(n): |
| 121 | results = test() |
Brian O'Connor | 0d9963f | 2014-01-14 14:44:21 -0800 | [diff] [blame] | 122 | #outputResults(filename, results) |
| 123 | sys.stdout.write('$') |
| 124 | sys.stdout.flush() |
| 125 | sleep(5) # sleep for 5 seconds between tests |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 126 | sys.stdout.write('.') |
| 127 | sys.stdout.flush() |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 128 | totalTime = datetime.now() - start |
| 129 | print '\nExperiments complete in %s (h:m:s.s)' % totalTime |
| 130 | |
brian | 6df8823 | 2014-01-14 17:05:07 -0800 | [diff] [blame] | 131 | def plot(result): |
| 132 | import matplotlib.pyplot as plt |
| 133 | import pylab |
| 134 | import numpy as np |
| 135 | from matplotlib.ticker import MaxNLocator |
| 136 | |
| 137 | tags = [ x[0] for x in result.details ] |
| 138 | numTags = len(tags) |
| 139 | scores = [ float(x[1]) for x in result.details ] |
| 140 | offset = [ float(x[2]) for x in result.details ] |
| 141 | rankings = [ float(x[3]) - float(x[2]) for x in result.details ] |
| 142 | counts = [ x[4] for x in result.details ] |
| 143 | |
| 144 | fig, ax1 = plt.subplots(figsize=(15, 9)) |
| 145 | plt.subplots_adjust(left=0.3, right=0.8) |
| 146 | pos = np.arange(numTags)+0.5 # Center bars on the Y-axis ticks |
| 147 | rects = ax1.barh(pos, rankings, left=offset, align='center', height=0.5, color='m') |
| 148 | |
| 149 | ax1.axis([0, result.onosTime, 0, numTags]) |
| 150 | pylab.yticks(pos, tags) |
| 151 | ax1.set_title('TITLE HERE') |
| 152 | #plt.text(result.onosTime/2, -0.5, |
| 153 | # 'Iteration: ' + str(1), horizontalalignment='center', size='small') |
| 154 | |
| 155 | # Set the right-hand Y-axis ticks and labels and set X-axis tick marks at the |
| 156 | # deciles |
| 157 | ax2 = ax1.twinx() |
| 158 | print MaxNLocator(7) |
| 159 | ax2.xaxis.set_major_locator(MaxNLocator(7)) # max number of xaxis ticks |
| 160 | #ax2.plot([100, 100], [0, 5], 'white', alpha=0.1) |
| 161 | ax2.xaxis.grid(True, linestyle='--', which='major', color='grey', alpha=0.25) |
| 162 | #Plot a solid vertical gridline to highlight the median position |
| 163 | #plt.plot([50, 50], [0, 5], 'grey', alpha=0.25) |
| 164 | |
| 165 | # Build up the score labels for the right Y-axis by first appending a carriage |
| 166 | # return to each string and then tacking on the appropriate meta information |
| 167 | # (i.e., 'laps' vs 'seconds'). We want the labels centered on the ticks, so if |
| 168 | # there is no meta info (like for pushups) then don't add the carriage return to |
| 169 | # the string |
| 170 | |
| 171 | ''' |
| 172 | scoreLabels = [withnew(i, scr) for i, scr in enumerate(scores)] |
| 173 | scoreLabels = [i+j for i, j in zip(scoreLabels, testMeta)] |
| 174 | ''' |
| 175 | scoreLabels = ['%.3f ms\n%s'%(i,j) for i,j in zip(scores,counts)] |
| 176 | # set the tick locations |
| 177 | ax2.set_yticks(pos) |
| 178 | # set the tick labels |
| 179 | ax2.set_yticklabels(scoreLabels) |
| 180 | # make sure that the limits are set equally on both yaxis so the ticks line up |
| 181 | ax2.set_ylim(ax1.get_ylim()) |
| 182 | |
| 183 | ax1.set_xlabel('Time (ms)') |
| 184 | ax2.set_ylabel('Average iteration / Count') |
| 185 | |
| 186 | # Lastly, write in the ranking inside each bar to aid in interpretation |
| 187 | for rect in rects: |
| 188 | # Rectangle widths are already integer-valued but are floating |
| 189 | # type, so it helps to remove the trailing decimal point and 0 by |
| 190 | # converting width to int type |
| 191 | width = int(rect.get_width()) |
| 192 | offset = int(rect.get_x()) |
| 193 | percent = width / result.onosTime |
| 194 | onePercent = 0.01 * result.onosTime |
| 195 | |
| 196 | rankStr = str(width) + 'ms' |
| 197 | if (percent < 0.09): # The bars aren't wide enough to print the ranking inside |
| 198 | xloc = offset + width + onePercent # Shift the text to the right side of the right edge |
| 199 | clr = 'black' # Black against white background |
| 200 | align = 'left' |
| 201 | else: |
| 202 | xloc = offset + 0.98*width # Shift the text to the left side of the right edge |
| 203 | clr = 'white' # White on magenta |
| 204 | align = 'right' |
| 205 | |
| 206 | # Center the text vertically in the bar |
| 207 | yloc = rect.get_y()+rect.get_height()/2.0 |
| 208 | ax1.text(xloc, yloc, rankStr, horizontalalignment=align, |
| 209 | verticalalignment='center', color=clr) |
| 210 | |
| 211 | plt.show() |
| 212 | plt.savefig('test.png') |
brian | b87e3b1 | 2014-01-12 22:12:14 -0800 | [diff] [blame] | 213 | if __name__ == '__main__': |
| 214 | n = N |
| 215 | runPerf(n) |
| 216 | |