261c8bb18bbc601fa77a96402bd87d2e8595f879
[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         return (pps_speed / 1000000.0 * 0.08 * (size+28))
33
34     @staticmethod
35     def get_pps(speed,size):
36         # speed is given in % of 10Gb/s, returning Mpps
37         return (speed * 100.0 / (8*(size+28)))
38
39     @staticmethod
40     def get_speed(packet_speed,size):
41         # return speed in Gb/s
42         return (packet_speed / 1000.0 * (8*(size+28)))
43
44     @staticmethod
45     def set_background_flows(background_machines, number_of_flows):
46         for machine in background_machines:
47             machine.set_flows(number_of_flows)
48
49     @staticmethod
50     def set_background_speed(background_machines, speed):
51         for machine in background_machines:
52             machine.set_generator_speed(speed)
53
54     @staticmethod
55     def set_background_size(background_machines, size):
56         for machine in background_machines:
57             machine.set_udp_packet_size(size)
58
59     @staticmethod
60     def start_background_traffic(background_machines):
61         for machine in background_machines:
62             machine.start()
63
64     @staticmethod
65     def stop_background_traffic(background_machines):
66         for machine in background_machines:
67             machine.stop()
68
69     @staticmethod
70     def report_result(flow_number, size, speed, pps_req_tx, pps_tx, pps_sut_tx,
71         pps_rx, lat_avg, lat_perc, lat_perc_max, lat_max, tx, rx, tot_drop,
72         elapsed_time,speed_prefix='', lat_avg_prefix='', lat_perc_prefix='',
73         lat_max_prefix='', abs_drop_rate_prefix='', drop_rate_prefix=''):
74         if flow_number < 0:
75             flow_number_str = '| ({:>4}) |'.format(abs(flow_number))
76         else:
77             flow_number_str = '|{:>7} |'.format(flow_number)
78         if pps_req_tx is None:
79             pps_req_tx_str = '{0: >14}'.format('   NA     |')
80         else:
81             pps_req_tx_str = '{:>7.3f} Mpps |'.format(pps_req_tx)
82         if pps_tx is None:
83             pps_tx_str = '{0: >14}'.format('   NA     |')
84         else:
85             pps_tx_str = '{:>7.3f} Mpps |'.format(pps_tx) 
86         if pps_sut_tx is None:
87             pps_sut_tx_str = '{0: >14}'.format('   NA     |')
88         else:
89             pps_sut_tx_str = '{:>7.3f} Mpps |'.format(pps_sut_tx)
90         if pps_rx is None:
91             pps_rx_str = '{0: >25}'.format('NA        |')
92         else:
93             pps_rx_str = bcolors.OKBLUE + '{:>4.1f} Gb/s |{:7.3f} Mpps {}|'.format(RapidTest.get_speed(pps_rx,size),pps_rx,bcolors.ENDC)
94         if tot_drop is None:
95             tot_drop_str = ' |       NA  | '
96         else:
97             tot_drop_str = ' | {:>9.0f} | '.format(tot_drop)
98         if lat_perc is None:
99             lat_perc_str = ' |{:^10.10}|'.format('NA')
100         elif lat_perc_max == True:
101             lat_perc_str = '|>{}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC) 
102         else:
103             lat_perc_str = '| {}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC) 
104         if elapsed_time is None:
105             elapsed_time_str = ' NA |'
106         else:
107             elapsed_time_str = '{:>3.0f} |'.format(elapsed_time)
108         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)
109             
110     def run_iteration(self, requested_duration, flow_number, size, speed):
111         BUCKET_SIZE_EXP = self.gen_machine.bucket_size_exp
112         LAT_PERCENTILE = self.test['lat_percentile']
113         r = 0;
114         sleep_time = 2
115         while (r < self.test['maxr']):
116             time.sleep(sleep_time)
117             # 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
118             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()
119             t1_dp_rx = t1_rx - t1_non_dp_rx
120             t1_dp_tx = t1_tx - t1_non_dp_tx
121             self.gen_machine.start_gen_cores()
122             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.
123             if self.sut_machine!= None:
124                 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()
125             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()
126             tx = t2_tx - t1_tx
127             dp_tx =  tx - (t2_non_dp_tx - t1_non_dp_tx )
128             dp_rx =  t2_rx - t1_rx - (t2_non_dp_rx - t1_non_dp_rx) 
129             tot_dp_drop = dp_tx - dp_rx
130             if tx == 0:
131                 RapidLog.critical("TX = 0. Test interrupted since no packet has been sent.")
132             if dp_tx == 0:
133                 RapidLog.critical("Only non-dataplane packets (e.g. ARP) sent. Test interrupted since no packet has been sent.")
134             # Ask PROX to calibrate the bucket size once we have a PROX function to do this.
135             # Measure latency statistics per second
136             lat_min, lat_max, lat_avg, used_avg, t2_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
137             lat_samples = sum(buckets)
138             sample_count = 0
139             for sample_percentile, bucket in enumerate(buckets,start=1):
140                 sample_count += bucket
141                 if sample_count > (lat_samples * LAT_PERCENTILE):
142                     break
143             percentile_max = (sample_percentile == len(buckets))
144             sample_percentile = sample_percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
145             if self.test['test'] == 'fixed_rate':
146                 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))
147             tot_rx = tot_non_dp_rx = tot_tx = tot_non_dp_tx = tot_drop = 0
148             lat_avg = used_avg = 0
149             buckets_total = [0] * 128
150             tot_lat_samples = 0
151             tot_lat_measurement_duration = float(0)
152             tot_core_measurement_duration = float(0)
153             tot_sut_core_measurement_duration = float(0)
154             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
155             lat_avail = core_avail = sut_avail = False
156             while (tot_core_measurement_duration - float(requested_duration) <= 0.1) or (tot_lat_measurement_duration - float(requested_duration) <= 0.1):
157                 time.sleep(0.5)
158                 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t3_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
159                 # Get statistics after some execution time
160                 if t3_lat_tsc != t2_lat_tsc:
161                     single_lat_measurement_duration = (t3_lat_tsc - t2_lat_tsc) * 1.0 / lat_hz  # time difference between the 2 measurements, expressed in seconds.
162                     # A second has passed in between to lat_stats requests. Hence we need to process the results
163                     tot_lat_measurement_duration = tot_lat_measurement_duration + single_lat_measurement_duration
164                     if lat_min > lat_min_sample:
165                         lat_min = lat_min_sample
166                     if lat_max < lat_max_sample:
167                         lat_max = lat_max_sample
168                     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
169                     used_avg = used_avg + used_sample * single_lat_measurement_duration  # and give it more weigth.
170                     lat_samples = sum(buckets)
171                     tot_lat_samples += lat_samples
172                     sample_count = 0
173                     for sample_percentile, bucket in enumerate(buckets,start=1):
174                         sample_count += bucket
175                         if sample_count > lat_samples * LAT_PERCENTILE:
176                             break
177                     percentile_max = (sample_percentile == len(buckets))
178                     sample_percentile = sample_percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
179                     buckets_total = [buckets_total[i] + buckets[i] for i in range(len(buckets_total))] 
180                     t2_lat_tsc = t3_lat_tsc
181                     lat_avail = True
182                 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()
183                 if t3_tsc != t2_tsc:
184                     single_core_measurement_duration = (t3_tsc - t2_tsc) * 1.0 / tsc_hz  # time difference between the 2 measurements, expressed in seconds.
185                     tot_core_measurement_duration = tot_core_measurement_duration + single_core_measurement_duration
186                     delta_rx = t3_rx - t2_rx
187                     tot_rx += delta_rx
188                     delta_non_dp_rx = t3_non_dp_rx - t2_non_dp_rx
189                     tot_non_dp_rx += delta_non_dp_rx
190                     delta_tx = t3_tx - t2_tx
191                     tot_tx += delta_tx
192                     delta_non_dp_tx = t3_non_dp_tx - t2_non_dp_tx
193                     tot_non_dp_tx += delta_non_dp_tx
194                     delta_dp_tx = delta_tx -delta_non_dp_tx
195                     delta_dp_rx = delta_rx -delta_non_dp_rx
196                     delta_dp_drop = delta_dp_tx - delta_dp_rx
197                     tot_dp_drop += delta_dp_drop
198                     delta_drop = t3_drop - t2_drop
199                     tot_drop += delta_drop
200                     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
201                     core_avail = True
202                 if self.sut_machine!=None:
203                     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()
204                     if t3_sut_tsc != t2_sut_tsc:
205                         single_sut_core_measurement_duration = (t3_sut_tsc - t2_sut_tsc) * 1.0 / tsc_hz  # time difference between the 2 measurements, expressed in seconds.
206                         tot_sut_core_measurement_duration = tot_sut_core_measurement_duration + single_sut_core_measurement_duration
207                         tot_sut_rx += t3_sut_rx - t2_sut_rx
208                         tot_sut_non_dp_rx += t3_sut_non_dp_rx - t2_sut_non_dp_rx
209                         delta_sut_tx = t3_sut_tx - t2_sut_tx
210                         tot_sut_tx += delta_sut_tx
211                         delta_sut_non_dp_tx = t3_sut_non_dp_tx - t2_sut_non_dp_tx
212                         tot_sut_non_dp_tx += delta_sut_non_dp_tx 
213                         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
214                         sut_avail = True
215                 if self.test['test'] == 'fixed_rate':
216                     if lat_avail == core_avail == True:
217                         lat_avail = core_avail = False
218                         pps_req_tx = (delta_tx + delta_drop - delta_rx)/single_core_measurement_duration/1000000
219                         pps_tx = delta_tx/single_core_measurement_duration/1000000
220                         if self.sut_machine != None and sut_avail:
221                             pps_sut_tx = delta_sut_tx/single_sut_core_measurement_duration/1000000
222                             sut_avail = False
223                         else:
224                             pps_sut_tx = None
225                         pps_rx = delta_rx/single_core_measurement_duration/1000000
226                         RapidLog.info(self.report_result(flow_number, size,
227                             speed, pps_req_tx, pps_tx, pps_sut_tx, pps_rx,
228                             lat_avg_sample, sample_percentile, percentile_max,
229                             lat_max_sample, delta_dp_tx, delta_dp_rx,
230                             tot_dp_drop, single_core_measurement_duration))
231             #Stop generating
232             self.gen_machine.stop_gen_cores()
233             r += 1
234             lat_avg = old_div(lat_avg, float(tot_lat_measurement_duration))
235             used_avg = old_div(used_avg, float(tot_lat_measurement_duration))
236             t4_tsc = t2_tsc
237             while t4_tsc == t2_tsc:
238                 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()
239             if self.test['test'] == 'fixed_rate':
240                 t4_lat_tsc = t2_lat_tsc
241                 while t4_lat_tsc == t2_lat_tsc:
242                     lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t4_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
243                 sample_count = 0
244                 lat_samples = sum(buckets)
245                 for percentile, bucket in enumerate(buckets,start=1):
246                     sample_count += bucket
247                     if sample_count > lat_samples * LAT_PERCENTILE:
248                         break
249                 percentile_max = (percentile == len(buckets))
250                 percentile = percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
251                 lat_max = lat_max_sample
252                 lat_avg = lat_avg_sample
253                 delta_rx = t4_rx - t2_rx
254                 delta_non_dp_rx = t4_non_dp_rx - t2_non_dp_rx
255                 delta_tx = t4_tx - t2_tx
256                 delta_non_dp_tx = t4_non_dp_tx - t2_non_dp_tx
257                 delta_dp_tx = delta_tx -delta_non_dp_tx
258                 delta_dp_rx = delta_rx -delta_non_dp_rx
259                 dp_tx = delta_dp_tx
260                 dp_rx = delta_dp_rx
261                 tot_dp_drop += delta_dp_tx - delta_dp_rx
262                 pps_req_tx = None
263                 pps_tx = None
264                 pps_sut_tx = None
265                 pps_rx = None
266                 drop_rate = 100.0*(dp_tx-dp_rx)/dp_tx
267                 tot_core_measurement_duration = None
268                 break ## Not really needed since the while loop will stop when evaluating the value of r
269             else:
270                 sample_count = 0
271                 for percentile, bucket in enumerate(buckets_total,start=1):
272                     sample_count += bucket
273                     if sample_count > tot_lat_samples * LAT_PERCENTILE:
274                         break
275                 percentile_max = (percentile == len(buckets_total))
276                 percentile = percentile *  float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
277                 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
278                 pps_tx = tot_tx/tot_core_measurement_duration/1000000.0 # tot_tx is all generated packets actually accepted by the interface
279                 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
280                 if self.sut_machine != None and sut_avail:
281                     pps_sut_tx = tot_sut_tx / tot_sut_core_measurement_duration / 1000000.0
282                 else:
283                     pps_sut_tx = None
284                 dp_tx = (t4_tx - t1_tx) - (t4_non_dp_tx - t1_non_dp_tx)
285                 dp_rx = (t4_rx - t1_rx) - (t4_non_dp_rx - t1_non_dp_rx)
286                 tot_dp_drop = dp_tx - dp_rx
287                 drop_rate = 100.0*tot_dp_drop/dp_tx
288                 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'])):
289                     break
290         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)