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
37 env = "rapid" #Default string for environment
38 test = "basicrapid" #Default string for test
39 loglevel="DEBUG" # sets log level for writing to file
40 runtime=10 # time in seconds for 1 test run
41 configonly = False # IF True, the system will upload all the necessary config fiels to the VMs, but not start PROX and the actual testing
44 print("usage: runrapid [--version] [-v]")
45 print(" [--env ENVIRONMENT_NAME]")
46 print(" [--test TEST_NAME]")
47 print(" [--runtime TIME_FOR_TEST]")
48 print(" [--configonly False|True]")
49 print(" [--log DEBUG|INFO|WARNING|ERROR|CRITICAL]")
50 print(" [-h] [--help]")
52 print("Command-line interface to runrapid")
54 print("optional arguments:")
55 print(" -v, --version Show program's version number and exit")
56 print(" --env ENVIRONMENT_NAME Parameters will be read from ENVIRONMENT_NAME.env Default is %s."%env)
57 print(" --test TEST_NAME Test cases will be read from TEST_NAME.test Default is %s."%test)
58 print(" --runtime Specify time in seconds for 1 test run")
59 print(" --configonly If True, only upload all config files to the VMs, do not run the tests. Default is %s."%configonly)
60 print(" --log Specify logging level for log file output, screen output level is hard coded")
61 print(" -h, --help Show help message and exit.")
65 opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "env=", "test=","runtime=","configonly=","log="])
66 except getopt.GetoptError as err:
67 print("===========================================")
69 print("===========================================")
76 if opt in ("-h", "--help"):
79 if opt in ("-v", "--version"):
80 print("Rapid Automated Performance Indication for Dataplane "+version)
84 print ("Using '"+env+"' as name for the environment")
87 print ("Using '"+test+".test' for test case definition")
88 if opt in ("--runtime"):
90 print ("Runtime: "+ runtime)
91 if opt in ("--configonly"):
93 print ("configonly: "+ configonly)
96 print ("Log level: "+ loglevel)
100 screen_formatter = logging.Formatter("%(message)s")
101 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
103 # get a top-level logger,
105 # BUT PREVENT IT from propagating messages to the root logger
107 log = logging.getLogger()
108 numeric_level = getattr(logging, loglevel.upper(), None)
109 if not isinstance(numeric_level, int):
110 raise ValueError('Invalid log level: %s' % loglevel)
111 log.setLevel(numeric_level)
114 # create a console handler
115 # and set its log level to the command-line option
117 console_handler = logging.StreamHandler(sys.stdout)
118 console_handler.setLevel(logging.INFO)
119 console_handler.setFormatter(screen_formatter)
121 # create a file handler
122 # and set its log level to DEBUG
124 log_file = 'RUN' +env+'.'+test+'.log'
125 file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
126 #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
127 file_handler.setLevel(numeric_level)
128 file_handler.setFormatter(file_formatter)
130 # add handlers to the logger
132 log.addHandler(file_handler)
133 log.addHandler(console_handler)
135 # Check if log exists and should therefore be rolled
136 needRoll = os.path.isfile(log_file)
139 # This is a stale log, so roll it
142 log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
144 # Roll over on application start
145 log.handlers[0].doRollover()
148 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
150 log.debug("runrapid.py version: "+version)
151 #========================================================================
152 def connect_socket(client):
154 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
157 sock = client.prox_sock()
162 log.exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
163 raise Exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
165 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
166 log.info("Connected to PROX on %s" % client.ip())
169 def connect_client(client):
171 log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
176 except RuntimeWarning, ex:
179 log.exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
180 raise Exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
182 log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
183 log.debug("Connected to VM on %s" % client.ip())
185 def run_iteration(gensock,sutsock):
187 # 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
188 time.sleep(sleep_time)
189 abs_old_rx, abs_old_tx, abs_old_drop, abs_old_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
190 gensock.start(gencores)
191 time.sleep(sleep_time)
193 old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
194 old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
195 time.sleep(float(runtime))
196 lat_min, lat_max, lat_avg = gensock.lat_stats(latcores)
197 # Get statistics after some execution time
198 new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
200 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
202 gensock.stop(gencores)
203 time.sleep(sleep_time)
204 abs_new_rx, abs_new_tx, abs_new_drop, abs_new_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
205 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
206 rx = new_rx - old_rx # rx is all packets received by the nop task = all packets received in the gen VM
207 tx = new_tx - old_tx # tx is all generated packets actually accepted by the interface
208 abs_dropped = (abs_new_tx - abs_old_tx) - (abs_new_rx - abs_old_rx)
209 tsc = new_tsc - old_tsc # time difference between the 2 measurements, expressed in cycles.
210 pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
211 pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
212 pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
214 sut_rx = new_sut_rx - old_sut_rx
215 sut_tx = new_sut_tx - old_sut_tx
216 sut_tsc = new_sut_tsc - old_sut_tsc
217 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
218 pps_sut_tx_str = '{:>9.3f}'.format(pps_sut_tx)
221 pps_sut_tx_str = 'NO MEAS.'
223 log.critical("TX = 0. Test interrupted since no packet has been sent.")
224 raise Exception("TX = 0")
225 return(pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max,abs_dropped,(abs_new_tx - abs_old_tx))
227 def new_speed(speed,minspeed,maxspeed,success):
228 # Following calculates the ratio for the new speed to be applied
229 # On the Y axis, we will find the ratio, a number between 0 and 1
230 # On the x axis, we find the % of dropped packets, a number between 0 and 100
231 # 2 lines are drawn and we take the minumun of these lines to calculate the ratio
232 # One line goes through (0,y0) and (p,q)
233 # The second line goes through (p,q) and (100,y100)
238 # ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100))
239 # return (int(speed*ratio*100)+0.5)/100.0
244 newspeed = (maxspeed+minspeed)/2.0
245 return (newspeed,minspeed,maxspeed)
247 def get_pps(speed,size):
248 return (speed * 100.0 / (8*(size+24)))
250 def run_speedtest(gensock,sutsock):
251 log.info("+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+")
252 log.info("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT. SUT sends packets back |")
253 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
254 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 |")
255 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
256 maxspeed = speed = 100
260 endpps_sut_tx_str = 'NO_RESULTS'
261 gensock.set_size(gencores,0,size) # This is setting the frame size
262 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)
263 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)
264 # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
265 while (maxspeed-minspeed > ACCURACY):
267 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
269 # Start generating packets at requested speed (in % of a 10Gb/s link)
270 gensock.speed(speed, gencores)
272 # Get statistics now that the generation is stable and NO ARP messages any more
273 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
274 drop_rate = 100.0*abs_dropped/abs_tx
275 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)):
276 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 |')
278 endpps_req_tx = pps_req_tx
280 endpps_sut_tx_str = pps_sut_tx_str
284 endabs_dropped = abs_dropped
285 enddrop_rate = drop_rate
288 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)+ '% | FAILED |')
290 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
291 if endpps_sut_tx_str <> 'NO_RESULTS':
292 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
293 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 |')
294 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
296 log.info('| Speed 0 or close to 0')
298 def run_flowtest(gensock,sutsock):
299 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
300 log.info("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port |")
301 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
302 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 |")
303 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
305 # 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.
306 flows={128:['1000000000000XXX','100000000000XXXX'],1024:['10000000000XXXXX','10000000000XXXXX'],8192:['1000000000XXXXXX','100000000XXXXXXX'],65535:['10000000XXXXXXXX','10000000XXXXXXXX'],524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
307 # flows={524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
308 gensock.set_size(gencores,0,size) # This is setting the frame size
309 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)
310 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)
311 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
312 for flow_number in sorted(flows.iterkeys()):
313 #speed = 100 Commented out: Not starting from 100% since we are trying more flows, so speed will not be higher than the speed achieved in previous loop
314 gensock.reset_stats()
316 sutsock.reset_stats()
317 source_port,destination_port = flows[flow_number]
318 gensock.set_random(gencores,0,34,source_port,2)
319 gensock.set_random(gencores,0,36,destination_port,2)
320 endpps_sut_tx_str = 'NO_RESULTS'
321 maxspeed = speed = 100
323 while (maxspeed-minspeed > ACCURACY):
324 print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
326 # Start generating packets at requested speed (in % of a 10Gb/s link)
327 gensock.speed(speed, gencores)
329 # Get statistics now that the generation is stable and NO ARP messages any more
330 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
331 drop_rate = 100.0*abs_dropped/abs_tx
332 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)):
334 endpps_req_tx = pps_req_tx
336 endpps_sut_tx_str = pps_sut_tx_str
340 endabs_dropped = abs_dropped
341 enddrop_rate = drop_rate
345 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
346 if endpps_sut_tx_str <> 'NO_RESULTS':
347 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)+ '% |')
348 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
350 log.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
352 def run_sizetest(gensock,sutsock):
353 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
354 log.info("| UDP, 1 flow, different packet sizes |")
355 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
356 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 |")
357 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
358 # PROX will use different packet sizes as defined in sizes[]
359 # sizes=[1496,1020,508,252,124,60]
360 sizes=[1020,508,252,124,60]
362 #speed = 100 Commented out: Not starting from 100% since we are trying smaller packets, so speed will not be higher than the speed achieved in previous loop
363 gensock.reset_stats()
365 sutsock.reset_stats()
366 gensock.set_size(gencores,0,size) # This is setting the frame size
367 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)
368 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)
369 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
370 endpps_sut_tx_str = 'NO_RESULTS'
371 maxspeed = speed = 100
373 while (maxspeed-minspeed > ACCURACY):
374 print(str(size+4)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
376 # Start generating packets at requested speed (in % of a 10Gb/s link)
377 gensock.speed(speed, gencores)
378 # Get statistics now that the generation is stable and NO ARP messages any more
379 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
380 drop_rate = 100.0*abs_dropped/abs_tx
381 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)):
383 endpps_req_tx = pps_req_tx
385 endpps_sut_tx_str = pps_sut_tx_str
389 endabs_dropped = abs_dropped
390 enddrop_rate = drop_rate
394 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
395 if endpps_sut_tx_str <> 'NO_RESULTS':
396 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)+ '% |')
397 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
399 log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
402 def run_irqtest(sock):
403 log.info("+----------------------------------------------------------------------------------------------------------------------------")
404 log.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic ")
405 log.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and ")
406 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 ")
407 log.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout ")
408 log.info("| the duration of the test. This is to avoid rounding errors in the case of 0.0 ")
409 log.info("+----------------------------------------------------------------------------------------------------------------------------")
411 buckets=sock.show_irq_buckets(1)
412 print('Measurement ongoing ... ',end='\r')
414 old_irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
415 irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
416 irq[0][0] = 'bucket us'
417 for j,bucket in enumerate(buckets,start=1):
418 irq[0][j] = '<'+ bucket
419 irq[0][-1] = '>'+ buckets [-2]
422 for j,bucket in enumerate(buckets,start=1):
423 for i,irqcore in enumerate(irqcores,start=1):
424 old_irq[i][j] = sock.irq_stats(irqcore,j-1)
425 time.sleep(float(runtime))
427 for i,irqcore in enumerate(irqcores,start=1):
428 irq[i][0]='core %s '%irqcore
429 for j,bucket in enumerate(buckets,start=1):
430 diff = sock.irq_stats(irqcore,j-1) - old_irq[i][j]
434 irq[i][j] = str(round(diff/float(runtime), 2))
435 log.info('\n'.join([''.join(['{:>12}'.format(item) for item in row]) for row in irq]))
437 def run_impairtest(gensock,sutsock,speed):
438 log.info("+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+")
439 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 |")
440 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
441 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 |")
442 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
445 gensock.set_size(gencores,0,size) # This is setting the frame size
446 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)
447 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)
448 # This will only work when using sending UDP packets. For different protocols and ethernet types, we would need a different calculation
449 gensock.speed(speed, gencores)
452 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '% ',end='\r')
455 # Get statistics now that the generation is stable and NO ARP messages any more
456 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,lat_max, abs_dropped, abs_tx = run_iteration(gensock,sutsock)
457 drop_rate = 100.0*abs_dropped/abs_tx
458 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)+ '% |')
461 # Running at low speed to make sure the ARP messages can get through.
462 # If not doing this, the ARP message could be dropped by a switch in overload and then the test will not give proper results
463 # Note hoever that if we would run the test steps during a very long time, the ARP would expire in the switch.
464 # PROX will send a new ARP request every seconds so chances are very low that they will all fail to get through
465 socks[0].speed(0.01, gencores)
466 socks[0].start(genstatcores)
468 socks[0].stop(gencores)
475 global DROP_RATE_TRESHOLD
477 global required_number_of_test_machines
487 testconfig = ConfigParser.RawConfigParser()
488 testconfig.read(test+'.test')
489 required_number_of_test_machines = testconfig.get('DEFAULT', 'total_number_of_test_machines')
490 DROP_RATE_TRESHOLD = float(testconfig.get('DEFAULT', 'drop_rate_treshold'))
491 ACCURACY = float(testconfig.get('DEFAULT', 'accuracy'))
492 config = ConfigParser.RawConfigParser()
493 config.read(env+'.env')
494 key = config.get('OpenStack', 'key')
495 total_number_of_machines = config.get('rapid', 'total_number_of_machines')
496 if int(required_number_of_test_machines) > int(total_number_of_machines):
497 log.exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
498 raise Exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_test_machines,total_number_of_machines))
499 for vm in range(1, int(total_number_of_machines)+1):
500 vmAdminIP.append(config.get('M%d'%vm, 'admin_ip'))
501 vmDPmac.append(config.get('M%d'%vm, 'dp_mac'))
502 vmDPIP.append(config.get('M%d'%vm, 'dp_ip'))
503 ip = vmDPIP[-1].split('.')
504 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))
506 for vm in range(1, int(required_number_of_test_machines)+1):
507 machine_index.append(int(testconfig.get('TestM%d'%vm, 'machine_index'))-1)
508 for vm in range(1, int(required_number_of_test_machines)+1):
509 config_file.append(testconfig.get('TestM%d'%vm, 'config_file'))
510 script_control.append(testconfig.get('TestM%d'%vm, 'script_control'))
511 group1cores=testconfig.get('TestM%d'%vm, 'group1cores')
512 if group1cores <> 'not_used':
513 group1cores=ast.literal_eval(group1cores)
514 group2cores=testconfig.get('TestM%d'%vm, 'group2cores')
515 if group2cores <> 'not_used':
516 group2cores=ast.literal_eval(group2cores)
517 group3cores=testconfig.get('TestM%d'%vm, 'group3cores')
518 if group3cores <> 'not_used':
519 group3cores=ast.literal_eval(group3cores)
520 with open("parameters%d.lua"%vm, "w") as f:
521 f.write('name="%s"\n'% testconfig.get('TestM%d'%vm, 'name'))
522 f.write('local_ip="%s"\n'% vmDPIP[machine_index[vm-1]])
523 f.write('local_hex_ip="%s"\n'% hexDPIP[machine_index[vm-1]])
524 gwVM = testconfig.get('TestM%d'%vm, 'gw_vm')
525 if gwVM <> 'not_used':
526 gwVMindex = int(gwVM)-1
527 f.write('gw_ip="%s"\n'% vmDPIP[machine_index[gwVMindex]])
528 f.write('gw_hex_ip="%s"\n'% hexDPIP[machine_index[gwVMindex]])
529 destVM = testconfig.get('TestM%d'%vm, 'dest_vm')
530 if destVM <> 'not_used':
531 destVMindex = int(destVM)-1
532 f.write('dest_ip="%s"\n'% vmDPIP[machine_index[destVMindex]])
533 f.write('dest_hex_ip="%s"\n'% hexDPIP[machine_index[destVMindex]])
534 f.write('dest_hex_mac="%s"\n'% vmDPmac[machine_index[destVMindex]].replace(':',' '))
535 if group1cores <> 'not_used':
536 f.write('group1="%s"\n'% ','.join(map(str, group1cores)))
537 if group2cores <> 'not_used':
538 f.write('group2="%s"\n'% ','.join(map(str, group2cores)))
539 if group3cores <> 'not_used':
540 f.write('group3="%s"\n'% ','.join(map(str, group3cores)))
541 if re.match('(l2){0,1}gen.*\.cfg',config_file[-1]):
542 gencores = group1cores
543 latcores = group2cores
544 genstatcores = group3cores
545 elif config_file[-1] == 'gen_gw.cfg':
546 gencores = group1cores
547 latcores = group2cores
548 genstatcores = group3cores
549 elif re.match('(l2){0,1}swap.*\.cfg',config_file[-1]):
550 sutstatcores = group1cores
551 elif config_file[-1] == 'secgw2.cfg':
552 sutstatcores = group1cores
553 elif config_file[-1] == 'irq.cfg':
554 irqcores = group1cores
556 #####################################################################################
558 log.debug ('exit cleanup')
561 for client in clients:
565 atexit.register(exit_handler)
567 for vm in range(0, int(required_number_of_test_machines)):
568 clients.append(prox_ctrl(vmAdminIP[machine_index[vm]], key+'.pem','root'))
569 connect_client(clients[-1])
570 # Creating script to bind the right network interface to the poll mode driver
571 devbindfile = "devbindvm%d.sh"%(vm+1)
572 with open("devbind.sh") as f:
573 newText=f.read().replace('MACADDRESS', vmDPmac[machine_index[vm]])
574 with open(devbindfile, "w") as f:
576 st = os.stat(devbindfile)
577 os.chmod(devbindfile, st.st_mode | stat.S_IEXEC)
578 clients[-1].scp_put('./%s'%devbindfile, '/root/devbind.sh')
579 cmd = '/root/devbind.sh'
580 clients[-1].run_cmd(cmd)
581 log.debug("devbind.sh running on VM%d"%(vm+1))
582 clients[-1].scp_put('./%s'%config_file[vm], '/root/%s'%config_file[vm])
583 clients[-1].scp_put('./parameters%d.lua'%(vm+1), '/root/parameters.lua')
584 log.debug("Starting PROX on VM%d"%(vm+1))
585 if script_control[vm] == 'true':
586 cmd = '/root/prox/build/prox -e -t -o cli -f /root/%s'%config_file[vm]
588 cmd = '/root/prox/build/prox -t -o cli -f /root/%s'%config_file[vm]
589 if configonly == False:
590 clients[-1].fork_cmd(cmd, 'PROX Testing on TestM%d'%(vm+1))
591 socks.append(connect_socket(clients[-1]))
594 init_code = testconfig.get('DEFAULT', 'init_code')
595 if init_code <> 'not_used':
597 ####################################################
599 # Best to run the flow test at the end since otherwise the tests coming after thatmight be influenced by the big number of entries in the switch flow tables
600 ####################################################
601 number_of_tests = testconfig.get('DEFAULT', 'number_of_tests')
602 for vm in range(1, int(number_of_tests)+1):
603 cmd=testconfig.get('test%d'%vm,'cmd')
605 ####################################################