Merge "Fixed typo in setting up ARP timers"
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / openstackrapid / runrapid.py
1 #!/usr/bin/python
2
3 ##
4 ## Copyright (c) 2010-2017 Intel Corporation
5 ##
6 ## Licensed under the Apache License, Version 2.0 (the "License");
7 ## you may not use this file except in compliance with the License.
8 ## You may obtain a copy of the License at
9 ##
10 ##     http://www.apache.org/licenses/LICENSE-2.0
11 ##
12 ## Unless required by applicable law or agreed to in writing, software
13 ## distributed under the License is distributed on an "AS IS" BASIS,
14 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ## See the License for the specific language governing permissions and
16 ## limitations under the License.
17 ##
18
19 from __future__ import print_function
20
21 import os
22 import stat
23 import sys
24 import time
25 import subprocess
26 import getopt
27 import re
28 import logging
29 from logging.handlers import RotatingFileHandler
30 from logging import handlers
31 from prox_ctrl import prox_ctrl
32 import ConfigParser
33 import ast
34 import atexit
35 import csv
36
37 version="19.1.10"
38 env = "rapid" #Default string for environment
39 test = "basicrapid" #Default string for test
40 loglevel="DEBUG" # sets log level for writing to file
41 runtime=10 # time in seconds for 1 test run
42 configonly = False # IF True, the system will upload all the necessary config fiels to the VMs, but not start PROX and the actual testing
43
44 def usage():
45         print("usage: runrapid    [--version] [-v]")
46         print("                   [--env ENVIRONMENT_NAME]")
47         print("                   [--test TEST_NAME]")
48         print("                   [--runtime TIME_FOR_TEST]")
49         print("                   [--configonly False|True]")
50         print("                   [--log DEBUG|INFO|WARNING|ERROR|CRITICAL]")
51         print("                   [-h] [--help]")
52         print("")
53         print("Command-line interface to runrapid")
54         print("")
55         print("optional arguments:")
56         print("  -v,  --version                 Show program's version number and exit")
57         print("  --env ENVIRONMENT_NAME         Parameters will be read from ENVIRONMENT_NAME.env Default is %s."%env)
58         print("  --test TEST_NAME               Test cases will be read from TEST_NAME.test Default is %s."%test)
59         print("  --runtime                      Specify time in seconds for 1 test run")
60         print("  --configonly                   If True, only upload all config files to the VMs, do not run the tests. Default is %s."%configonly)
61         print("  --log                          Specify logging level for log file output, screen output level is hard coded")
62         print("  -h, --help                     Show help message and exit.")
63         print("")
64
65 try:
66         opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "env=", "test=","runtime=","configonly=","log="])
67 except getopt.GetoptError as err:
68         print("===========================================")
69         print(str(err))
70         print("===========================================")
71         usage()
72         sys.exit(2)
73 if args:
74         usage()
75         sys.exit(2)
76 for opt, arg in opts:
77         if opt in ("-h", "--help"):
78                 usage()
79                 sys.exit()
80         if opt in ("-v", "--version"):
81                 print("Rapid Automated Performance Indication for Dataplane "+version)
82                 sys.exit()
83         if opt in ("--env"):
84                 env = arg
85                 print ("Using '"+env+"' as name for the environment")
86         if opt in ("--test"):
87                 test = arg
88                 print ("Using '"+test+".test' for test case definition")
89         if opt in ("--runtime"):
90                 runtime = arg
91                 print ("Runtime: "+ runtime)
92         if opt in ("--configonly"):
93                 configonly = arg
94                 print ("configonly: "+ configonly)
95         if opt in ("--log"):
96                 loglevel = arg
97                 print ("Log level: "+ loglevel)
98
99 class bcolors:
100     HEADER = '\033[95m'
101     OKBLUE = '\033[94m'
102     OKGREEN = '\033[92m'
103     WARNING = '\033[93m'
104     FAIL = '\033[91m'
105     ENDC = '\033[0m'
106     BOLD = '\033[1m'
107     UNDERLINE = '\033[4m'
108
109 # create formatters
110 screen_formatter = logging.Formatter("%(message)s")
111 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
112
113 # get a top-level logger,
114 # set its log level,
115 # BUT PREVENT IT from propagating messages to the root logger
116 #
117 log = logging.getLogger()
118 numeric_level = getattr(logging, loglevel.upper(), None)
119 if not isinstance(numeric_level, int):
120     raise ValueError('Invalid log level: %s' % loglevel)
121 log.setLevel(numeric_level)
122 log.propagate = 0
123
124 # create a console handler
125 # and set its log level to the command-line option 
126
127 console_handler = logging.StreamHandler(sys.stdout)
128 console_handler.setLevel(logging.INFO)
129 console_handler.setFormatter(screen_formatter)
130
131 # create a file handler
132 # and set its log level to DEBUG
133 #
134 log_file = 'RUN' +env+'.'+test+'.log'
135 data_file = 'RUN' +env+'.'+test+'.csv'
136 file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
137 #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
138 file_handler.setLevel(numeric_level)
139 file_handler.setFormatter(file_formatter)
140
141 # add handlers to the logger
142 #
143 log.addHandler(file_handler)
144 log.addHandler(console_handler)
145
146 # Check if log exists and should therefore be rolled
147 needRoll = os.path.isfile(log_file)
148
149
150 # This is a stale log, so roll it
151 if needRoll:    
152     # Add timestamp
153     log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
154
155     # Roll over on application start
156     log.handlers[0].doRollover()
157
158 # Add timestamp
159 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
160
161 log.debug("runrapid.py version: "+version)
162 #========================================================================
163 def connect_socket(client):
164         attempts = 1
165         log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
166         sock = None
167         while True:
168                 sock = client.prox_sock()
169                 if sock is not None:
170                         break
171                 attempts += 1
172                 if attempts > 20:
173                         log.exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
174                         raise Exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
175                 time.sleep(2)
176                 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
177         log.info("Connected to PROX on %s" % client.ip())
178         return sock
179
180 def connect_client(client):
181         attempts = 1
182         log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
183         while True:
184                 try:
185                         client.connect()
186                         break
187                 except RuntimeWarning, ex:
188                         attempts += 1
189                         if attempts > 20:
190                                 log.exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
191                                 raise Exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
192                         time.sleep(2)
193                         log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
194         log.debug("Connected to VM on %s" % client.ip())
195
196 def run_iteration(gensock,sutsock):
197         sleep_time = 3
198         # Sleep_time is needed to be able to do accurate measurements to check for packet loss. We need to make this time large enough so that we do not take the first measurement while some packets from the previous tests migth still be in flight
199         time.sleep(sleep_time)
200         abs_old_rx, abs_old_tx, abs_old_drop, abs_old_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
201         gensock.start(gencores)
202         time.sleep(sleep_time)
203         if sutsock!='none':
204                 old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
205         old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
206         time.sleep(float(runtime))
207         lat_min, lat_max, lat_avg = gensock.lat_stats(latcores)
208         # Get statistics after some execution time
209         new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
210         if sutsock!='none':
211                 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
212         #Stop generating
213         gensock.stop(gencores)
214         time.sleep(sleep_time)
215         abs_new_rx, abs_new_tx, abs_new_drop, abs_new_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
216         drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM
217         rx = new_rx - old_rx     # rx is all packets received by the nop task = all packets received in the gen VM
218         tx = new_tx - old_tx     # tx is all generated packets actually accepted by the interface
219         abs_dropped = (abs_new_tx - abs_old_tx) - (abs_new_rx - abs_old_rx)
220         tsc = new_tsc - old_tsc  # time difference between the 2 measurements, expressed in cycles.
221         pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
222         pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
223         pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
224         if sutsock!='none':
225                 sut_rx = new_sut_rx - old_sut_rx
226                 sut_tx = new_sut_tx - old_sut_tx
227                 sut_tsc = new_sut_tsc - old_sut_tsc
228                 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
229                 pps_sut_tx_str = '{:>9.3f}'.format(pps_sut_tx)
230         else:
231                 pps_sut_tx = 0
232                 pps_sut_tx_str = 'NO MEAS.'
233         if (tx == 0):
234                 log.critical("TX = 0. Test interrupted since no packet has been sent.")
235                 raise Exception("TX = 0")
236         return(pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max,abs_dropped,(abs_new_tx - abs_old_tx))
237
238 def new_speed(speed,minspeed,maxspeed,success):
239         # Following calculates the ratio for the new speed to be applied
240         # On the Y axis, we will find the ratio, a number between 0 and 1
241         # On the x axis, we find the % of dropped packets, a number between 0 and 100
242         # 2 lines are drawn and we take the minumun of these lines to calculate the ratio
243         # One line goes through (0,y0) and (p,q)
244         # The second line goes through (p,q) and (100,y100)
245 #       y0=0.99
246 #       y100=0.1
247 #       p=1
248 #       q=.99
249 #       ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100))
250 #       return (int(speed*ratio*100)+0.5)/100.0
251         if success:
252                 minspeed = speed
253         else:
254                 maxspeed = speed
255         newspeed = (maxspeed+minspeed)/2.0
256         return (newspeed,minspeed,maxspeed)
257
258 def get_pps(speed,size):
259         return (speed * 100.0 / (8*(size+24)))
260
261 def run_speedtest(gensock,sutsock):
262         maxspeed = speed = STARTSPEED
263         minspeed = 0
264         size=175
265         attempts = 0
266         log.info("+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
267         log.info("| Generator is sending UDP (1 flow) packets ("+ '{:>5}'.format(size+4) +" bytes) to SUT. SUT sends packets back                                                                                       |")
268         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
269         log.info("| Test   |  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Max. Latency  |  Packets Lost  | Loss Ratio | Result     |")
270         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
271         endpps_sut_tx_str = 'NO_RESULTS'
272         gensock.set_size(gencores,0,size) # This is setting the frame size
273         gensock.set_value(gencores,0,16,(size-14),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
274         gensock.set_value(gencores,0,38,(size-34),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
275         # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
276         while (maxspeed-minspeed > ACCURACY):
277                 attempts += 1
278                 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
279                 sys.stdout.flush()
280                 # Start generating packets at requested speed (in % of a 10Gb/s link)
281                 gensock.speed(speed, gencores)
282                 time.sleep(1)
283                 # Get statistics now that the generation is stable and initial ARP messages are dealt with.
284                 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
285                 drop_rate = 100.0*abs_dropped/abs_tx
286                 if ((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001 and ((drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0)) and (lat_avg< LAT_AVG_TRESHOLD) and (lat_max < LAT_MAX_TRESHOLD):
287                         log.info('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(pps_tx) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+ '{:>9.0f}'.format(lat_avg)+' us   | '+  '{:>9.0f}'.format(lat_max)+' us   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  | SUCCESS    |')
288                         endspeed = speed
289                         endpps_req_tx = pps_req_tx
290                         endpps_tx = pps_tx
291                         endpps_sut_tx_str = pps_sut_tx_str
292                         endpps_rx = pps_rx
293                         endlat_avg = lat_avg
294                         endlat_max = lat_max
295                         endabs_dropped = abs_dropped
296                         enddrop_rate = drop_rate
297                         success = True 
298                 else:
299                         abs_drop_rate_prefix = bcolors.ENDC
300                         if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
301                                 abs_drop_rate_prefix = bcolors.FAIL
302                         if (drop_rate < DROP_RATE_TRESHOLD):
303                                 drop_rate_prefix = bcolors.ENDC
304                         else:
305                                 drop_rate_prefix = bcolors.FAIL
306                         if (lat_avg< LAT_AVG_TRESHOLD):
307                                 lat_avg_prefix = bcolors.ENDC
308                         else:
309                                 lat_avg_prefix = bcolors.FAIL
310                         if (lat_max< LAT_MAX_TRESHOLD):
311                                 lat_max_prefix = bcolors.ENDC
312                         else:
313                                 lat_max_prefix = bcolors.FAIL
314                         if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
315                                 speed_prefix = bcolors.ENDC
316                         else:
317                                 speed_prefix = bcolors.FAIL
318                         log.info('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% '+speed_prefix +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | ' + '{:>9.3f}'.format(pps_tx) +' Mpps | '+ bcolors.ENDC  + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+lat_avg_prefix+ '{:>9.0f}'.format(lat_avg)+' us   | '+lat_max_prefix+ '{:>9.0f}'.format(lat_max)+' us   | '+ abs_drop_rate_prefix + '{:>14d}'.format(abs_dropped)+drop_rate_prefix+ ' |''{:>9.2f}'.format(drop_rate)+bcolors.ENDC+ '%  | FAILED     |')
319                         success = False 
320                 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
321         if endpps_sut_tx_str <>  'NO_RESULTS':
322                 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
323                 log.info('|{:>7}'.format('END')+" | " + '{:>5.1f}'.format(endspeed) + '% ' +'{:>6.3f}'.format(get_pps(endspeed,size)) + ' Mpps | '+ '{:>9.3f}'.format(endpps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(endpps_tx) +' Mpps | ' + '{:>9}'.format(endpps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(endpps_rx)+' Mpps | '+ '{:>9.0f}'.format(endlat_avg)+' us   | '+ '{:>9.0f}'.format(endlat_max)+' us   | '+'{:>14d}'.format(endabs_dropped)+ ' |''{:>9.2f}'.format(enddrop_rate)+ '%  | SUCCESS    |')
324                 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
325         else:
326                 log.info('| Speed 0 or close to 0')
327
328 def run_flowtest(gensock,sutsock):
329         log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
330         log.info("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port                                                                                      |")
331         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
332         log.info("| Flows  |  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Max. Latency  |  Packets Lost  | Loss Ratio |")
333         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
334         size=60
335         # To generate a desired number of flows, PROX will randomize the bits in source and destination ports, as specified by the bit masks in the flows variable. 
336         flows={128:['1000000000000XXX','100000000000XXXX'],1024:['10000000000XXXXX','10000000000XXXXX'],8192:['1000000000XXXXXX','100000000XXXXXXX'],65535:['10000000XXXXXXXX','10000000XXXXXXXX'],524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
337 #       flows={524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
338         gensock.set_size(gencores,0,size) # This is setting the frame size
339         gensock.set_value(gencores,0,16,(size-14),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
340         gensock.set_value(gencores,0,38,(size-34),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
341         # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
342         for flow_number in sorted(flows.iterkeys()):
343                 attempts = 0
344                 gensock.reset_stats()
345                 if sutsock!='none':
346                         sutsock.reset_stats()
347                 source_port,destination_port = flows[flow_number]
348                 gensock.set_random(gencores,0,34,source_port,2)
349                 gensock.set_random(gencores,0,36,destination_port,2)
350                 endpps_sut_tx_str = 'NO_RESULTS'
351                 maxspeed = speed = STARTSPEED
352                 minspeed = 0
353                 while (maxspeed-minspeed > ACCURACY):
354                         attempts += 1
355                         print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
356                         sys.stdout.flush()
357                         # Start generating packets at requested speed (in % of a 10Gb/s link)
358                         gensock.speed(speed, gencores)
359                         time.sleep(1)
360                         # Get statistics now that the generation is stable and initial ARP messages are dealt with
361                         pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
362                         drop_rate = 100.0*abs_dropped/abs_tx
363                         if ((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001 and ((drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0)) and (lat_avg< LAT_AVG_TRESHOLD) and (lat_max < LAT_MAX_TRESHOLD):
364                                 log.debug('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(pps_tx) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+ '{:>9.0f}'.format(lat_avg)+' us   | '+  '{:>9.0f}'.format(lat_max)+' us   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  | SUCCESS    |')
365                                 endspeed = speed
366                                 endpps_req_tx = pps_req_tx
367                                 endpps_tx = pps_tx
368                                 endpps_sut_tx_str = pps_sut_tx_str
369                                 endpps_rx = pps_rx
370                                 endlat_avg = lat_avg 
371                                 endlat_max = lat_max 
372                                 endabs_dropped = abs_dropped
373                                 enddrop_rate = drop_rate
374                                 success = True
375                         else:
376                                 abs_drop_rate_prefix = bcolors.ENDC
377                                 if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
378                                         abs_drop_rate_prefix = bcolors.FAIL
379                                 if (drop_rate < DROP_RATE_TRESHOLD):
380                                         drop_rate_prefix = bcolors.ENDC
381                                 else:
382                                         drop_rate_prefix = bcolors.FAIL
383                                 if (lat_avg< LAT_AVG_TRESHOLD):
384                                         lat_avg_prefix = bcolors.ENDC
385                                 else:
386                                         lat_avg_prefix = bcolors.FAIL
387                                 if (lat_max< LAT_MAX_TRESHOLD):
388                                         lat_max_prefix = bcolors.ENDC
389                                 else:
390                                         lat_max_prefix = bcolors.FAIL
391                                 if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
392                                         speed_prefix = bcolors.ENDC
393                                 else:
394                                         speed_prefix = bcolors.FAIL
395                                 log.debug('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% '+speed_prefix +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | ' + '{:>9.3f}'.format(pps_tx) +' Mpps | '+ bcolors.ENDC  + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+lat_avg_prefix+ '{:>9.0f}'.format(lat_avg)+' us   | '+lat_max_prefix+ '{:>9.0f}'.format(lat_max)+' us   | '+ abs_drop_rate_prefix + '{:>14d}'.format(abs_dropped)+drop_rate_prefix+ ' |''{:>9.2f}'.format(drop_rate)+bcolors.ENDC+ '%  | FAILED     |')
396                                 success = False 
397                         speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
398                 if endpps_sut_tx_str <>  'NO_RESULTS':
399                         log.info('|{:>7}'.format(str(flow_number))+" | " + '{:>5.1f}'.format(endspeed) + '% ' +'{:>6.3f}'.format(get_pps(endspeed,size)) + ' Mpps | '+ '{:>9.3f}'.format(endpps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(endpps_tx) +' Mpps | ' + '{:>9}'.format(endpps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(endpps_rx)+' Mpps | '+ '{:>9.0f}'.format(endlat_avg)+' us   | '+ '{:>9.0f}'.format(endlat_max)+' us   | '+ '{:>14d}'.format(endabs_dropped)+ ' |'+'{:>9.2f}'.format(enddrop_rate)+ '%  |')
400                         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
401                         writer.writerow({'flow':flow_number,'size':(size+4),'endspeed':endspeed,'endspeedpps':get_pps(endspeed,size),'endpps_req_tx':endpps_req_tx,'endpps_tx':endpps_tx,'endpps_sut_tx_str':endpps_sut_tx_str,'endpps_rx':endpps_rx,'endlat_avg':endlat_avg,'endlat_max':endlat_max,'endabs_dropped':endabs_dropped,'enddrop_rate':enddrop_rate})
402                 else:
403                         log.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
404
405 def run_sizetest(gensock,sutsock):
406         log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
407         log.info("| UDP, 1 flow, different packet sizes                                                                                                                             |")
408         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
409         log.info("| Pktsize|  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Max. Latency  |  Packets Lost  | Loss Ratio |")
410         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
411         # PROX will use different packet sizes as defined in sizes[]
412         sizes=[1496,1020,508,252,124,60]
413 #       sizes=[1020,508,252,124,60]
414 #       sizes=[124,60]
415         for size in sizes:
416                 attempts = 0
417                 gensock.reset_stats()
418                 if sutsock!='none':
419                         sutsock.reset_stats()
420                 gensock.set_size(gencores,0,size) # This is setting the frame size
421                 gensock.set_value(gencores,0,16,(size-14),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
422                 gensock.set_value(gencores,0,38,(size-34),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
423                 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
424                 endpps_sut_tx_str = 'NO_RESULTS'
425                 maxspeed = speed = STARTSPEED
426                 minspeed = 0
427                 while (maxspeed-minspeed > ACCURACY):
428                         attempts += 1
429                         print(str(size+4)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
430                         sys.stdout.flush()
431                         # Start generating packets at requested speed (in % of a 10Gb/s link)
432                         gensock.speed(speed, gencores)
433                         # Get statistics now that the generation is stable and initial ARP messages are dealt with
434                         pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
435                         drop_rate = 100.0*abs_dropped/abs_tx
436                         if ((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001 and ((drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0)) and (lat_avg< LAT_AVG_TRESHOLD) and (lat_max < LAT_MAX_TRESHOLD):
437                                 log.debug('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(pps_tx) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+ '{:>9.0f}'.format(lat_avg)+' us   | '+  '{:>9.0f}'.format(lat_max)+' us   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  | SUCCESS    |')
438                                 endspeed = speed
439                                 endpps_req_tx = pps_req_tx
440                                 endpps_tx = pps_tx
441                                 endpps_sut_tx_str = pps_sut_tx_str
442                                 endpps_rx = pps_rx
443                                 endlat_avg = lat_avg 
444                                 endlat_max = lat_max 
445                                 endabs_dropped = abs_dropped
446                                 enddrop_rate = drop_rate
447                                 success = True
448                         else:
449                                 abs_drop_rate_prefix = bcolors.ENDC
450                                 if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
451                                         abs_drop_rate_prefix = bcolors.FAIL
452                                 if (drop_rate < DROP_RATE_TRESHOLD):
453                                         drop_rate_prefix = bcolors.ENDC
454                                 else:
455                                         drop_rate_prefix = bcolors.FAIL
456                                 if (lat_avg< LAT_AVG_TRESHOLD):
457                                         lat_avg_prefix = bcolors.ENDC
458                                 else:
459                                         lat_avg_prefix = bcolors.FAIL
460                                 if (lat_max< LAT_MAX_TRESHOLD):
461                                         lat_max_prefix = bcolors.ENDC
462                                 else:
463                                         lat_max_prefix = bcolors.FAIL
464                                 if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
465                                         speed_prefix = bcolors.ENDC
466                                 else:
467                                         speed_prefix = bcolors.FAIL
468                                 log.debug('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% '+speed_prefix +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | ' + '{:>9.3f}'.format(pps_tx) +' Mpps | '+ bcolors.ENDC  + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+lat_avg_prefix+ '{:>9.0f}'.format(lat_avg)+' us   | '+lat_max_prefix+ '{:>9.0f}'.format(lat_max)+' us   | '+ abs_drop_rate_prefix + '{:>14d}'.format(abs_dropped)+drop_rate_prefix+ ' |''{:>9.2f}'.format(drop_rate)+bcolors.ENDC+ '%  | FAILED     |')
469                                 success = False 
470                         speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
471                 if endpps_sut_tx_str <>  'NO_RESULTS':
472                         log.info('|{:>7}'.format(size+4)+" | " + '{:>5.1f}'.format(endspeed) + '% ' +'{:>6.3f}'.format(get_pps(endspeed,size)) + ' Mpps | '+ '{:>9.3f}'.format(endpps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(endpps_tx) +' Mpps | ' + '{:>9}'.format(endpps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(endpps_rx)+' Mpps | '+ '{:>9.0f}'.format(endlat_avg)+' us   | '+'{:>9.0f}'.format(endlat_max)+' us   | '+ '{:>14d}'.format(endabs_dropped)+ ' |'+'{:>9.2f}'.format(enddrop_rate)+ '%  |')
473                         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
474                         writer.writerow({'flow':'1','size':(size+4),'endspeed':endspeed,'endspeedpps':get_pps(endspeed,size),'endpps_req_tx':endpps_req_tx,'endpps_tx':endpps_tx,'endpps_sut_tx_str':endpps_sut_tx_str,'endpps_rx':endpps_rx,'endlat_avg':endlat_avg,'endlat_max':endlat_max,'endabs_dropped':endabs_dropped,'enddrop_rate':enddrop_rate})
475                 else:
476                         log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
477
478
479 def run_max_frame_rate(gensock,sutsock):
480         log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------+")
481         log.info("| UDP, 1 flow, different packet sizes                                                                                                                       |")
482         log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
483         log.info("|Pktsz| Speed requested  | Sent to NIC | Sent by Gen | Fwrd by SUT | Rec. by Gen | Avg. Latency| Max. Latency|   Sent    |  Received |  Lost   | Total Lost |")
484         log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
485         # PROX will use different packet sizes as defined in sizes[]
486         sizes=[1496,1020,508,252,124,60]
487         sleep_time = 3
488         for size in sizes:
489                 # Sleep_time is needed to be able to do accurate measurements to check for packet loss. We need to make this time large enough so that we do not take the first measurement while some packets from the previous tests migth still be in flight
490                 time.sleep(sleep_time)
491                 gensock.reset_stats()
492                 if sutsock!='none':
493                         sutsock.reset_stats()
494                 gensock.set_size(gencores,0,size) # This is setting the frame size
495                 gensock.set_value(gencores,0,16,(size-14),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
496                 gensock.set_value(gencores,0,38,(size-34),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
497                 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
498                 pps_sut_tx_str = 'NO_RESULTS'
499                 speed = STARTSPEED 
500                 # Start generating packets at requested speed (in % of a 10Gb/s link)
501                 gensock.speed(speed / len(gencores), gencores)
502                 duration = float(runtime)
503                 first = 1
504                 tot_drop = 0
505                 if sutsock!='none':
506                         old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
507                 old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
508                 gensock.start(gencores)
509                 while (duration > 0):
510                         duration = duration - 1
511                         time.sleep(1)
512                         lat_min, lat_max, lat_avg = gensock.lat_stats(latcores)
513                         # Get statistics after some execution time
514                         new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
515                         if sutsock!='none':
516                                 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
517                         drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM
518                         rx = new_rx - old_rx     # rx is all packets received by the nop task = all packets received in the gen VM
519                         tx = new_tx - old_tx     # tx is all generated packets actually accepted by the interface
520                         tsc = new_tsc - old_tsc  # time difference between the 2 measurements, expressed in cycles.
521                         old_drop = new_drop
522                         old_rx = new_rx
523                         old_tx = new_tx
524                         old_tsc = new_tsc
525                         pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
526                         pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
527                         pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
528                         if sutsock!='none':
529                                 sut_rx = new_sut_rx - old_sut_rx
530                                 sut_tx = new_sut_tx - old_sut_tx
531                                 sut_tsc = new_sut_tsc - old_sut_tsc
532                                 old_sut_tx = new_sut_tx
533                                 old_sut_rx = new_sut_rx
534                                 old_sut_tsc= new_sut_tsc
535                                 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
536                                 pps_sut_tx_str = '{:>7.3f}'.format(pps_sut_tx)
537                         else:
538                                 pps_sut_tx = 0
539                                 pps_sut_tx_str = 'NO MEAS.'
540                         if (tx == 0):
541                                 log.critical("TX = 0. Test interrupted since no packet has been sent.")
542                                 raise Exception("TX = 0")
543                         tot_drop = tot_drop + tx - rx
544                 
545                         if pps_sut_tx_str <>  'NO_RESULTS':
546                                 # First second mpps are not valid as there is no alignement between time the generator is started and per seconds stats
547                                 if (first):
548                                         log.info('|{:>4}'.format(size+4)+" |" + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps|'+'             |' +'             |'  +'             |'+ '             |'+ '{:>8.0f}'.format(lat_avg)+' us  |'+'{:>8.0f}'.format(lat_max)+' us  | ' + '{:>9.0f}'.format(tx) + ' | '+ '{:>9.0f}'.format(rx) + ' | '+ '{:>7.0f}'.format(tx-rx) + ' | '+'{:>7.0f}'.format(tot_drop) +' |  ')
549                                 else:
550                                         log.info('|{:>4}'.format(size+4)+" |" + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps|'+ '{:>7.3f}'.format(pps_req_tx)+' Mpps |'+ '{:>7.3f}'.format(pps_tx) +' Mpps |' + '{:>7}'.format(pps_sut_tx_str) +' Mpps |'+ '{:>7.3f}'.format(pps_rx)+' Mpps |'+ '{:>8.0f}'.format(lat_avg)+' us  |'+'{:>8.0f}'.format(lat_max)+' us  | ' + '{:>9.0f}'.format(tx) + ' | '+ '{:>9.0f}'.format(rx) + ' | '+ '{:>7.0f}'.format(tx-rx) + ' | '+ '{:>7.0f}'.format(tot_drop) +' |  ')
551                         else:
552                                 log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
553                         first = 0
554                         if (duration <= 0):
555                                 #Stop generating
556                                 gensock.stop(gencores)
557                                 time.sleep(sleep_time)
558                                 lat_min, lat_max, lat_avg = gensock.lat_stats(latcores)
559                                 # Get statistics after some execution time
560                                 new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
561                                 if sutsock!='none':
562                                         new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
563                                 drop = new_drop-old_drop # drop is all packets dropped by all tasks. This includes packets dropped at the generator task + packets dropped by the nop task. In steady state, this equals to the number of packets received by this VM
564                                 rx = new_rx - old_rx     # rx is all packets received by the nop task = all packets received in the gen VM
565                                 tx = new_tx - old_tx     # tx is all generated packets actually accepted by the interface
566                                 tsc = new_tsc - old_tsc  # time difference between the 2 measurements, expressed in cycles.
567                                 tot_drop = tot_drop + tx - rx
568                                 if sutsock!='none':
569                                         sut_rx = new_sut_rx - old_sut_rx
570                                         sut_tx = new_sut_tx - old_sut_tx
571                                         sut_tsc = new_sut_tsc - old_sut_tsc
572                                 if pps_sut_tx_str <>  'NO_RESULTS':
573                                         log.info('|{:>4}'.format(size+4)+" |" + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps|'+'             |' +'             |'  +'             |'+ '             |'+ '{:>8.0f}'.format(lat_avg)+' us  |'+'{:>8.0f}'.format(lat_max)+' us  | ' + '{:>9.0f}'.format(tx) + ' | '+ '{:>9.0f}'.format(rx) + ' | '+ '{:>7.0f}'.format(tx-rx) + ' | '+ '{:>7.0f}'.format(tot_drop) +'  | ')
574                 log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
575
576 def run_irqtest(sock):
577         log.info("+----------------------------------------------------------------------------------------------------------------------------")
578         log.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic   ")
579         log.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and    ")
580         log.info("| that number expressed in us and so on. The numbers in the other rows show how many times per second, the program was       ")
581         log.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout   ")
582         log.info("| the duration of the test. This is to avoid rounding errors in the case of 0.0                                              ") 
583         log.info("+----------------------------------------------------------------------------------------------------------------------------")
584         sys.stdout.flush()
585         buckets=sock.show_irq_buckets(1)
586         print('Measurement ongoing ... ',end='\r')
587         sock.stop(irqcores)
588         old_irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)] 
589         irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
590         irq[0][0] = 'bucket us' 
591         for j,bucket in enumerate(buckets,start=1):
592                 irq[0][j] = '<'+ bucket
593         irq[0][-1] = '>'+ buckets [-2]
594         sock.start(irqcores)
595         time.sleep(2)
596         for j,bucket in enumerate(buckets,start=1):
597                 for i,irqcore in enumerate(irqcores,start=1):
598                         old_irq[i][j] = sock.irq_stats(irqcore,j-1)
599         time.sleep(float(runtime))
600         sock.stop(irqcores)
601         for i,irqcore in enumerate(irqcores,start=1):
602                 irq[i][0]='core %s '%irqcore
603                 for j,bucket in enumerate(buckets,start=1):
604                         diff =  sock.irq_stats(irqcore,j-1) - old_irq[i][j]
605                         if diff == 0:
606                                 irq[i][j] = '0'
607                         else:
608                                 irq[i][j] = str(round(diff/float(runtime), 2))
609         log.info('\n'.join([''.join(['{:>12}'.format(item) for item in row]) for row in irq]))
610
611 def run_impairtest(gensock,sutsock):
612         log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
613         log.info("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT via GW dropping and delaying packets. SUT sends packets back. Use ctrl-c to stop the test           |")
614         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
615         log.info("| Test   |  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Max. Latency  |  Packets Lost  | Loss Ratio |")
616         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
617         size=60
618         attempts = 0
619         gensock.set_size(gencores,0,size) # This is setting the frame size
620         gensock.set_value(gencores,0,16,(size-14),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
621         gensock.set_value(gencores,0,38,(size-34),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
622         # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
623         speed = STARTSPEED
624         gensock.speed(speed, gencores)
625         while True:
626                 attempts += 1
627                 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
628                 sys.stdout.flush()
629                 time.sleep(1)
630                 # Get statistics now that the generation is stable and NO ARP messages any more
631                 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
632                 drop_rate = 100.0*abs_dropped/abs_tx
633                 log.info('|{:>7}'.format(str(attempts))+" | " + '{:>5.1f}'.format(speed) + '% ' +'{:>6.3f}'.format(get_pps(speed,size)) + ' Mpps | '+ '{:>9.3f}'.format(pps_req_tx)+' Mpps | '+ '{:>9.3f}'.format(pps_tx) +' Mpps | ' + '{:>9}'.format(pps_sut_tx_str) +' Mpps | '+ '{:>9.3f}'.format(pps_rx)+' Mpps | '+ '{:>9.0f}'.format(lat_avg)+' us   | '+ '{:>9.0f}'.format(lat_max)+' us   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  |')
634
635 def init_test():
636 # Running at low speed to make sure the ARP messages can get through.
637 # If not doing this, the ARP message could be dropped by a switch in overload and then the test will not give proper results
638 # Note hoever that if we would run the test steps during a very long time, the ARP would expire in the switch.
639 # PROX will send a new ARP request every seconds so chances are very low that they will all fail to get through
640         socks[0].speed(0.01, gencores)
641         socks[0].start(genstatcores)
642         time.sleep(2)
643         socks[0].stop(gencores)
644
645 global sutstatcores
646 global genstatcores
647 global latcores
648 global gencores
649 global irqcores
650 global DROP_RATE_TRESHOLD
651 global LAT_AVG_TRESHOLD
652 global LAT_MAX_TRESHOLD
653 global ACCURACY
654 global STARTSPEED
655 global required_number_of_test_machines
656 clients =[]
657 socks =[]
658 vmDPIP =[]
659 vmAdminIP =[]
660 vmDPmac =[]
661 hexDPIP =[]
662 config_file =[]
663 script_control =[]
664
665 testconfig = ConfigParser.RawConfigParser()
666 testconfig.read(test+'.test')
667 required_number_of_test_machines = testconfig.get('DEFAULT', 'total_number_of_test_machines')
668 DROP_RATE_TRESHOLD = float(testconfig.get('DEFAULT', 'drop_rate_treshold'))
669 LAT_AVG_TRESHOLD = float(testconfig.get('DEFAULT', 'lat_avg_treshold'))
670 LAT_MAX_TRESHOLD = float(testconfig.get('DEFAULT', 'lat_max_treshold'))
671 ACCURACY = float(testconfig.get('DEFAULT', 'accuracy'))
672 STARTSPEED = float(testconfig.get('DEFAULT', 'startspeed'))
673 config = ConfigParser.RawConfigParser()
674 config.read(env+'.env')
675 key = config.get('OpenStack', 'key')
676 total_number_of_machines = config.get('rapid', 'total_number_of_machines')
677 if int(required_number_of_test_machines) > int(total_number_of_machines):
678         log.exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
679         raise Exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
680 for vm in range(1, int(total_number_of_machines)+1):
681         vmAdminIP.append(config.get('M%d'%vm, 'admin_ip'))
682         vmDPmac.append(config.get('M%d'%vm, 'dp_mac'))
683         vmDPIP.append(config.get('M%d'%vm, 'dp_ip'))
684         ip = vmDPIP[-1].split('.')
685         hexDPIP.append(hex(int(ip[0]))[2:].zfill(2) + ' ' + hex(int(ip[1]))[2:].zfill(2) + ' ' + hex(int(ip[2]))[2:].zfill(2) + ' ' + hex(int(ip[3]))[2:].zfill(2))
686 machine_index = []
687 for vm in range(1, int(required_number_of_test_machines)+1):
688         machine_index.append(int(testconfig.get('TestM%d'%vm, 'machine_index'))-1)
689 for vm in range(1, int(required_number_of_test_machines)+1):
690         config_file.append(testconfig.get('TestM%d'%vm, 'config_file'))
691         script_control.append(testconfig.get('TestM%d'%vm, 'script_control'))
692         group1cores=testconfig.get('TestM%d'%vm, 'group1cores')
693         if group1cores <> 'not_used':
694                 group1cores=ast.literal_eval(group1cores)
695         group2cores=testconfig.get('TestM%d'%vm, 'group2cores')
696         if group2cores <> 'not_used':
697                 group2cores=ast.literal_eval(group2cores)
698         group3cores=testconfig.get('TestM%d'%vm, 'group3cores')
699         if group3cores <> 'not_used':
700                 group3cores=ast.literal_eval(group3cores)
701         with open("parameters%d.lua"%vm, "w") as f:
702                 f.write('name="%s"\n'% testconfig.get('TestM%d'%vm, 'name'))
703                 f.write('local_ip="%s"\n'% vmDPIP[machine_index[vm-1]])
704                 f.write('local_hex_ip="%s"\n'% hexDPIP[machine_index[vm-1]])
705                 gwVM = testconfig.get('TestM%d'%vm, 'gw_vm')
706                 if gwVM <> 'not_used':
707                         gwVMindex = int(gwVM)-1
708                         f.write('gw_ip="%s"\n'% vmDPIP[machine_index[gwVMindex]])
709                         f.write('gw_hex_ip="%s"\n'% hexDPIP[machine_index[gwVMindex]])
710                 destVM = testconfig.get('TestM%d'%vm, 'dest_vm')
711                 if destVM <> 'not_used':
712                         destVMindex = int(destVM)-1
713                         f.write('dest_ip="%s"\n'% vmDPIP[machine_index[destVMindex]])
714                         f.write('dest_hex_ip="%s"\n'% hexDPIP[machine_index[destVMindex]])
715                         f.write('dest_hex_mac="%s"\n'% vmDPmac[machine_index[destVMindex]].replace(':',' '))
716                 if group1cores <> 'not_used':
717                         f.write('group1="%s"\n'% ','.join(map(str, group1cores)))
718                 if group2cores <> 'not_used':
719                         f.write('group2="%s"\n'% ','.join(map(str, group2cores)))
720                 if group3cores <> 'not_used':
721                         f.write('group3="%s"\n'% ','.join(map(str, group3cores)))
722         if re.match('(l2){0,1}gen.*\.cfg',config_file[-1]):
723                 gencores = group1cores
724                 latcores = group2cores
725                 genstatcores = group3cores
726         elif config_file[-1] == 'gen_gw.cfg':
727                 gencores = group1cores
728                 latcores = group2cores
729                 genstatcores = group3cores
730         elif  re.match('(l2){0,1}swap.*\.cfg',config_file[-1]):
731                 sutstatcores = group1cores
732         elif config_file[-1] == 'secgw2.cfg':
733                 sutstatcores = group1cores
734         elif config_file[-1] == 'irq.cfg':
735                 irqcores = group1cores
736         f.close
737 #####################################################################################
738 def exit_handler():
739         log.debug ('exit cleanup')
740         for sock in socks:
741                 sock.quit()
742         for client in clients:
743                 client.close()
744         sys.exit(0)
745
746 atexit.register(exit_handler)
747
748 for vm in range(0, int(required_number_of_test_machines)):
749         clients.append(prox_ctrl(vmAdminIP[machine_index[vm]], key+'.pem','root'))
750         connect_client(clients[-1])
751 # Creating script to bind the right network interface to the poll mode driver
752         devbindfile = "devbindvm%d.sh"%(vm+1)
753         with open("devbind.sh") as f:
754                 newText=f.read().replace('MACADDRESS', vmDPmac[machine_index[vm]])
755                 with open(devbindfile, "w") as f:
756                         f.write(newText)
757         st = os.stat(devbindfile)
758         os.chmod(devbindfile, st.st_mode | stat.S_IEXEC)
759         clients[-1].scp_put('./%s'%devbindfile, '/root/devbind.sh')
760         cmd = '/root/devbind.sh'
761         clients[-1].run_cmd(cmd)
762         log.debug("devbind.sh running on VM%d"%(vm+1))
763         clients[-1].scp_put('./%s'%config_file[vm], '/root/%s'%config_file[vm])
764         clients[-1].scp_put('./parameters%d.lua'%(vm+1), '/root/parameters.lua')
765         log.debug("Starting PROX on VM%d"%(vm+1))
766         if script_control[vm] == 'true':
767                 cmd = '/root/prox/build/prox -e -t -o cli -f /root/%s'%config_file[vm]
768         else:
769                 cmd = '/root/prox/build/prox -t -o cli -f /root/%s'%config_file[vm]
770         if configonly == False:
771                 clients[-1].fork_cmd(cmd, 'PROX Testing on TestM%d'%(vm+1))
772                 socks.append(connect_socket(clients[-1]))
773 if configonly:
774         sys.exit()
775 init_code = testconfig.get('DEFAULT', 'init_code')
776 if init_code <> 'not_used':
777         eval(init_code)
778 ####################################################
779 # Run test cases
780 # Best to run the flow test at the end since otherwise the tests coming after might be influenced by the big number of entries in the switch flow tables
781 ####################################################
782 number_of_tests = testconfig.get('DEFAULT', 'number_of_tests')
783 data_file = 'RUN' +env+'.'+test+'.csv'
784 data_csv_file = open(data_file,'w')
785 with data_csv_file:
786         fieldnames = ['flow','size','endspeed','endspeedpps','endpps_req_tx','endpps_tx','endpps_sut_tx_str','endpps_rx','endlat_avg','endlat_max','endabs_dropped','enddrop_rate']
787         writer = csv.DictWriter(data_csv_file, fieldnames=fieldnames)
788         writer.writeheader()
789         for vm in range(1, int(number_of_tests)+1):
790                 cmd=testconfig.get('test%d'%vm,'cmd')
791                 eval(cmd)
792 ####################################################