b39520c75e66e5711a27a340c0baa1a44a4fd9bb
[samplevnf.git] / VNFs / DPPD-PROX / helper-scripts / rapid / rapid_test.py
1 #!/usr/bin/python
2
3 ##
4 ## Copyright (c) 2010-2020 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 ##
11 ##     http://www.apache.org/licenses/LICENSE-2.0
12 ##
13 ## Unless required by applicable law or agreed to in writing, software
14 ## distributed under the License is distributed on an "AS IS" BASIS,
15 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 ## See the License for the specific language governing permissions and
17 ## limitations under the License.
18 ##
19
20 import time
21 from past.utils import old_div
22 from rapid_log import RapidLog
23 from rapid_log import bcolors
24
25 class RapidTest(object):
26     """
27     Class to manage the flowsizetesting
28     """
29     @staticmethod
30     def get_percentageof10Gbps(pps_speed,size):
31         # speed is given in pps, returning % of 10Gb/s
32         # 12 bytes is the inter packet gap 
33         # pre-amble is 7 bytes
34         # SFD (start of frame delimiter) is 1 byte
35         # Total of 20 bytes overhead per packet
36         return (pps_speed / 1000000.0 * 0.08 * (size+20))
37
38     @staticmethod
39     def get_pps(speed,size):
40         # speed is given in % of 10Gb/s, returning Mpps
41         # 12 bytes is the inter packet gap 
42         # pre-amble is 7 bytes
43         # SFD (start of frame delimiter) is 1 byte
44         # Total of 20 bytes overhead per packet
45         return (speed * 100.0 / (8*(size+20)))
46
47     @staticmethod
48     def get_speed(packet_speed,size):
49         # return speed in Gb/s
50         # 12 bytes is the inter packet gap 
51         # pre-amble is 7 bytes
52         # SFD (start of frame delimiter) is 1 byte
53         # Total of 20 bytes overhead per packet
54         return (packet_speed / 1000.0 * (8*(size+20)))
55
56     @staticmethod
57     def set_background_flows(background_machines, number_of_flows):
58         for machine in background_machines:
59             _ = machine.set_flows(number_of_flows)
60
61     @staticmethod
62     def set_background_speed(background_machines, speed):
63         for machine in background_machines:
64             machine.set_generator_speed(speed)
65
66     @staticmethod
67     def set_background_size(background_machines, imix):
68         # imixs is a list of packet sizes
69         for machine in background_machines:
70             machine.set_udp_packet_size(imix)
71
72     @staticmethod
73     def start_background_traffic(background_machines):
74         for machine in background_machines:
75             machine.start()
76
77     @staticmethod
78     def stop_background_traffic(background_machines):
79         for machine in background_machines:
80             machine.stop()
81
82     @staticmethod
83     def report_result(flow_number, size, speed, pps_req_tx, pps_tx, pps_sut_tx,
84         pps_rx, lat_avg, lat_perc, lat_perc_max, lat_max, tx, rx, tot_drop,
85         elapsed_time,speed_prefix='', lat_avg_prefix='', lat_perc_prefix='',
86         lat_max_prefix='', abs_drop_rate_prefix='', drop_rate_prefix=''):
87         if flow_number < 0:
88             flow_number_str = '| ({:>4}) |'.format(abs(flow_number))
89         else:
90             flow_number_str = '|{:>7} |'.format(flow_number)
91         if pps_req_tx is None:
92             pps_req_tx_str = '{0: >14}'.format('   NA     |')
93         else:
94             pps_req_tx_str = '{:>7.3f} Mpps |'.format(pps_req_tx)
95         if pps_tx is None:
96             pps_tx_str = '{0: >14}'.format('   NA     |')
97         else:
98             pps_tx_str = '{:>7.3f} Mpps |'.format(pps_tx) 
99         if pps_sut_tx is None:
100             pps_sut_tx_str = '{0: >14}'.format('   NA     |')
101         else:
102             pps_sut_tx_str = '{:>7.3f} Mpps |'.format(pps_sut_tx)
103         if pps_rx is None:
104             pps_rx_str = '{0: >25}'.format('NA        |')
105         else:
106             pps_rx_str = bcolors.OKBLUE + '{:>4.1f} Gb/s |{:7.3f} Mpps {}|'.format(RapidTest.get_speed(pps_rx,size),pps_rx,bcolors.ENDC)
107         if tot_drop is None:
108             tot_drop_str = ' |       NA  | '
109         else:
110             tot_drop_str = ' | {:>9.0f} | '.format(tot_drop)
111         if lat_perc is None:
112             lat_perc_str = ' |{:^10.10}|'.format('NA')
113         elif lat_perc_max == True:
114             lat_perc_str = '|>{}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC) 
115         else:
116             lat_perc_str = '| {}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC) 
117         if elapsed_time is None:
118             elapsed_time_str = ' NA |'
119         else:
120             elapsed_time_str = '{:>3.0f} |'.format(elapsed_time)
121         return(flow_number_str + '{:>5.1f}'.format(speed) + '% '+speed_prefix +'{:>6.3f}'.format(RapidTest.get_pps(speed,size)) + ' Mpps|'+ pps_req_tx_str + pps_tx_str + bcolors.ENDC + pps_sut_tx_str + pps_rx_str +lat_avg_prefix+ ' {:>6.0f}'.format(lat_avg)+' us'+lat_perc_str+lat_max_prefix+'{:>6.0f}'.format(lat_max)+' us | ' + '{:>9.0f}'.format(tx) + ' | {:>9.0f}'.format(rx) + ' | '+ abs_drop_rate_prefix+ '{:>9.0f}'.format(tx-rx) + tot_drop_str +drop_rate_prefix+ '{:>5.2f}'.format(old_div(float(tx-rx),tx))  +bcolors.ENDC+' |' + elapsed_time_str)
122             
123     def run_iteration(self, requested_duration, flow_number, size, speed):
124         BUCKET_SIZE_EXP = self.gen_machine.bucket_size_exp
125         LAT_PERCENTILE = self.test['lat_percentile']
126         r = 0;
127         sleep_time = 2
128         while (r < self.test['maxr']):
129             time.sleep(sleep_time)
130             # 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
131             t1_rx, t1_non_dp_rx, t1_tx, t1_non_dp_tx, t1_drop, t1_tx_fail, t1_tsc, abs_tsc_hz = self.gen_machine.core_stats()
132             t1_dp_rx = t1_rx - t1_non_dp_rx
133             t1_dp_tx = t1_tx - t1_non_dp_tx
134             self.gen_machine.start_gen_cores()
135             time.sleep(2) ## Needs to be 2 seconds since this 1 sec is the time that PROX uses to refresh the stats. Note that this can be changed in PROX!! Don't do it.
136             if self.sut_machine!= None:
137                 t2_sut_rx, t2_sut_non_dp_rx, t2_sut_tx, t2_sut_non_dp_tx, t2_sut_drop, t2_sut_tx_fail, t2_sut_tsc, sut_tsc_hz = self.sut_machine.core_stats()
138             t2_rx, t2_non_dp_rx, t2_tx, t2_non_dp_tx, t2_drop, t2_tx_fail, t2_tsc, tsc_hz = self.gen_machine.core_stats()
139             tx = t2_tx - t1_tx
140             dp_tx =  tx - (t2_non_dp_tx - t1_non_dp_tx )
141             dp_rx =  t2_rx - t1_rx - (t2_non_dp_rx - t1_non_dp_rx) 
142             tot_dp_drop = dp_tx - dp_rx
143             if tx == 0:
144                 RapidLog.critical("TX = 0. Test interrupted since no packet has been sent.")
145             if dp_tx == 0:
146                 RapidLog.critical("Only non-dataplane packets (e.g. ARP) sent. Test interrupted since no packet has been sent.")
147             # Ask PROX to calibrate the bucket size once we have a PROX function to do this.
148             # Measure latency statistics per second
149             lat_min, lat_max, lat_avg, used_avg, t2_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
150             lat_samples = sum(buckets)
151             sample_count = 0
152             for sample_percentile, bucket in enumerate(buckets,start=1):
153                 sample_count += bucket
154                 if sample_count > (lat_samples * LAT_PERCENTILE):
155                     break
156             percentile_max = (sample_percentile == len(buckets))
157             sample_percentile = sample_percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
158             if self.test['test'] == 'fixed_rate':
159                 RapidLog.info(self.report_result(flow_number,size,speed,None,None,None,None,lat_avg,sample_percentile,percentile_max,lat_max, dp_tx, dp_rx , None, None))
160             tot_rx = tot_non_dp_rx = tot_tx = tot_non_dp_tx = tot_drop = 0
161             lat_avg = used_avg = 0
162             buckets_total = [0] * 128
163             tot_lat_samples = 0
164             tot_lat_measurement_duration = float(0)
165             tot_core_measurement_duration = float(0)
166             tot_sut_core_measurement_duration = float(0)
167             tot_sut_rx = tot_sut_non_dp_rx = tot_sut_tx = tot_sut_non_dp_tx = tot_sut_drop = tot_sut_tx_fail = tot_sut_tsc = 0
168             lat_avail = core_avail = sut_avail = False
169             while (tot_core_measurement_duration - float(requested_duration) <= 0.1) or (tot_lat_measurement_duration - float(requested_duration) <= 0.1):
170                 time.sleep(0.5)
171                 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t3_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
172                 # Get statistics after some execution time
173                 if t3_lat_tsc != t2_lat_tsc:
174                     single_lat_measurement_duration = (t3_lat_tsc - t2_lat_tsc) * 1.0 / lat_hz  # time difference between the 2 measurements, expressed in seconds.
175                     # A second has passed in between to lat_stats requests. Hence we need to process the results
176                     tot_lat_measurement_duration = tot_lat_measurement_duration + single_lat_measurement_duration
177                     if lat_min > lat_min_sample:
178                         lat_min = lat_min_sample
179                     if lat_max < lat_max_sample:
180                         lat_max = lat_max_sample
181                     lat_avg = lat_avg + lat_avg_sample * single_lat_measurement_duration # Sometimes, There is more than 1 second between 2 lat_stats. Hence we will take the latest measurement
182                     used_avg = used_avg + used_sample * single_lat_measurement_duration  # and give it more weigth.
183                     lat_samples = sum(buckets)
184                     tot_lat_samples += lat_samples
185                     sample_count = 0
186                     for sample_percentile, bucket in enumerate(buckets,start=1):
187                         sample_count += bucket
188                         if sample_count > lat_samples * LAT_PERCENTILE:
189                             break
190                     percentile_max = (sample_percentile == len(buckets))
191                     sample_percentile = sample_percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
192                     buckets_total = [buckets_total[i] + buckets[i] for i in range(len(buckets_total))] 
193                     t2_lat_tsc = t3_lat_tsc
194                     lat_avail = True
195                 t3_rx, t3_non_dp_rx, t3_tx, t3_non_dp_tx, t3_drop, t3_tx_fail, t3_tsc, tsc_hz = self.gen_machine.core_stats()
196                 if t3_tsc != t2_tsc:
197                     single_core_measurement_duration = (t3_tsc - t2_tsc) * 1.0 / tsc_hz  # time difference between the 2 measurements, expressed in seconds.
198                     tot_core_measurement_duration = tot_core_measurement_duration + single_core_measurement_duration
199                     delta_rx = t3_rx - t2_rx
200                     tot_rx += delta_rx
201                     delta_non_dp_rx = t3_non_dp_rx - t2_non_dp_rx
202                     tot_non_dp_rx += delta_non_dp_rx
203                     delta_tx = t3_tx - t2_tx
204                     tot_tx += delta_tx
205                     delta_non_dp_tx = t3_non_dp_tx - t2_non_dp_tx
206                     tot_non_dp_tx += delta_non_dp_tx
207                     delta_dp_tx = delta_tx -delta_non_dp_tx
208                     delta_dp_rx = delta_rx -delta_non_dp_rx
209                     delta_dp_drop = delta_dp_tx - delta_dp_rx
210                     tot_dp_drop += delta_dp_drop
211                     delta_drop = t3_drop - t2_drop
212                     tot_drop += delta_drop
213                     t2_rx, t2_non_dp_rx, t2_tx, t2_non_dp_tx, t2_drop, t2_tx_fail, t2_tsc = t3_rx, t3_non_dp_rx, t3_tx, t3_non_dp_tx, t3_drop, t3_tx_fail, t3_tsc
214                     core_avail = True
215                 if self.sut_machine!=None:
216                     t3_sut_rx, t3_sut_non_dp_rx, t3_sut_tx, t3_sut_non_dp_tx, t3_sut_drop, t3_sut_tx_fail, t3_sut_tsc, sut_tsc_hz = self.sut_machine.core_stats()
217                     if t3_sut_tsc != t2_sut_tsc:
218                         single_sut_core_measurement_duration = (t3_sut_tsc - t2_sut_tsc) * 1.0 / tsc_hz  # time difference between the 2 measurements, expressed in seconds.
219                         tot_sut_core_measurement_duration = tot_sut_core_measurement_duration + single_sut_core_measurement_duration
220                         tot_sut_rx += t3_sut_rx - t2_sut_rx
221                         tot_sut_non_dp_rx += t3_sut_non_dp_rx - t2_sut_non_dp_rx
222                         delta_sut_tx = t3_sut_tx - t2_sut_tx
223                         tot_sut_tx += delta_sut_tx
224                         delta_sut_non_dp_tx = t3_sut_non_dp_tx - t2_sut_non_dp_tx
225                         tot_sut_non_dp_tx += delta_sut_non_dp_tx 
226                         t2_sut_rx, t2_sut_non_dp_rx, t2_sut_tx, t2_sut_non_dp_tx, t2_sut_drop, t2_sut_tx_fail, t2_sut_tsc = t3_sut_rx, t3_sut_non_dp_rx, t3_sut_tx, t3_sut_non_dp_tx, t3_sut_drop, t3_sut_tx_fail, t3_sut_tsc
227                         sut_avail = True
228                 if self.test['test'] == 'fixed_rate':
229                     if lat_avail == core_avail == True:
230                         lat_avail = core_avail = False
231                         pps_req_tx = (delta_tx + delta_drop - delta_rx)/single_core_measurement_duration/1000000
232                         pps_tx = delta_tx/single_core_measurement_duration/1000000
233                         if self.sut_machine != None and sut_avail:
234                             pps_sut_tx = delta_sut_tx/single_sut_core_measurement_duration/1000000
235                             sut_avail = False
236                         else:
237                             pps_sut_tx = None
238                         pps_rx = delta_rx/single_core_measurement_duration/1000000
239                         RapidLog.info(self.report_result(flow_number, size,
240                             speed, pps_req_tx, pps_tx, pps_sut_tx, pps_rx,
241                             lat_avg_sample, sample_percentile, percentile_max,
242                             lat_max_sample, delta_dp_tx, delta_dp_rx,
243                             tot_dp_drop, single_core_measurement_duration))
244             #Stop generating
245             self.gen_machine.stop_gen_cores()
246             r += 1
247             lat_avg = old_div(lat_avg, float(tot_lat_measurement_duration))
248             used_avg = old_div(used_avg, float(tot_lat_measurement_duration))
249             t4_tsc = t2_tsc
250             while t4_tsc == t2_tsc:
251                 t4_rx, t4_non_dp_rx, t4_tx, t4_non_dp_tx, t4_drop, t4_tx_fail, t4_tsc, abs_tsc_hz = self.gen_machine.core_stats()
252             if self.test['test'] == 'fixed_rate':
253                 t4_lat_tsc = t2_lat_tsc
254                 while t4_lat_tsc == t2_lat_tsc:
255                     lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t4_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
256                 sample_count = 0
257                 lat_samples = sum(buckets)
258                 for percentile, bucket in enumerate(buckets,start=1):
259                     sample_count += bucket
260                     if sample_count > lat_samples * LAT_PERCENTILE:
261                         break
262                 percentile_max = (percentile == len(buckets))
263                 percentile = percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
264                 lat_max = lat_max_sample
265                 lat_avg = lat_avg_sample
266                 delta_rx = t4_rx - t2_rx
267                 delta_non_dp_rx = t4_non_dp_rx - t2_non_dp_rx
268                 delta_tx = t4_tx - t2_tx
269                 delta_non_dp_tx = t4_non_dp_tx - t2_non_dp_tx
270                 delta_dp_tx = delta_tx -delta_non_dp_tx
271                 delta_dp_rx = delta_rx -delta_non_dp_rx
272                 dp_tx = delta_dp_tx
273                 dp_rx = delta_dp_rx
274                 tot_dp_drop += delta_dp_tx - delta_dp_rx
275                 pps_req_tx = None
276                 pps_tx = None
277                 pps_sut_tx = None
278                 pps_rx = None
279                 drop_rate = 100.0*(dp_tx-dp_rx)/dp_tx
280                 tot_core_measurement_duration = None
281                 break ## Not really needed since the while loop will stop when evaluating the value of r
282             else:
283                 sample_count = 0
284                 for percentile, bucket in enumerate(buckets_total,start=1):
285                     sample_count += bucket
286                     if sample_count > tot_lat_samples * LAT_PERCENTILE:
287                         break
288                 percentile_max = (percentile == len(buckets_total))
289                 percentile = percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
290                 pps_req_tx = (tot_tx + tot_drop - tot_rx)/tot_core_measurement_duration/1000000.0 # tot_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
291                 pps_tx = tot_tx/tot_core_measurement_duration/1000000.0 # tot_tx is all generated packets actually accepted by the interface
292                 pps_rx = tot_rx/tot_core_measurement_duration/1000000.0 # tot_rx is all packets received by the nop task = all packets received in the gen VM
293                 if self.sut_machine != None and sut_avail:
294                     pps_sut_tx = tot_sut_tx / tot_sut_core_measurement_duration / 1000000.0
295                 else:
296                     pps_sut_tx = None
297                 dp_tx = (t4_tx - t1_tx) - (t4_non_dp_tx - t1_non_dp_tx)
298                 dp_rx = (t4_rx - t1_rx) - (t4_non_dp_rx - t1_non_dp_rx)
299                 tot_dp_drop = dp_tx - dp_rx
300                 drop_rate = 100.0*tot_dp_drop/dp_tx
301                 if ((drop_rate < self.test['drop_rate_threshold']) or (tot_dp_drop == self.test['drop_rate_threshold'] ==0) or (tot_dp_drop > self.test['maxz'])):
302                     break
303         return(pps_req_tx,pps_tx,pps_sut_tx,pps_rx,lat_avg,percentile,percentile_max,lat_max,dp_tx,dp_rx,tot_dp_drop,(t4_tx_fail - t1_tx_fail),drop_rate,lat_min,used_avg,r,tot_core_measurement_duration)