7bbfa2a773180c2ce9691531d0afe9deb24f6f57
[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
35 version="18.2.12"
36 env = "rapid" #Default string for environment
37 test = "basicrapid" #Default string for test
38 loglevel="DEBUG" # sets log level for writing to file
39 runtime=10 # time in seconds for 1 test run
40
41 def usage():
42         print("usage: runrapid    [--version] [-v]")
43         print("                   [--env ENVIRONMENT_NAME]")
44         print("                   [--test TEST_NAME]")
45         print("                   [--runtime TIME_FOR_TEST]")
46         print("                   [--log DEBUG|INFO|WARNING|ERROR|CRITICAL]")
47         print("                   [-h] [--help]")
48         print("")
49         print("Command-line interface to runrapid")
50         print("")
51         print("optional arguments:")
52         print("  -v,  --version                 Show program's version number and exit")
53         print("  --env ENVIRONMENT_NAME         Parameters will be read from ENVIRONMENT_NAME.env Default is %s."%env)
54         print("  --test TEST_NAME               Test cases will be read from TEST_NAME.test Default is %s."%test)
55         print("  --runtime                      Specify time in seconds for 1 test run")
56         print("  --log                          Specify logging level for log file output, screen output level is hard coded")
57         print("  -h, --help                     Show help message and exit.")
58         print("")
59
60 try:
61         opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "env=", "test=","runtime=","log="])
62 except getopt.GetoptError as err:
63         print("===========================================")
64         print(str(err))
65         print("===========================================")
66         usage()
67         sys.exit(2)
68 if args:
69         usage()
70         sys.exit(2)
71 for opt, arg in opts:
72         if opt in ("-h", "--help"):
73                 usage()
74                 sys.exit()
75         if opt in ("-v", "--version"):
76                 print("Rapid Automated Performance Indication for Dataplane "+version)
77                 sys.exit()
78         if opt in ("--env"):
79                 env = arg
80                 print ("Using '"+env+"' as name for the environment")
81         if opt in ("--test"):
82                 test = arg
83                 print ("Using '"+test+".test' for test case definition")
84         if opt in ("--runtime"):
85                 runtime = arg
86                 print ("Runtime: "+ runtime)
87         if opt in ("--log"):
88                 loglevel = arg
89                 print ("Log level: "+ loglevel)
90
91
92 # create formatters
93 screen_formatter = logging.Formatter("%(message)s")
94 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
95
96 # get a top-level logger,
97 # set its log level,
98 # BUT PREVENT IT from propagating messages to the root logger
99 #
100 log = logging.getLogger()
101 numeric_level = getattr(logging, loglevel.upper(), None)
102 if not isinstance(numeric_level, int):
103     raise ValueError('Invalid log level: %s' % loglevel)
104 log.setLevel(numeric_level)
105 log.propagate = 0
106
107 # create a console handler
108 # and set its log level to the command-line option 
109
110 console_handler = logging.StreamHandler(sys.stdout)
111 console_handler.setLevel(logging.INFO)
112 console_handler.setFormatter(screen_formatter)
113
114 # create a file handler
115 # and set its log level to DEBUG
116 #
117 log_file = 'RUN' +env+'.'+test+'.log'
118 file_handler = logging.handlers.RotatingFileHandler(log_file, backupCount=10)
119 #file_handler = log.handlers.TimedRotatingFileHandler(log_file, 'D', 1, 5)
120 file_handler.setLevel(numeric_level)
121 file_handler.setFormatter(file_formatter)
122
123 # add handlers to the logger
124 #
125 log.addHandler(file_handler)
126 log.addHandler(console_handler)
127
128 # Check if log exists and should therefore be rolled
129 needRoll = os.path.isfile(log_file)
130
131
132 # This is a stale log, so roll it
133 if needRoll:    
134     # Add timestamp
135     log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
136
137     # Roll over on application start
138     log.handlers[0].doRollover()
139
140 # Add timestamp
141 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
142
143 log.debug("runrapid.py version: "+version)
144 #========================================================================
145 def connect_socket(client):
146         attempts = 1
147         log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
148         sock = None
149         while True:
150                 sock = client.prox_sock()
151                 if sock is not None:
152                         break
153                 attempts += 1
154                 if attempts > 20:
155                         log.exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
156                         raise Exception("Failed to connect to PROX on %s after %d attempts" % (client.ip(), attempts))
157                 time.sleep(2)
158                 log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
159         log.info("Connected to PROX on %s" % client.ip())
160         return sock
161
162 def connect_client(client):
163         attempts = 1
164         log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
165         while True:
166                 try:
167                         client.connect()
168                         break
169                 except RuntimeWarning, ex:
170                         attempts += 1
171                         if attempts > 20:
172                                 log.exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
173                                 raise Exception("Failed to connect to VM after %d attempts:\n%s" % (attempts, ex))
174                         time.sleep(2)
175                         log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
176         log.debug("Connected to VM on %s" % client.ip())
177
178 def run_iteration(gensock,sutsock):
179         sleep_time = 2
180         # 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
181         abs_old_rx, abs_old_tx, abs_old_drop, abs_old_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
182         gensock.start(gencores)
183         time.sleep(sleep_time)
184         if sutsock!='none':
185                 old_sut_rx, old_sut_tx, old_sut_drop, old_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
186         old_rx, old_tx, old_drop, old_tsc, tsc_hz = gensock.core_stats(genstatcores)
187         time.sleep(float(runtime))
188         lat_min, lat_max, lat_avg = gensock.lat_stats(latcores)
189         # Get statistics after some execution time
190         new_rx, new_tx, new_drop, new_tsc, tsc_hz = gensock.core_stats(genstatcores)
191         if sutsock!='none':
192                 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
193         #Stop generating
194         gensock.stop(gencores)
195         time.sleep(sleep_time)
196         abs_new_rx, abs_new_tx, abs_new_drop, abs_new_tsc, abs_tsc_hz = gensock.core_stats(genstatcores)
197         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
198         rx = new_rx - old_rx     # rx is all packets received by the nop task = all packets received in the gen VM
199         tx = new_tx - old_tx     # tx is all generated packets actually accepted by the interface
200         abs_dropped = (abs_new_tx - abs_old_tx) - (abs_new_rx - abs_old_rx)
201         tsc = new_tsc - old_tsc  # time difference between the 2 measurements, expressed in cycles.
202         pps_req_tx = (tx+drop-rx)*tsc_hz*1.0/(tsc*1000000)
203         pps_tx = tx*tsc_hz*1.0/(tsc*1000000)
204         pps_rx = rx*tsc_hz*1.0/(tsc*1000000)
205         if sutsock!='none':
206                 sut_rx = new_sut_rx - old_sut_rx
207                 sut_tx = new_sut_tx - old_sut_tx
208                 sut_tsc = new_sut_tsc - old_sut_tsc
209                 pps_sut_tx = sut_tx*sut_tsc_hz*1.0/(sut_tsc*1000000)
210                 pps_sut_tx_str = '{:>9.3f}'.format(pps_sut_tx)
211         else:
212                 pps_sut_tx = 0
213                 pps_sut_tx_str = 'NO MEAS.'
214         if (tx == 0):
215                 log.critical("TX = 0. Test interrupted since no packet has been sent.")
216                 raise Exception("TX = 0")
217         return(pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,abs_dropped)
218
219 def new_speed(speed,minspeed,maxspeed,success):
220         # Following calculates the ratio for the new speed to be applied
221         # On the Y axis, we will find the ratio, a number between 0 and 1
222         # On the x axis, we find the % of dropped packets, a number between 0 and 100
223         # 2 lines are drawn and we take the minumun of these lines to calculate the ratio
224         # One line goes through (0,y0) and (p,q)
225         # The second line goes through (p,q) and (100,y100)
226 #       y0=0.99
227 #       y100=0.1
228 #       p=1
229 #       q=.99
230 #       ratio = min((q-y0)/p*drop_rate+y0,(q-y100)/(p-100)*drop_rate+q-p*(q-y100)/(p-100))
231 #       return (int(speed*ratio*100)+0.5)/100.0
232         if success:
233                 minspeed = speed
234         else:
235                 maxspeed = speed
236         newspeed = (maxspeed+minspeed)/2.0
237         return (newspeed,minspeed,maxspeed)
238
239 def get_pps(speed,size):
240         return (speed * 100.0 / (8*(size+24)))
241
242 def get_drop_rate(speed,pps_rx,size):
243         # pps_rx are all the packets that are received by the generator. That is substracted
244         # from the pps that we wanted to send. This is calculated by taking the variable speed
245         # which is the requested percentage of a 10Gb/s link. So we take  10000bps (10Gbps, note
246         # that the speed variable is already expressed in % so we only take 100 and not 10000)
247         # divided by the number of bits in 1 packet. That is 8 bits in a byte times the size of
248         # a frame (=our size + 24 bytes overhead).
249         tried_to_send = get_pps(speed,size)
250         return abs(100.0*(tried_to_send - pps_rx)/tried_to_send)
251
252 def run_speedtest(gensock,sutsock):
253         log.info("+-------------------------------------------------------------------------------------------------------------------------------------------------------------+")
254         log.info("| Generator is sending UDP (1 flow) packets (64 bytes) to SUT. SUT sends packets back                                                                         |")
255         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+-------------------------+")
256         log.info("| Test   |  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Packets Lost  | Loss Ratio | Result     |")
257         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
258         speed = 100
259         maxspeed = speed
260         minspeed = 0
261         size=60
262         attempts = 0
263         endpps_sut_tx_str = 'NO_RESULTS'
264         gensock.set_size(gencores,0,size) # This is setting the frame size
265         gensock.set_value(gencores,0,16,(size-18),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
266         gensock.set_value(gencores,0,38,(size-38),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
267         # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
268         while (maxspeed-minspeed > ACCURACY):
269                 attempts += 1
270                 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
271                 sys.stdout.flush()
272                 # Start generating packets at requested speed (in % of a 10Gb/s link)
273                 gensock.speed(speed, gencores)
274                 time.sleep(1)
275                 # Get statistics now that the generation is stable and NO ARP messages any more
276                 pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,abs_dropped = run_iteration(gensock,sutsock)
277                 drop_rate = get_drop_rate(speed,pps_rx,size)
278                 if (drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0):
279                         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   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  | SUCCESS    |')
280                         endspeed = speed
281                         endpps_req_tx = pps_req_tx
282                         endpps_tx = pps_tx
283                         endpps_sut_tx_str = pps_sut_tx_str
284                         endpps_rx = pps_rx
285                         endlat_avg = lat_avg
286                         endabs_dropped = abs_dropped
287                         enddrop_rate = drop_rate
288                         success = True 
289                 else:
290                         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   | '+ '{:>14d}'.format(abs_dropped)+ ' |''{:>9.2f}'.format(drop_rate)+ '%  | FAILED     |')
291                         success = False 
292                 speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
293         if endpps_sut_tx_str <>  'NO_RESULTS':
294                 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
295                 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   | '+ '{:>14d}'.format(endabs_dropped)+ ' |''{:>9.2f}'.format(enddrop_rate)+ '%  | SUCCESS    |')
296                 log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
297         else:
298                 log.info('| Speed 0 or close to 0')
299
300 def run_flowtest(gensock,sutsock):
301         log.info("+------------------------------------------------------------------------------------------------------------------------------------------------+")
302         log.info("| UDP, 64 bytes, different number of flows by randomizing SRC & DST UDP port                                                                     |")
303         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
304         log.info("| Flows  |  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Packets Lost  | Loss Ratio |")
305         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
306         speed = 100
307         size=60
308         # 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. 
309         flows={128:['1000000000000XXX','100000000000XXXX'],1024:['10000000000XXXXX','10000000000XXXXX'],8192:['1000000000XXXXXX','100000000XXXXXXX'],65535:['10000000XXXXXXXX','10000000XXXXXXXX'],524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
310 #       flows={524280:['1000000XXXXXXXXX','100000XXXXXXXXXX']}
311         gensock.set_size(gencores,0,size) # This is setting the frame size
312         gensock.set_value(gencores,0,16,(size-18),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
313         gensock.set_value(gencores,0,38,(size-38),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
314         # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
315         for flow_number in sorted(flows.iterkeys()):
316                 #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
317                 gensock.reset_stats()
318                 if sutsock!='none':
319                         sutsock.reset_stats()
320                 source_port,destination_port = flows[flow_number]
321                 gensock.set_random(gencores,0,34,source_port,2)
322                 gensock.set_random(gencores,0,36,destination_port,2)
323                 endpps_sut_tx_str = 'NO_RESULTS'
324                 maxspeed = speed
325                 minspeed = 0
326                 while (maxspeed-minspeed > ACCURACY):
327                         print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
328                         sys.stdout.flush()
329                         # Start generating packets at requested speed (in % of a 10Gb/s link)
330                         gensock.speed(speed, gencores)
331                         time.sleep(1)
332                         # Get statistics now that the generation is stable and NO ARP messages any more
333                         pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg,abs_dropped = run_iteration(gensock,sutsock)
334                         drop_rate = get_drop_rate(speed,pps_rx,size)
335                         if (drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0):
336                                 endspeed = speed
337                                 endpps_req_tx = pps_req_tx
338                                 endpps_tx = pps_tx
339                                 endpps_sut_tx_str = pps_sut_tx_str
340                                 endpps_rx = pps_rx
341                                 endlat_avg = lat_avg 
342                                 endabs_dropped = abs_dropped
343                                 enddrop_rate = drop_rate
344                                 success = True
345                         else:
346                                 success = False 
347                         speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
348                 if endpps_sut_tx_str <>  'NO_RESULTS':
349                         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   | '+ '{:>14d}'.format(endabs_dropped)+ ' |'+'{:>9.2f}'.format(enddrop_rate)+ '% |')
350                         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
351                 else:
352                         log.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
353
354 def run_sizetest(gensock,sutsock):
355         log.info("+------------------------------------------------------------------------------------------------------------------------------------------------+")
356         log.info("| UDP, 1 flow, different packet sizes                                                                                                            |")
357         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
358         log.info("| Pktsize|  Speed requested   | Sent to NIC    |  Sent by Gen   | Forward by SUT |  Rec. by Gen   |  Avg. Latency  |  Packets Lost  | Loss Ratio |")
359         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
360         speed = 100
361         # PROX will use different packet sizes as defined in sizes[]
362 #       sizes=[1496,1020,508,252,124,60]
363         sizes=[1020,508,252,124,60]
364         for size in sizes:
365                 #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
366                 gensock.reset_stats()
367                 if sutsock!='none':
368                         sutsock.reset_stats()
369                 gensock.set_size(gencores,0,size) # This is setting the frame size
370                 gensock.set_value(gencores,0,16,(size-18),2) # 18 is the difference between the frame size and IP size = size of (MAC addresses, ethertype and FCS)
371                 gensock.set_value(gencores,0,38,(size-38),2) # 38 is the difference between the frame size and UDP size = 18 + size of IP header (=20)
372                 # This will only work when using sending UDP packets. For different protocls and ehternet types, we would need a differnt calculation
373                 endpps_sut_tx_str = 'NO_RESULTS'
374                 maxspeed = speed
375                 minspeed = 0
376                 while (maxspeed-minspeed > ACCURACY):
377                         print(str(size+4)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
378                         sys.stdout.flush()
379                         # Start generating packets at requested speed (in % of a 10Gb/s link)
380                         gensock.speed(speed, gencores)
381                         # Get statistics now that the generation is stable and NO ARP messages any more
382                         pps_req_tx,pps_tx,pps_sut_tx_str,pps_rx,lat_avg, abs_dropped = run_iteration(gensock,sutsock)
383                         drop_rate = get_drop_rate(speed,pps_rx,size)
384                         if (drop_rate < DROP_RATE_TRESHOLD) or (abs_dropped==DROP_RATE_TRESHOLD ==0):
385                                 endspeed = speed
386                                 endpps_req_tx = pps_req_tx
387                                 endpps_tx = pps_tx
388                                 endpps_sut_tx_str = pps_sut_tx_str
389                                 endpps_rx = pps_rx
390                                 endlat_avg = lat_avg 
391                                 endabs_dropped = abs_dropped
392                                 enddrop_rate = drop_rate
393                                 success = True
394                         else:
395                                 success = False 
396                         speed,minspeed,maxspeed = new_speed(speed,minspeed,maxspeed,success)
397                 if endpps_sut_tx_str <>  'NO_RESULTS':
398                         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   | '+ '{:>14d}'.format(endabs_dropped)+ ' |'+'{:>9.2f}'.format(enddrop_rate)+ '% |')
399                         log.info("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
400                 else:
401                         log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
402
403
404 def run_irqtest(sock):
405         log.info("+----------------------------------------------------------------------------------------------------------------------------")
406         log.info("| Measuring time probably spent dealing with an interrupt. Interrupting DPDK cores for more than 50us might be problematic   ")
407         log.info("| and result in packet loss. The first row shows the interrupted time buckets: first number is the bucket between 0us and    ")
408         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       ")
409         log.info("| interrupted for a time as specified by its bucket. '0' is printed when there are no interrupts in this bucket throughout   ")
410         log.info("| the duration of the test. This is to avoid rounding errors in the case of 0.0                                              ") 
411         log.info("+----------------------------------------------------------------------------------------------------------------------------")
412         sys.stdout.flush()
413         buckets=sock.show_irq_buckets(1)
414         print('Measurement ongoing ... ',end='\r')
415         sock.stop(irqcores)
416         old_irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)] 
417         irq = [[0 for x in range(len(buckets)+1)] for y in range(len(irqcores)+1)]
418         irq[0][0] = 'bucket us' 
419         for j,bucket in enumerate(buckets,start=1):
420                 irq[0][j] = '<'+ bucket
421         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         sock.start(irqcores)
426         time.sleep(float(runtime))
427         sock.stop(irqcores)
428         for i,irqcore in enumerate(irqcores,start=1):
429                 irq[i][0]='core %s '%irqcore
430                 for j,bucket in enumerate(buckets,start=1):
431                         diff =  sock.irq_stats(irqcore,j-1) - old_irq[i][j]
432                         if diff == 0:
433                                 irq[i][j] = '0'
434                         else:
435                                 irq[i][j] = diff/float(runtime)
436         log.info('\n'.join([''.join(['{:>12}'.format(item) for item in row]) for row in irq]))
437
438
439 def init_test():
440 # Running at low speed to make sure the ARP messages can get through.
441 # If not doing this, the ARP message could be dropped by a switch in overload and then the test will not give proper results
442 # Note hoever that if we would run the test steps during a very long time, the ARP would expire in the switch.
443 # PROX will send a new ARP request every seconds so chances are very low that they will all fail to get through
444         sock[0].speed(0.01, gencores)
445         sock[0].start(genstatcores)
446         time.sleep(2)
447         sock[0].stop(gencores)
448
449 global sutstatcores
450 global genstatcores
451 global latcores
452 global gencores
453 global irqcores
454 global DROP_RATE_TRESHOLD
455 global ACCURACY
456 vmDPIP =[]
457 vmAdminIP =[]
458 vmDPmac =[]
459 hexDPIP =[]
460 config_file =[]
461 script_control =[]
462
463 testconfig = ConfigParser.RawConfigParser()
464 testconfig.read(test+'.test')
465 required_number_of_VMs = testconfig.get('DEFAULT', 'total_number_of_vms')
466 DROP_RATE_TRESHOLD = float(testconfig.get('DEFAULT', 'drop_rate_treshold'))
467 ACCURACY = float(testconfig.get('DEFAULT', 'accuracy'))
468 config = ConfigParser.RawConfigParser()
469 config.read(env+'.env')
470 key = config.get('OpenStack', 'key')
471 total_number_of_VMs = config.get('rapid', 'total_number_of_VMs')
472 if int(required_number_of_VMs) > int(total_number_of_VMs):
473         log.exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_VMs,total_number_of_VMs))
474         raise Exception("Not enough VMs for this test: %s needed and only %s available" % (required_number_of_VMs,total_number_of_VMs))
475 for vm in range(1, int(total_number_of_VMs)+1):
476         vmAdminIP.append(config.get('VM%d'%vm, 'admin_ip'))
477         vmDPmac.append(config.get('VM%d'%vm, 'dp_mac'))
478         vmDPIP.append(config.get('VM%d'%vm, 'dp_ip'))
479         ip = vmDPIP[-1].split('.')
480         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))
481 for vm in range(1, int(required_number_of_VMs)+1):
482         config_file.append(testconfig.get('VM%d'%vm, 'config_file'))
483         script_control.append(testconfig.get('VM%d'%vm, 'script_control'))
484         group1cores=testconfig.get('VM%d'%vm, 'group1cores')
485         if group1cores <> 'not_used':
486                 group1cores=ast.literal_eval(group1cores)
487         group2cores=testconfig.get('VM%d'%vm, 'group2cores')
488         if group2cores <> 'not_used':
489                 group2cores=ast.literal_eval(group2cores)
490         group3cores=testconfig.get('VM%d'%vm, 'group3cores')
491         if group3cores <> 'not_used':
492                 group3cores=ast.literal_eval(group3cores)
493         with open("parameters%d.lua"%vm, "w") as f:
494                 f.write('name="%s"\n'% testconfig.get('VM%d'%vm, 'name'))
495                 f.write('local_ip="%s"\n'% vmDPIP[vm-1])
496                 f.write('local_hex_ip="%s"\n'% hexDPIP[vm-1])
497                 gwVM = testconfig.get('VM%d'%vm, 'gw_vm')
498                 if gwVM <> 'not_used':
499                         gwVMindex = int(gwVM)-1
500                         f.write('gw_ip="%s"\n'% vmDPIP[gwVMindex])
501                         f.write('gw_hex_ip="%s"\n'% hexDPIP[gwVMindex])
502                 destVM = testconfig.get('VM%d'%vm, 'dest_vm')
503                 if destVM <> 'not_used':
504                         destVMindex = int(destVM)-1
505                         f.write('dest_ip="%s"\n'% vmDPIP[destVMindex])
506                         f.write('dest_hex_ip="%s"\n'% hexDPIP[destVMindex])
507                         f.write('dest_hex_mac="%s"\n'% vmDPmac[destVMindex].replace(':',' '))
508                 if group1cores <> 'not_used':
509                         f.write('group1="%s"\n'% ','.join(map(str, group1cores)))
510                 if group2cores <> 'not_used':
511                         f.write('group2="%s"\n'% ','.join(map(str, group2cores)))
512                 if group3cores <> 'not_used':
513                         f.write('group3="%s"\n'% ','.join(map(str, group3cores)))
514         if re.match('(l2){0,1}gen.*\.cfg',config_file[-1]):
515                 gencores = group1cores
516                 latcores = group2cores
517                 genstatcores = group3cores
518         elif config_file[-1] == 'gen_gw.cfg':
519                 gencores = group1cores
520                 latcores = group2cores
521                 genstatcores = group3cores
522         elif  re.match('(l2){0,1}swap.*\.cfg',config_file[-1]):
523                 sutstatcores = group1cores
524         elif config_file[-1] == 'secgw2.cfg':
525                 sutstatcores = group1cores
526         elif config_file[-1] == 'irq.cfg':
527                 irqcores = group1cores
528         f.close
529 #####################################################################################
530 client =[]
531 sock =[]
532
533 for vm in range(0, int(required_number_of_VMs)):
534         client.append(prox_ctrl(vmAdminIP[vm], key+'.pem','root'))
535         connect_client(client[-1])
536 # Creating script to bind the right network interface to the poll mode driver
537         devbindfile = "devbindvm%d.sh"%(vm+1)
538         with open("devbind.sh") as f:
539                 newText=f.read().replace('MACADDRESS', vmDPmac[vm])
540                 with open(devbindfile, "w") as f:
541                         f.write(newText)
542         st = os.stat(devbindfile)
543         os.chmod(devbindfile, st.st_mode | stat.S_IEXEC)
544         client[-1].scp_put('./%s'%devbindfile, '/root/devbind.sh')
545         cmd = '/root/devbind.sh'
546         client[-1].run_cmd(cmd)
547         log.debug("devbind.sh running on VM%d"%(vm+1))
548         client[-1].scp_put('./%s'%config_file[vm], '/root/%s'%config_file[vm])
549         client[-1].scp_put('./parameters%d.lua'%(vm+1), '/root/parameters.lua')
550         log.debug("Starting PROX on VM%d"%(vm+1))
551         if script_control[vm] == 'true':
552                 cmd = '/root/prox/build/prox -e -t -o cli -f /root/%s'%config_file[vm]
553         else:
554                 cmd = '/root/prox/build/prox -t -o cli -f /root/%s'%config_file[vm]
555         client[-1].fork_cmd(cmd, 'PROX Testing on VM%d'%(vm+1))
556         sock.append(connect_socket(client[-1]))
557
558 init_code = testconfig.get('DEFAULT', 'init_code')
559 if init_code <> 'not_used':
560         eval(init_code)
561 ####################################################
562 # Run test cases
563 # 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
564 ####################################################
565 number_of_tests = testconfig.get('DEFAULT', 'number_of_tests')
566 for vm in range(1, int(number_of_tests)+1):
567         cmd=testconfig.get('test%d'%vm,'cmd')
568         eval(cmd)
569 ####################################################
570 for vm in range(0, int(required_number_of_VMs)):
571         sock[vm].quit()
572         client[vm].close()