e96844162ca30541e144cb67eda6a6b4bf64e154
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / openstackrapid / runrapid.py
1 #!/usr/bin/python
2
3 ##
4 ## Copyright (c) 2010-2017 Intel Corporation
5 ##
6 ## Licensed under the Apache License, Version 2.0 (the "License");
7 ## you may not use this file except in compliance with the License.
8 ## You may obtain a copy of the License at
9 ##
10 ##     http://www.apache.org/licenses/LICENSE-2.0
11 ##
12 ## Unless required by applicable law or agreed to in writing, software
13 ## distributed under the License is distributed on an "AS IS" BASIS,
14 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ## See the License for the specific language governing permissions and
16 ## limitations under the License.
17 ##
18
19 from __future__ import print_function
20
21 import os
22 import stat
23 import sys
24 import time
25 import subprocess
26 import getopt
27 import re
28 import logging
29 from logging.handlers import RotatingFileHandler
30 from logging import handlers
31 from prox_ctrl import prox_ctrl
32 import ConfigParser
33 import ast
34 import atexit
35
36 version="18.6.15"
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
42
43 def usage():
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]")
51         print("")
52         print("Command-line interface to runrapid")
53         print("")
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.")
62         print("")
63
64 try:
65         opts, args = getopt.getopt(sys.argv[1:], "vh", ["version","help", "env=", "test=","runtime=","configonly=","log="])
66 except getopt.GetoptError as err:
67         print("===========================================")
68         print(str(err))
69         print("===========================================")
70         usage()
71         sys.exit(2)
72 if args:
73         usage()
74         sys.exit(2)
75 for opt, arg in opts:
76         if opt in ("-h", "--help"):
77                 usage()
78                 sys.exit()
79         if opt in ("-v", "--version"):
80                 print("Rapid Automated Performance Indication for Dataplane "+version)
81                 sys.exit()
82         if opt in ("--env"):
83                 env = arg
84                 print ("Using '"+env+"' as name for the environment")
85         if opt in ("--test"):
86                 test = arg
87                 print ("Using '"+test+".test' for test case definition")
88         if opt in ("--runtime"):
89                 runtime = arg
90                 print ("Runtime: "+ runtime)
91         if opt in ("--configonly"):
92                 configonly = arg
93                 print ("configonly: "+ configonly)
94         if opt in ("--log"):
95                 loglevel = arg
96                 print ("Log level: "+ loglevel)
97
98
99 # create formatters
100 screen_formatter = logging.Formatter("%(message)s")
101 file_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
102
103 # get a top-level logger,
104 # set its log level,
105 # BUT PREVENT IT from propagating messages to the root logger
106 #
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)
112 log.propagate = 0
113
114 # create a console handler
115 # and set its log level to the command-line option 
116
117 console_handler = logging.StreamHandler(sys.stdout)
118 console_handler.setLevel(logging.INFO)
119 console_handler.setFormatter(screen_formatter)
120
121 # create a file handler
122 # and set its log level to DEBUG
123 #
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)
129
130 # add handlers to the logger
131 #
132 log.addHandler(file_handler)
133 log.addHandler(console_handler)
134
135 # Check if log exists and should therefore be rolled
136 needRoll = os.path.isfile(log_file)
137
138
139 # This is a stale log, so roll it
140 if needRoll:    
141     # Add timestamp
142     log.debug('\n---------\nLog closed on %s.\n---------\n' % time.asctime())
143
144     # Roll over on application start
145     log.handlers[0].doRollover()
146
147 # Add timestamp
148 log.debug('\n---------\nLog started on %s.\n---------\n' % time.asctime())
149
150 log.debug("runrapid.py version: "+version)
151 #========================================================================
152 def connect_socket(client):
153         attempts = 1
154         log.debug("Trying to connect to PROX (just launched) on %s, attempt: %d" % (client.ip(), attempts))
155         sock = None
156         while True:
157                 sock = client.prox_sock()
158                 if sock is not None:
159                         break
160                 attempts += 1
161                 if attempts > 20:
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))
164                 time.sleep(2)
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())
167         return sock
168
169 def connect_client(client):
170         attempts = 1
171         log.debug("Trying to connect to VM which was just launched on %s, attempt: %d" % (client.ip(), attempts))
172         while True:
173                 try:
174                         client.connect()
175                         break
176                 except RuntimeWarning, ex:
177                         attempts += 1
178                         if attempts > 20:
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))
181                         time.sleep(2)
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())
184
185 def run_iteration(gensock,sutsock):
186         sleep_time = 3
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)
192         if sutsock!='none':
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)
199         if sutsock!='none':
200                 new_sut_rx, new_sut_tx, new_sut_drop, new_sut_tsc, sut_tsc_hz = sutsock.core_stats(sutstatcores)
201         #Stop generating
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)
213         if sutsock!='none':
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)
219         else:
220                 pps_sut_tx = 0
221                 pps_sut_tx_str = 'NO MEAS.'
222         if (tx == 0):
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))
226
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)
234 #       y0=0.99
235 #       y100=0.1
236 #       p=1
237 #       q=.99
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
240         if success:
241                 minspeed = speed
242         else:
243                 maxspeed = speed
244         newspeed = (maxspeed+minspeed)/2.0
245         return (newspeed,minspeed,maxspeed)
246
247 def get_pps(speed,size):
248         return (speed * 100.0 / (8*(size+24)))
249
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
257         minspeed = 0
258         size=60
259         attempts = 0
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):
266                 attempts += 1
267                 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
268                 sys.stdout.flush()
269                 # Start generating packets at requested speed (in % of a 10Gb/s link)
270                 gensock.speed(speed, gencores)
271                 time.sleep(1)
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    |')
277                         endspeed = speed
278                         endpps_req_tx = pps_req_tx
279                         endpps_tx = pps_tx
280                         endpps_sut_tx_str = pps_sut_tx_str
281                         endpps_rx = pps_rx
282                         endlat_avg = lat_avg
283                         endlat_max = lat_max
284                         endabs_dropped = abs_dropped
285                         enddrop_rate = drop_rate
286                         success = True 
287                 else:
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     |')
289                         success = False 
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("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+------------+")
295         else:
296                 log.info('| Speed 0 or close to 0')
297
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("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
304         size=60
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()
315                 if sutsock!='none':
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
322                 minspeed = 0
323                 while (maxspeed-minspeed > ACCURACY):
324                         print(str(flow_number)+' flows: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
325                         sys.stdout.flush()
326                         # Start generating packets at requested speed (in % of a 10Gb/s link)
327                         gensock.speed(speed, gencores)
328                         time.sleep(1)
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)):
333                                 endspeed = speed
334                                 endpps_req_tx = pps_req_tx
335                                 endpps_tx = pps_tx
336                                 endpps_sut_tx_str = pps_sut_tx_str
337                                 endpps_rx = pps_rx
338                                 endlat_avg = lat_avg 
339                                 endlat_max = lat_max 
340                                 endabs_dropped = abs_dropped
341                                 enddrop_rate = drop_rate
342                                 success = True
343                         else:
344                                 success = False 
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("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
349                 else:
350                         log.info('|{:>7}'.format(str(flow_number))+" | Speed 0 or close to 0")
351
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]
361         for size in sizes:
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()
364                 if sutsock!='none':
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
372                 minspeed = 0
373                 while (maxspeed-minspeed > ACCURACY):
374                         print(str(size+4)+' bytes: Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
375                         sys.stdout.flush()
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)):
382                                 endspeed = speed
383                                 endpps_req_tx = pps_req_tx
384                                 endpps_tx = pps_tx
385                                 endpps_sut_tx_str = pps_sut_tx_str
386                                 endpps_rx = pps_rx
387                                 endlat_avg = lat_avg 
388                                 endlat_max = lat_max 
389                                 endabs_dropped = abs_dropped
390                                 enddrop_rate = drop_rate
391                                 success = True
392                         else:
393                                 success = False 
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("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
398                 else:
399                         log.debug('|{:>7}'.format(str(size))+" | Speed 0 or close to 0")
400
401
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("+----------------------------------------------------------------------------------------------------------------------------")
410         sys.stdout.flush()
411         buckets=sock.show_irq_buckets(1)
412         print('Measurement ongoing ... ',end='\r')
413         sock.stop(irqcores)
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]
420         sock.start(irqcores)
421         time.sleep(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))
426         sock.stop(irqcores)
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]
431                         if diff == 0:
432                                 irq[i][j] = '0'
433                         else:
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]))
436
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("+--------+--------------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+------------+")
443         size=60
444         attempts = 0
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)
450         while True:
451                 attempts += 1
452                 print('Measurement ongoing at speed: ' + str(round(speed,2)) + '%      ',end='\r')
453                 sys.stdout.flush()
454                 time.sleep(1)
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)+ '%  |')
459
460 def init_test():
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)
467         time.sleep(2)
468         socks[0].stop(gencores)
469
470 global sutstatcores
471 global genstatcores
472 global latcores
473 global gencores
474 global irqcores
475 global DROP_RATE_TRESHOLD
476 global ACCURACY
477 global required_number_of_test_machines
478 clients =[]
479 socks =[]
480 vmDPIP =[]
481 vmAdminIP =[]
482 vmDPmac =[]
483 hexDPIP =[]
484 config_file =[]
485 script_control =[]
486
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))
505 machine_index = []
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
555         f.close
556 #####################################################################################
557 def exit_handler():
558         log.debug ('exit cleanup')
559         for sock in socks:
560                 sock.quit()
561         for client in clients:
562                 client.close()
563         sys.exit(0)
564
565 atexit.register(exit_handler)
566
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:
575                         f.write(newText)
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]
587         else:
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]))
592 if configonly:
593         sys.exit()
594 init_code = testconfig.get('DEFAULT', 'init_code')
595 if init_code <> 'not_used':
596         eval(init_code)
597 ####################################################
598 # Run test cases
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')
604         eval(cmd)
605 ####################################################