4 ## Copyright (c) 2020 Intel Corporation
6 ## Licensed under the Apache License, Version 2.0 (the "License");
7 ## you may not use this file except in compliance with the License.
8 ## You may obtain a copy of the License at
11 ## http://www.apache.org/licenses/LICENSE-2.0
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.
21 from past.utils import old_div
22 from rapid_log import RapidLog
23 from rapid_log import bcolors
26 class RapidTest(object):
28 Class to manage the testing
30 def __init__(self, test_param, runtime, pushgateway, environment_file ):
31 self.test = test_param
32 self.test['runtime'] = runtime
33 self.test['pushgateway'] = pushgateway
34 self.test['environment_file'] = environment_file
35 if 'maxr' not in self.test.keys():
37 if 'maxz' not in self.test.keys():
38 self.test['maxz'] = inf
41 def get_percentageof10Gbps(pps_speed,size):
42 # speed is given in pps, returning % of 10Gb/s
43 # 12 bytes is the inter packet gap
44 # pre-amble is 7 bytes
45 # SFD (start of frame delimiter) is 1 byte
46 # Total of 20 bytes overhead per packet
47 return (pps_speed / 1000000.0 * 0.08 * (size+20))
50 def get_pps(speed,size):
51 # speed is given in % of 10Gb/s, returning Mpps
52 # 12 bytes is the inter packet gap
53 # pre-amble is 7 bytes
54 # SFD (start of frame delimiter) is 1 byte
55 # Total of 20 bytes overhead per packet
56 return (speed * 100.0 / (8*(size+20)))
59 def get_speed(packet_speed,size):
60 # return speed in Gb/s
61 # 12 bytes is the inter packet gap
62 # pre-amble is 7 bytes
63 # SFD (start of frame delimiter) is 1 byte
64 # Total of 20 bytes overhead per packet
65 return (packet_speed / 1000.0 * (8*(size+20)))
68 def set_background_flows(background_machines, number_of_flows):
69 for machine in background_machines:
70 _ = machine.set_flows(number_of_flows)
73 def set_background_speed(background_machines, speed):
74 for machine in background_machines:
75 machine.set_generator_speed(speed)
78 def set_background_size(background_machines, imix):
79 # imixs is a list of packet sizes
80 for machine in background_machines:
81 machine.set_udp_packet_size(imix)
84 def start_background_traffic(background_machines):
85 for machine in background_machines:
89 def stop_background_traffic(background_machines):
90 for machine in background_machines:
94 def report_result(flow_number, size, speed, pps_req_tx, pps_tx, pps_sut_tx,
95 pps_rx, lat_avg, lat_perc, lat_perc_max, lat_max, tx, rx, tot_drop,
96 elapsed_time,speed_prefix='', lat_avg_prefix='', lat_perc_prefix='',
97 lat_max_prefix='', abs_drop_rate_prefix='', drop_rate_prefix=''):
99 flow_number_str = '| ({:>4}) |'.format(abs(flow_number))
101 flow_number_str = '|{:>7} |'.format(flow_number)
102 if pps_req_tx is None:
103 pps_req_tx_str = '{0: >14}'.format(' NA |')
105 pps_req_tx_str = '{:>7.3f} Mpps |'.format(pps_req_tx)
107 pps_tx_str = '{0: >14}'.format(' NA |')
109 pps_tx_str = '{:>7.3f} Mpps |'.format(pps_tx)
110 if pps_sut_tx is None:
111 pps_sut_tx_str = '{0: >14}'.format(' NA |')
113 pps_sut_tx_str = '{:>7.3f} Mpps |'.format(pps_sut_tx)
115 pps_rx_str = '{0: >25}'.format('NA |')
117 pps_rx_str = bcolors.OKBLUE + '{:>4.1f} Gb/s |{:7.3f} Mpps {}|'.format(RapidTest.get_speed(pps_rx,size),pps_rx,bcolors.ENDC)
119 tot_drop_str = ' | NA | '
121 tot_drop_str = ' | {:>9.0f} | '.format(tot_drop)
123 lat_perc_str = ' |{:^10.10}|'.format('NA')
124 elif lat_perc_max == True:
125 lat_perc_str = '|>{}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
127 lat_perc_str = '| {}{:>5.0f} us{} |'.format(lat_perc_prefix,float(lat_perc), bcolors.ENDC)
128 if elapsed_time is None:
129 elapsed_time_str = ' NA |'
131 elapsed_time_str = '{:>3.0f} |'.format(elapsed_time)
132 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)
134 def run_iteration(self, requested_duration, flow_number, size, speed):
135 BUCKET_SIZE_EXP = self.gen_machine.bucket_size_exp
136 LAT_PERCENTILE = self.test['lat_percentile']
139 while (r < self.test['maxr']):
140 time.sleep(sleep_time)
141 # 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
142 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()
143 t1_dp_rx = t1_rx - t1_non_dp_rx
144 t1_dp_tx = t1_tx - t1_non_dp_tx
145 self.gen_machine.start_gen_cores()
146 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.
147 if self.sut_machine!= None:
148 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()
149 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()
151 dp_tx = tx - (t2_non_dp_tx - t1_non_dp_tx )
152 dp_rx = t2_rx - t1_rx - (t2_non_dp_rx - t1_non_dp_rx)
153 tot_dp_drop = dp_tx - dp_rx
155 RapidLog.critical("TX = 0. Test interrupted since no packet has been sent.")
157 RapidLog.critical("Only non-dataplane packets (e.g. ARP) sent. Test interrupted since no packet has been sent.")
158 # Ask PROX to calibrate the bucket size once we have a PROX function to do this.
159 # Measure latency statistics per second
160 lat_min, lat_max, lat_avg, used_avg, t2_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
161 lat_samples = sum(buckets)
163 for sample_percentile, bucket in enumerate(buckets,start=1):
164 sample_count += bucket
165 if sample_count > (lat_samples * LAT_PERCENTILE):
167 percentile_max = (sample_percentile == len(buckets))
168 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
169 if self.test['test'] == 'fixed_rate':
170 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))
171 tot_rx = tot_non_dp_rx = tot_tx = tot_non_dp_tx = tot_drop = 0
172 lat_avg = used_avg = 0
173 buckets_total = [0] * 128
175 tot_lat_measurement_duration = float(0)
176 tot_core_measurement_duration = float(0)
177 tot_sut_core_measurement_duration = float(0)
178 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
179 lat_avail = core_avail = sut_avail = False
180 while (tot_core_measurement_duration - float(requested_duration) <= 0.1) or (tot_lat_measurement_duration - float(requested_duration) <= 0.1):
182 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t3_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
183 # Get statistics after some execution time
184 if t3_lat_tsc != t2_lat_tsc:
185 single_lat_measurement_duration = (t3_lat_tsc - t2_lat_tsc) * 1.0 / lat_hz # time difference between the 2 measurements, expressed in seconds.
186 # A second has passed in between to lat_stats requests. Hence we need to process the results
187 tot_lat_measurement_duration = tot_lat_measurement_duration + single_lat_measurement_duration
188 if lat_min > lat_min_sample:
189 lat_min = lat_min_sample
190 if lat_max < lat_max_sample:
191 lat_max = lat_max_sample
192 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
193 used_avg = used_avg + used_sample * single_lat_measurement_duration # and give it more weigth.
194 lat_samples = sum(buckets)
195 tot_lat_samples += lat_samples
197 for sample_percentile, bucket in enumerate(buckets,start=1):
198 sample_count += bucket
199 if sample_count > lat_samples * LAT_PERCENTILE:
201 percentile_max = (sample_percentile == len(buckets))
202 sample_percentile = sample_percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
203 buckets_total = [buckets_total[i] + buckets[i] for i in range(len(buckets_total))]
204 t2_lat_tsc = t3_lat_tsc
206 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()
208 single_core_measurement_duration = (t3_tsc - t2_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
209 tot_core_measurement_duration = tot_core_measurement_duration + single_core_measurement_duration
210 delta_rx = t3_rx - t2_rx
212 delta_non_dp_rx = t3_non_dp_rx - t2_non_dp_rx
213 tot_non_dp_rx += delta_non_dp_rx
214 delta_tx = t3_tx - t2_tx
216 delta_non_dp_tx = t3_non_dp_tx - t2_non_dp_tx
217 tot_non_dp_tx += delta_non_dp_tx
218 delta_dp_tx = delta_tx -delta_non_dp_tx
219 delta_dp_rx = delta_rx -delta_non_dp_rx
220 delta_dp_drop = delta_dp_tx - delta_dp_rx
221 tot_dp_drop += delta_dp_drop
222 delta_drop = t3_drop - t2_drop
223 tot_drop += delta_drop
224 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
226 if self.sut_machine!=None:
227 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()
228 if t3_sut_tsc != t2_sut_tsc:
229 single_sut_core_measurement_duration = (t3_sut_tsc - t2_sut_tsc) * 1.0 / tsc_hz # time difference between the 2 measurements, expressed in seconds.
230 tot_sut_core_measurement_duration = tot_sut_core_measurement_duration + single_sut_core_measurement_duration
231 tot_sut_rx += t3_sut_rx - t2_sut_rx
232 tot_sut_non_dp_rx += t3_sut_non_dp_rx - t2_sut_non_dp_rx
233 delta_sut_tx = t3_sut_tx - t2_sut_tx
234 tot_sut_tx += delta_sut_tx
235 delta_sut_non_dp_tx = t3_sut_non_dp_tx - t2_sut_non_dp_tx
236 tot_sut_non_dp_tx += delta_sut_non_dp_tx
237 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
239 if self.test['test'] == 'fixed_rate':
240 if lat_avail == core_avail == True:
241 lat_avail = core_avail = False
242 pps_req_tx = (delta_tx + delta_drop - delta_rx)/single_core_measurement_duration/1000000
243 pps_tx = delta_tx/single_core_measurement_duration/1000000
244 if self.sut_machine != None and sut_avail:
245 pps_sut_tx = delta_sut_tx/single_sut_core_measurement_duration/1000000
249 pps_rx = delta_rx/single_core_measurement_duration/1000000
250 RapidLog.info(self.report_result(flow_number, size,
251 speed, pps_req_tx, pps_tx, pps_sut_tx, pps_rx,
252 lat_avg_sample, sample_percentile, percentile_max,
253 lat_max_sample, delta_dp_tx, delta_dp_rx,
254 tot_dp_drop, single_core_measurement_duration))
256 self.gen_machine.stop_gen_cores()
258 lat_avg = old_div(lat_avg, float(tot_lat_measurement_duration))
259 used_avg = old_div(used_avg, float(tot_lat_measurement_duration))
261 while t4_tsc == t2_tsc:
262 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()
263 if self.test['test'] == 'fixed_rate':
264 t4_lat_tsc = t2_lat_tsc
265 while t4_lat_tsc == t2_lat_tsc:
266 lat_min_sample, lat_max_sample, lat_avg_sample, used_sample, t4_lat_tsc, lat_hz, buckets = self.gen_machine.lat_stats()
268 lat_samples = sum(buckets)
269 for percentile, bucket in enumerate(buckets,start=1):
270 sample_count += bucket
271 if sample_count > lat_samples * LAT_PERCENTILE:
273 percentile_max = (percentile == len(buckets))
274 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
275 lat_max = lat_max_sample
276 lat_avg = lat_avg_sample
277 delta_rx = t4_rx - t2_rx
278 delta_non_dp_rx = t4_non_dp_rx - t2_non_dp_rx
279 delta_tx = t4_tx - t2_tx
280 delta_non_dp_tx = t4_non_dp_tx - t2_non_dp_tx
281 delta_dp_tx = delta_tx -delta_non_dp_tx
282 delta_dp_rx = delta_rx -delta_non_dp_rx
285 tot_dp_drop += delta_dp_tx - delta_dp_rx
290 drop_rate = 100.0*(dp_tx-dp_rx)/dp_tx
291 tot_core_measurement_duration = None
292 break ## Not really needed since the while loop will stop when evaluating the value of r
295 for percentile, bucket in enumerate(buckets_total,start=1):
296 sample_count += bucket
297 if sample_count > tot_lat_samples * LAT_PERCENTILE:
299 percentile_max = (percentile == len(buckets_total))
300 percentile = percentile * float(2 ** BUCKET_SIZE_EXP) / (old_div(float(lat_hz),float(10**6)))
301 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
302 pps_tx = tot_tx/tot_core_measurement_duration/1000000.0 # tot_tx is all generated packets actually accepted by the interface
303 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
304 if self.sut_machine != None and sut_avail:
305 pps_sut_tx = tot_sut_tx / tot_sut_core_measurement_duration / 1000000.0
308 dp_tx = (t4_tx - t1_tx) - (t4_non_dp_tx - t1_non_dp_tx)
309 dp_rx = (t4_rx - t1_rx) - (t4_non_dp_rx - t1_non_dp_rx)
310 tot_dp_drop = dp_tx - dp_rx
311 drop_rate = 100.0*tot_dp_drop/dp_tx
312 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'])):
314 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)