4 ## Copyright (c) 2010-2017 Intel Corporation
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
10 ## http://www.apache.org/licenses/LICENSE-2.0
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.
19 from __future__ import print_function
29 from logging.handlers import RotatingFileHandler
30 from logging import handlers
31 from prox_ctrl import prox_ctrl
38 env = "rapid" #Default string for environment
39 test = "basicrapid" #Default string for test
40 machine_map_file = "MachineMap" #Default string for machine map file
41 loglevel="DEBUG" # sets log level for writing to file
42 runtime=10 # time in seconds for 1 test run
43 configonly = False # IF True, the system will upload all the necessary config fiels to the VMs, but not start PROX and the actual testing
46 print("usage: runrapid [--version] [-v]")
47 print(" [--env ENVIRONMENT_NAME]")
48 print(" [--test TEST_NAME]")
49 print(" [--map MACHINE_MAP_FILE]")
50 print(" [--runtime TIME_FOR_TEST]")
51 print(" [--configonly False|True]")
52 print(" [--log DEBUG|INFO|WARNING|ERROR|CRITICAL]")
53 print(" [-h] [--help]")
55 print("Command-line interface to runrapid")
57 print("optional arguments:")
58 print(" -v, --version Show program's version number and exit")
59 print(" --env ENVIRONMENT_NAME Parameters will be read from ENVIRONMENT_NAME.env Default is %s."%env)
60 print(" --test TEST_NAME Test cases will be read from TEST_NAME.test Default is %s."%test)
61 print(" --map MACHINE_MAP_FILE Machine mapping will be read from MACHINE_MAP_FILE.cfg Default is %s."%machine_map_file)
62 print(" --runtime Specify time in seconds for 1 test run")
63 print(" --configonly If True, only upload all config files to the VMs, do not run the tests. Default is %s."%configonly)
64 print(" --log Specify logging level for log file output, screen output level is hard coded")
65 print(" -h, --help Show help message and exit.")
69 opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "env=", "test=", "map=", "runtime=","configonly=","log="])
70 except getopt.GetoptError as err:
71 print("===========================================")
73 print("===========================================")
80 if opt in ("-h", "--help"):
83 if opt in ("-v", "--version"):
84 print("Rapid Automated Performance Indication for Dataplane "+version)
88 print ("Using '"+env+"' as name for the environment")
91 print ("Using '"+test+".test' for test case definition")
93 machine_map_file = arg
94 print ("Using '"+machine_map_file+".cfg' for machine mapping")
95 if opt in ("--runtime"):
97 print ("Runtime: "+ runtime)
98 if opt in ("--configonly"):
100 print ("configonly: "+ configonly)
103 print ("Log level: "+ loglevel)
113 UNDERLINE = '\033[4m'
116 screen_formatter = logging.Formatter("%(message)s")
117 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
119 # get a top-level logger,
121 # BUT PREVENT IT from propagating messages to the root logger
123 log = logging.getLogger()
124 numeric_level = getattr(logging, loglevel.upper(), None)
125 if not isinstance(numeric_level, int):
126 raise ValueError('Invalid log level: %s' % loglevel)
127 log.setLevel(numeric_level)
130 # create a console handler
131 # and set its log level to the command-line option
133 console_handler = logging.StreamHandler(sys.stdout)
134 console_handler.setLevel(logging.INFO)
135 console_handler.setFormatter(screen_formatter)
137 # create a file handler
138 # and set its log level to DEBUG
140 log_file = 'RUN{}.{}.log'.format(env,test)
141 file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
142 #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
143 file_handler.setLevel(numeric_level)
144 file_handler.setFormatter(file_formatter)
146 # add handlers to the logger
148 log.addHandler(file_handler)
149 log.addHandler(console_handler)
151 # Check if log exists and should therefore be rolled
152 needRoll = os.path.isfile(log_file)
155 # This is a stale log, so roll it
158 log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
160 # Roll over on application start
161 log.handlers[0].doRollover()
164 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
166 log.debug("runrapid.py version: "+version)
167 #========================================================================
168 def connect_socket(client):
170 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
173 sock = client.prox_sock()
178 log.exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
179 raise Exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
181 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
182 log.info("Connected to PROX on %s" % client.ip())
185 def connect_client(client):
187 log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
192 except RuntimeWarning, ex:
195 log.exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
196 raise Exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
198 log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
199 log.debug("Connected to VM on %s" % client.ip())
201 def run_iteration(gensock,sutsock):
203 # 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
204 time.sleep(sleep_time)
205 abs_old_rx, abs_old_tx, abs_old_drop, abs_old_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
206 gensock.start(gencores)
207 time.sleep(sleep_time)
209 old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
210 old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
211 # Measure latency statistics per second
217 while n_loops < float(runtime):
220 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample = gensock.lat_stats(latcores)
221 if lat_min > lat_min_sample:
222 lat_min = lat_min_sample
223 if lat_max < lat_max_sample:
224 lat_max = lat_max_sample
225 lat_avg = lat_avg + lat_avg_sample
226 used_avg = used_avg + used_sample
227 lat_avg = lat_avg / n_loops
228 used_avg = used_avg / n_loops
229 # Get statistics after some execution time
230 new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
232 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
234 gensock.stop(gencores)
235 time.sleep(sleep_time)
236 abs_new_rx, abs_new_tx, abs_new_drop, abs_new_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
237 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
238 rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM
239 tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface
240 abs_dropped = (abs_new_tx - abs_old_tx) - (abs_new_rx - abs_old_rx)
241 tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles.
242 pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
243 pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
244 pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
246 sut_rx = new_sut_rx - old_sut_rx
247 sut_tx = new_sut_tx - old_sut_tx
248 sut_tsc = new_sut_tsc - old_sut_tsc
249 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
250 pps_sut_tx_str = '{:>9.3f}'.format(pps_sut_tx)
253 pps_sut_tx_str = 'NO MEAS.'
255 log.critical("TX = 0. Test interrupted since no packet has been sent.")
256 raise Exception("TX = 0")
257 return(pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max,abs_dropped,(abs_new_tx - abs_old_tx),lat_min,used_avg)
259 def new_speed(speed,minspeed,maxspeed,success):
264 newspeed = (maxspeed+minspeed)/2.0
265 return (newspeed,minspeed,maxspeed)
267 def get_pps(speed,size):
268 return (speed * 100.0 / (8*(size+24)))
270 def run_speedtest(gensock,sutsock):
271 maxspeed = speed = STARTSPEED
275 log.info("+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
276 log.info("| Generator is sending UDP (1 flow) packets ("+ '{:>5}'.format(size+4) +" bytes) to SUT. SUT sends packets back |")
277 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
278 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 |")
279 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
280 endpps_sut_tx_str = 'NO_RESULTS'
281 gensock.set_size(gencores,0,size) # This is setting the frame size
282 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)
283 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)
284 # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
285 gensock.start(latcores)
286 while (maxspeed-minspeed > ACCURACY):
288 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
290 # Start generating packets at requested speed (in % of a 10Gb/s link)
291 gensock.speed(speed / len(gencores), gencores)
293 # Get statistics now that the generation is stable and initial ARP messages are dealt with.
294 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx, lat_min, lat_used = run_iteration(gensock,sutsock)
295 drop_rate = 100.0*abs_dropped/abs_tx
297 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
300 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):
301 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 |'+lat_warning)
303 endpps_req_tx = pps_req_tx
305 endpps_sut_tx_str = pps_sut_tx_str
309 endabs_dropped = abs_dropped
310 enddrop_rate = drop_rate
311 endwarning = lat_warning
314 abs_drop_rate_prefix = bcolors.ENDC
315 if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
316 abs_drop_rate_prefix = bcolors.FAIL
317 if (drop_rate < DROP_RATE_TRESHOLD):
318 drop_rate_prefix = bcolors.ENDC
320 drop_rate_prefix = bcolors.FAIL
321 if (lat_avg< LAT_AVG_TRESHOLD):
322 lat_avg_prefix = bcolors.ENDC
324 lat_avg_prefix = bcolors.FAIL
325 if (lat_max< LAT_MAX_TRESHOLD):
326 lat_max_prefix = bcolors.ENDC
328 lat_max_prefix = bcolors.FAIL
329 if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
330 speed_prefix = bcolors.ENDC
332 speed_prefix = bcolors.FAIL
333 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 |'+lat_warning)
335 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
336 if endpps_sut_tx_str <> 'NO_RESULTS':
337 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
338 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 |'+endwarning)
339 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
340 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})
342 log.info('| Speed 0 or close to 0')
343 gensock.stop(latcores)
345 def run_flowtest(gensock,sutsock):
347 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
348 log.info("| UDP, "+ '{:>5}'.format(size+4) +" bytes, different number of flows by randomizing SRC & DST UDP port |")
349 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
350 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 |")
351 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
352 # 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.
354 1: ['1000000000000000','1000000000000000'],\
355 2: ['1000000000000000','100000000000000X'],\
356 4: ['100000000000000X','100000000000000X'],\
357 8: ['100000000000000X','10000000000000XX'],\
358 16: ['10000000000000XX','10000000000000XX'],\
359 32: ['10000000000000XX','1000000000000XXX'],\
360 64: ['1000000000000XXX','1000000000000XXX'],\
361 128: ['1000000000000XXX','100000000000XXXX'],\
362 256: ['100000000000XXXX','100000000000XXXX'],\
363 512: ['100000000000XXXX','10000000000XXXXX'],\
364 1024: ['10000000000XXXXX','10000000000XXXXX'],\
365 2048: ['10000000000XXXXX','1000000000XXXXXX'],\
366 4096: ['1000000000XXXXXX','1000000000XXXXXX'],\
367 8192: ['1000000000XXXXXX','100000000XXXXXXX'],\
368 16384: ['100000000XXXXXXX','100000000XXXXXXX'],\
369 32768: ['100000000XXXXXXX','10000000XXXXXXXX'],\
370 65535: ['10000000XXXXXXXX','10000000XXXXXXXX'],\
371 131072: ['10000000XXXXXXXX','1000000XXXXXXXXX'],\
372 262144: ['1000000XXXXXXXXX','1000000XXXXXXXXX'],\
373 524280: ['1000000XXXXXXXXX','100000XXXXXXXXXX'],\
374 1048576:['100000XXXXXXXXXX','100000XXXXXXXXXX'],}
375 gensock.set_size(gencores,0,size) # This is setting the frame size
376 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)
377 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)
378 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
379 gensock.start(latcores)
380 for flow_number in flow_size_list:
382 gensock.reset_stats()
384 sutsock.reset_stats()
385 source_port,destination_port = flows[flow_number]
386 gensock.set_random(gencores,0,34,source_port,2)
387 gensock.set_random(gencores,0,36,destination_port,2)
388 endpps_sut_tx_str = 'NO_RESULTS'
389 maxspeed = speed = STARTSPEED
391 while (maxspeed-minspeed > ACCURACY):
393 print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
395 # Start generating packets at requested speed (in % of a 10Gb/s link)
396 gensock.speed(speed / len(gencores), gencores)
398 # Get statistics now that the generation is stable and initial ARP messages are dealt with
399 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx, lat_min, lat_used = run_iteration(gensock,sutsock)
400 drop_rate = 100.0*abs_dropped/abs_tx
402 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
405 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):
406 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 |'+lat_warning)
408 endpps_req_tx = pps_req_tx
410 endpps_sut_tx_str = pps_sut_tx_str
414 endabs_dropped = abs_dropped
415 enddrop_rate = drop_rate
416 endwarning = lat_warning
419 abs_drop_rate_prefix = bcolors.ENDC
420 if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
421 abs_drop_rate_prefix = bcolors.FAIL
422 if (drop_rate < DROP_RATE_TRESHOLD):
423 drop_rate_prefix = bcolors.ENDC
425 drop_rate_prefix = bcolors.FAIL
426 if (lat_avg< LAT_AVG_TRESHOLD):
427 lat_avg_prefix = bcolors.ENDC
429 lat_avg_prefix = bcolors.FAIL
430 if (lat_max< LAT_MAX_TRESHOLD):
431 lat_max_prefix = bcolors.ENDC
433 lat_max_prefix = bcolors.FAIL
434 if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
435 speed_prefix = bcolors.ENDC
437 speed_prefix = bcolors.FAIL
438 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 |'+lat_warning)
440 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
441 if endpps_sut_tx_str <> 'NO_RESULTS':
442 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)+ '% |'+endwarning)
443 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
444 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})
446 log.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
447 gensock.stop(latcores)
449 def run_sizetest(gensock,sutsock):
450 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
451 log.info("| UDP, 1 flow, different packet sizes |")
452 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
453 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 |")
454 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
455 gensock.start(latcores)
456 for size in packet_size_list:
459 gensock.reset_stats()
461 sutsock.reset_stats()
462 gensock.set_size(gencores,0,size) # This is setting the frame size
463 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)
464 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)
465 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
466 endpps_sut_tx_str = 'NO_RESULTS'
467 maxspeed = speed = STARTSPEED
469 while (maxspeed-minspeed > ACCURACY):
471 print(str(size+4)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
473 # Start generating packets at requested speed (in % of a 10Gb/s link)
474 gensock.speed(speed / len(gencores), gencores)
475 # Get statistics now that the generation is stable and initial ARP messages are dealt with
476 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx, lat_min, lat_used = run_iteration(gensock,sutsock)
477 drop_rate = 100.0*abs_dropped/abs_tx
479 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
482 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):
483 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 |'+lat_warning)
485 endpps_req_tx = pps_req_tx
487 endpps_sut_tx_str = pps_sut_tx_str
491 endabs_dropped = abs_dropped
492 enddrop_rate = drop_rate
493 endwarning = lat_warning
496 abs_drop_rate_prefix = bcolors.ENDC
497 if ((abs_dropped>0) and (DROP_RATE_TRESHOLD ==0)):
498 abs_drop_rate_prefix = bcolors.FAIL
499 if (drop_rate < DROP_RATE_TRESHOLD):
500 drop_rate_prefix = bcolors.ENDC
502 drop_rate_prefix = bcolors.FAIL
503 if (lat_avg< LAT_AVG_TRESHOLD):
504 lat_avg_prefix = bcolors.ENDC
506 lat_avg_prefix = bcolors.FAIL
507 if (lat_max< LAT_MAX_TRESHOLD):
508 lat_max_prefix = bcolors.ENDC
510 lat_max_prefix = bcolors.FAIL
511 if (((get_pps(speed,size) - pps_tx)/get_pps(speed,size))<0.001):
512 speed_prefix = bcolors.ENDC
514 speed_prefix = bcolors.FAIL
515 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 |'+ lat_warning)
517 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
518 if endpps_sut_tx_str <> 'NO_RESULTS':
519 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)+ '% |'+ endwarning)
520 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
521 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})
523 log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
524 gensock.stop(latcores)
526 def run_max_frame_rate(gensock,sutsock):
527 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------+")
528 log.info("| UDP, 1 flow, different packet sizes |")
529 log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
530 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 |")
531 log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
533 gensock.start(latcores)
534 for size in packet_size_list:
535 # 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
536 time.sleep(sleep_time)
538 gensock.reset_stats()
540 sutsock.reset_stats()
541 gensock.set_size(gencores,0,size) # This is setting the frame size
542 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)
543 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)
544 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
545 pps_sut_tx_str = 'NO_RESULTS'
547 # Start generating packets at requested speed (in % of a 10Gb/s link)
548 gensock.speed(speed / len(gencores), gencores)
549 duration = float(runtime)
553 old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
554 old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
555 gensock.start(gencores)
556 while (duration > 0):
558 lat_min, lat_max, lat_avg, lat_used = gensock.lat_stats(latcores)
560 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
563 # Get statistics after some execution time
564 new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
566 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
567 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
568 rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM
569 tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface
570 tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles.
574 sut_rx = new_sut_rx - old_sut_rx
575 sut_tx = new_sut_tx - old_sut_tx
576 sut_tsc = new_sut_tsc - old_sut_tsc
579 duration = duration - 1
584 pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
585 pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
586 pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
588 old_sut_tx = new_sut_tx
589 old_sut_rx = new_sut_rx
590 old_sut_tsc= new_sut_tsc
591 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
592 pps_sut_tx_str = '{:>7.3f}'.format(pps_sut_tx)
595 pps_sut_tx_str = 'NO MEAS.'
597 log.critical("TX = 0. Test interrupted since no packet has been sent.")
598 raise Exception("TX = 0")
599 tot_drop = tot_drop + tx - rx
601 if pps_sut_tx_str <> 'NO_RESULTS':
602 # First second mpps are not valid as there is no alignement between time the generator is started and per seconds stats
604 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) +' |'+lat_warning)
606 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) +' |'+lat_warning)
608 log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
612 gensock.stop(gencores)
613 time.sleep(sleep_time)
614 lat_min, lat_max, lat_avg, lat_used = gensock.lat_stats(latcores)
616 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
619 # Get statistics after some execution time
620 new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
622 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
623 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
624 rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM
625 tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface
626 tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles.
627 tot_drop = tot_drop + tx - rx
629 sut_rx = new_sut_rx - old_sut_rx
630 sut_tx = new_sut_tx - old_sut_tx
631 sut_tsc = new_sut_tsc - old_sut_tsc
632 if pps_sut_tx_str <> 'NO_RESULTS':
633 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) +' |'+lat_warning)
634 log.info("+-----+------------------+-------------+-------------+-------------+-------------+-------------+-------------+-----------+-----------+---------+------------+")
635 gensock.stop(latcores)
639 def run_irqtest(sock):
640 log.info("+----------------------------------------------------------------------------------------------------------------------------")
641 log.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic ")
642 log.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and ")
643 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 ")
644 log.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout ")
645 log.info("| the duration of the test. This is to avoid rounding errors in the case of 0.0 ")
646 log.info("+----------------------------------------------------------------------------------------------------------------------------")
648 buckets=sock.show_irq_buckets(1)
649 print('Measurement ongoing ... ',end='\r')
651 old_irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
652 irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
653 irq[0][0] = 'bucket us'
654 for j,bucket in enumerate(buckets,start=1):
655 irq[0][j] = '<'+ bucket
656 irq[0][-1] = '>'+ buckets [-2]
659 for j,bucket in enumerate(buckets,start=1):
660 for i,irqcore in enumerate(irqcores,start=1):
661 old_irq[i][j] = sock.irq_stats(irqcore,j-1)
662 time.sleep(float(runtime))
664 for i,irqcore in enumerate(irqcores,start=1):
665 irq[i][0]='core %s '%irqcore
666 for j,bucket in enumerate(buckets,start=1):
667 diff = sock.irq_stats(irqcore,j-1) - old_irq[i][j]
671 irq[i][j] = str(round(diff/float(runtime), 2))
673 log.info(''.join(['{:>12}'.format(item) for item in row]))
674 # log.info('\n'.join([''.join(['{:>12}'.format(item) for item in row]) for row in irq]))
676 def run_impairtest(gensock,sutsock):
678 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
679 log.info("| Generator is sending UDP (1 flow) packets ("+ '{:>5}'.format(size+4) +" bytes) to SUT via GW dropping and delaying packets. SUT sends packets back. Use ctrl-c to stop the test |")
680 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
681 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 |")
682 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
684 gensock.set_size(gencores,0,size) # This is setting the frame size
685 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)
686 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)
687 # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
688 gensock.start(latcores)
690 gensock.speed(speed / len(gencores), gencores)
693 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
696 # Get statistics now that the generation is stable and NO ARP messages any more
697 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx, lat_min, lat_used = run_iteration(gensock,sutsock)
698 drop_rate = 100.0*abs_dropped/abs_tx
700 lat_warning = bcolors.FAIL + ' Potential latency accuracy problem: {:>3.0f}%'.format(lat_used*100) + bcolors.ENDC
703 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)+ '% |'+lat_warning)
704 writer.writerow({'flow':'1','size':(size+4),'endspeed':speed,'endspeedpps':get_pps(speed,size),'endpps_req_tx':pps_req_tx,'endpps_tx':pps_tx,'endpps_sut_tx_str':pps_sut_tx_str,'endpps_rx':pps_rx,'endlat_avg':lat_avg,'endlat_max':lat_max,'endabs_dropped':abs_dropped,'enddrop_rate':drop_rate})
705 gensock.stop(latcores)
707 def run_inittest(gensock):
708 # Running at low speed to make sure the ARP messages can get through.
709 # If not doing this, the ARP message could be dropped by a switch in overload and then the test will not give proper results
710 # Note hoever that if we would run the test steps during a very long time, the ARP would expire in the switch.
711 # PROX will send a new ARP request every seconds so chances are very low that they will all fail to get through
712 gensock.speed(0.01 / len(gencores), gencores)
713 gensock.start(genstatcores)
715 gensock.stop(genstatcores)
723 global packet_size_list
724 global flow_size_list
725 global required_number_of_test_machines
740 data_file = 'RUN{}.{}.csv'.format(env,test)
741 data_csv_file = open(data_file,'w')
742 testconfig = ConfigParser.RawConfigParser()
743 testconfig.read(test+'.test')
744 required_number_of_test_machines = testconfig.get('DEFAULT', 'total_number_of_test_machines')
745 config = ConfigParser.RawConfigParser()
746 config.read(env+'.env')
747 machine_map = ConfigParser.RawConfigParser()
748 machine_map.read(machine_map_file +'.cfg')
749 key = config.get('OpenStack', 'key')
750 total_number_of_machines = config.get('rapid', 'total_number_of_machines')
751 if int(required_number_of_test_machines) > int(total_number_of_machines):
752 log.exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
753 raise Exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
754 for vm in range(1, int(total_number_of_machines)+1):
755 vmAdminIP.append(config.get('M%d'%vm, 'admin_ip'))
756 vmDPmac.append(config.get('M%d'%vm, 'dp_mac'))
757 vmDPIP.append(config.get('M%d'%vm, 'dp_ip'))
758 ip = vmDPIP[-1].split('.')
759 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))
761 for vm in range(1, int(required_number_of_test_machines)+1):
762 machine_index.append(int(machine_map.get('TestM%d'%vm, 'machine_index'))-1)
763 prox_socket.append(testconfig.getboolean('TestM%d'%vm, 'prox_socket'))
764 for vm in range(1, int(required_number_of_test_machines)+1):
765 if prox_socket[vm-1]:
766 prox_launch_exit.append(testconfig.getboolean('TestM%d'%vm, 'prox_launch_exit'))
767 config_file.append(testconfig.get('TestM%d'%vm, 'config_file'))
768 with open('{}_{}_parameters{}.lua'.format(env,test,vm), "w") as f:
769 f.write('name="%s"\n'% testconfig.get('TestM%d'%vm, 'name'))
770 f.write('local_ip="%s"\n'% vmDPIP[machine_index[vm-1]])
771 f.write('local_hex_ip="%s"\n'% hexDPIP[machine_index[vm-1]])
772 if re.match('(l2){0,1}gen\.cfg',config_file[-1]):
773 gencores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'gencores'))
774 latcores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'latcores'))
775 STARTSPEED = float(testconfig.get('TestM%d'%vm, 'startspeed'))
776 genstatcores = gencores + latcores
777 auto_start.append(False)
778 mach_type.append('gen')
779 f.write('gencores="%s"\n'% ','.join(map(str, gencores)))
780 f.write('latcores="%s"\n'% ','.join(map(str, latcores)))
781 destVMindex = int(testconfig.get('TestM%d'%vm, 'dest_vm'))-1
782 f.write('dest_ip="%s"\n'% vmDPIP[machine_index[destVMindex]])
783 f.write('dest_hex_ip="%s"\n'% hexDPIP[machine_index[destVMindex]])
784 f.write('dest_hex_mac="%s"\n'% vmDPmac[machine_index[destVMindex]].replace(':',' '))
785 elif re.match('(l2){0,1}gen_gw\.cfg',config_file[-1]):
786 gencores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'gencores'))
787 latcores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'latcores'))
788 STARTSPEED = float(testconfig.get('TestM%d'%vm, 'startspeed'))
789 genstatcores = gencores + latcores
790 auto_start.append(False)
791 mach_type.append('gen')
792 f.write('gencores="%s"\n'% ','.join(map(str, gencores)))
793 f.write('latcores="%s"\n'% ','.join(map(str, latcores)))
794 gwVMindex = int(testconfig.get('TestM%d'%vm, 'gw_vm')) -1
795 f.write('gw_ip="%s"\n'% vmDPIP[machine_index[gwVMindex]])
796 f.write('gw_hex_ip="%s"\n'% hexDPIP[machine_index[gwVMindex]])
797 destVMindex = int(testconfig.get('TestM%d'%vm, 'dest_vm'))-1
798 f.write('dest_ip="%s"\n'% vmDPIP[machine_index[destVMindex]])
799 f.write('dest_hex_ip="%s"\n'% hexDPIP[machine_index[destVMindex]])
800 f.write('dest_hex_mac="%s"\n'% vmDPmac[machine_index[destVMindex]].replace(':',' '))
801 elif re.match('(l2){0,1}swap.*\.cfg',config_file[-1]):
802 sutstatcores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'swapcores'))
803 auto_start.append(True)
804 mach_type.append('sut')
805 f.write('swapcores="%s"\n'% ','.join(map(str, sutstatcores)))
806 elif config_file[-1] == 'secgw1.cfg':
807 auto_start.append(True)
808 mach_type.append('none')
809 f.write('secgwcores="%s"\n'% ','.join(map(str, ast.literal_eval(testconfig.get('TestM%d'%vm, 'secgwcores')))))
810 destVMindex = int(testconfig.get('TestM%d'%vm, 'dest_vm'))-1
811 f.write('dest_ip="%s"\n'% vmDPIP[machine_index[destVMindex]])
812 f.write('dest_hex_ip="%s"\n'% hexDPIP[machine_index[destVMindex]])
813 f.write('dest_hex_mac="%s"\n'% vmDPmac[machine_index[destVMindex]].replace(':',' '))
814 elif config_file[-1] == 'secgw2.cfg':
815 sutstatcores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'secgwcores'))
816 auto_start.append(True)
817 mach_type.append('sut')
818 f.write('secgwcores="%s"\n'% ','.join(map(str, sutstatcores)))
819 elif config_file[-1] == 'impair.cfg':
820 auto_start.append(True)
821 mach_type.append('none')
822 f.write('impaircores="%s"\n'% ','.join(map(str, ast.literal_eval(testconfig.get('TestM%d'%vm, 'impaircores')))))
823 elif config_file[-1] == 'irq.cfg':
824 irqcores = ast.literal_eval(testconfig.get('TestM%d'%vm, 'irqcores'))
825 auto_start.append(False)
826 mach_type.append('irq')
827 f.write('irqcores="%s"\n'% ','.join(map(str, irqcores)))
829 #####################################################################################
831 log.debug ('exit cleanup')
832 for index, sock in enumerate(socks):
833 if socks_control[index]:
835 for client in clients:
840 atexit.register(exit_handler)
842 for vm in range(0, int(required_number_of_test_machines)):
844 clients.append(prox_ctrl(vmAdminIP[machine_index[vm]], key+'.pem','root'))
845 connect_client(clients[-1])
846 # Creating script to bind the right network interface to the poll mode driver
847 devbindfile = '{}_{}_devbindvm{}.sh'.format(env,test, vm+1)
848 with open("devbind.sh") as f:
849 newText=f.read().replace('MACADDRESS', vmDPmac[machine_index[vm]])
850 with open(devbindfile, "w") as f:
852 st = os.stat(devbindfile)
853 os.chmod(devbindfile, st.st_mode | stat.S_IEXEC)
854 clients[-1].scp_put('./%s'%devbindfile, '/root/devbind.sh')
855 cmd = '/root/devbind.sh'
856 clients[-1].run_cmd(cmd)
857 log.debug("devbind.sh running on VM%d"%(vm+1))
858 clients[-1].scp_put('./%s'%config_file[vm], '/root/%s'%config_file[vm])
859 clients[-1].scp_put('./{}_{}_parameters{}.lua'.format(env,test, vm+1), '/root/parameters.lua')
861 if prox_launch_exit[vm]:
862 log.debug("Starting PROX on VM%d"%(vm+1))
864 cmd = '/root/prox/build/prox -t -o cli -f /root/%s'%config_file[vm]
866 cmd = '/root/prox/build/prox -e -t -o cli -f /root/%s'%config_file[vm]
867 clients[-1].fork_cmd(cmd, 'PROX Testing on TestM%d'%(vm+1))
868 socks_control.append(prox_launch_exit[vm])
869 socks.append(connect_socket(clients[-1]))
870 sock_type.append(mach_type[vm])
872 socks_control.append(False)
874 def get_BinarySearchParams() :
875 global DROP_RATE_TRESHOLD
876 global LAT_AVG_TRESHOLD
877 global LAT_MAX_TRESHOLD
880 DROP_RATE_TRESHOLD = float(testconfig.get('BinarySearchParams', 'drop_rate_threshold'))
881 LAT_AVG_TRESHOLD = float(testconfig.get('BinarySearchParams', 'lat_avg_threshold'))
882 LAT_MAX_TRESHOLD = float(testconfig.get('BinarySearchParams', 'lat_max_threshold'))
883 ACCURACY = float(testconfig.get('BinarySearchParams', 'accuracy'))
887 ####################################################
889 # 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
890 ####################################################
891 gensock_index = sock_type.index('gen') if 'gen' in sock_type else -1
892 sutsock_index = sock_type.index('sut') if 'sut' in sock_type else -1
893 irqsock_index = sock_type.index('irq') if 'irq' in sock_type else -1
894 number_of_tests = testconfig.get('DEFAULT', 'number_of_tests')
896 fieldnames = ['flow','size','endspeed','endspeedpps','endpps_req_tx','endpps_tx','endpps_sut_tx_str','endpps_rx','endlat_avg','endlat_max','endabs_dropped','enddrop_rate']
897 writer = csv.DictWriter(data_csv_file, fieldnames=fieldnames)
899 for test_nr in range(1, int(number_of_tests)+1):
900 test=testconfig.get('test%d'%test_nr,'test')
902 if test == 'speedtest':
903 get_BinarySearchParams()
904 PACKETSIZE = int(testconfig.get('test%d'%test_nr, 'packetsize'))
905 run_speedtest(socks[gensock_index],socks[sutsock_index])
906 elif test == 'flowtest':
907 get_BinarySearchParams()
908 PACKETSIZE = int(testconfig.get('test%d'%test_nr, 'packetsize'))
909 flow_size_list = ast.literal_eval(testconfig.get('test%d'%test_nr, 'flows'))
910 run_flowtest(socks[gensock_index],socks[sutsock_index])
911 elif test == 'sizetest':
912 get_BinarySearchParams()
913 packet_size_list = ast.literal_eval(testconfig.get('test%d'%test_nr, 'packetsizes'))
914 run_sizetest(socks[gensock_index],socks[sutsock_index])
915 elif test == 'max_frame_rate':
916 # PACKETSIZE = int(testconfig.get('test%d'%test_nr, 'packetsize'))
917 packet_size_list = ast.literal_eval(testconfig.get('test%d'%test_nr, 'packetsizes'))
918 run_max_frame_rate(socks[gensock_index],socks[sutsock_index])
919 elif test == 'impairtest':
920 get_BinarySearchParams()
921 PACKETSIZE = int(testconfig.get('test%d'%test_nr, 'packetsize'))
922 run_impairtest(socks[gensock_index],socks[sutsock_index])
923 elif test == 'irqtest':
924 run_irqtest(socks[irqsock_index])
925 elif test == 'inittest':
926 run_inittest(socks[gensock_index])
927 ####################################################