add yardstick iruya 9.0.0 release notes
[yardstick.git] / yardstick / network_services / traffic_profile / ixia_rfc2544.py
1 # Copyright (c) 2016-2019 Intel Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import logging
16 import collections
17
18 from yardstick.common import utils
19 from yardstick.network_services.traffic_profile import base as tp_base
20 from yardstick.network_services.traffic_profile import trex_traffic_profile
21
22
23 LOG = logging.getLogger(__name__)
24
25
26 class IXIARFC2544Profile(trex_traffic_profile.TrexProfile):
27
28     UPLINK = 'uplink'
29     DOWNLINK = 'downlink'
30     DROP_PERCENT_ROUND = 6
31     STATUS_SUCCESS = "Success"
32     STATUS_FAIL = "Failure"
33
34     def __init__(self, yaml_data):
35         super(IXIARFC2544Profile, self).__init__(yaml_data)
36         self.rate = self.config.frame_rate
37         self.rate_unit = self.config.rate_unit
38         self.iteration = 0
39         self.full_profile = {}
40
41     def _get_ip_and_mask(self, ip_range):
42         _ip_range = ip_range.split('-')
43         if len(_ip_range) == 1:
44             return _ip_range[0], None
45
46         mask = utils.get_mask_from_ip_range(_ip_range[0], _ip_range[1])
47         return _ip_range[0], mask
48
49     def _get_fixed_and_mask(self, port_range):
50         _port_range = str(port_range).split('-')
51         if len(_port_range) == 1:
52             return int(_port_range[0]), 0
53
54         return int(_port_range[0]), int(_port_range[1])
55
56     def _get_ixia_traffic_profile(self, profile_data, mac=None):
57         mac = {} if mac is None else mac
58         result = {}
59         for traffickey, values in profile_data.items():
60             if not traffickey.startswith((self.UPLINK, self.DOWNLINK)):
61                 continue
62
63             # values should be single-item dict, so just grab the first item
64             try:
65                 key, value = next(iter(values.items()))
66             except StopIteration:
67                 result[traffickey] = {}
68                 continue
69
70             port_id = value.get('id', 1)
71             port_index = port_id - 1
72
73             result[traffickey] = {
74                 'bidir': False,
75                 'id': port_id,
76                 'rate': self.rate,
77                 'rate_unit': self.rate_unit,
78                 'outer_l2': {},
79                 'outer_l3': {},
80                 'outer_l4': {},
81             }
82
83             frame_rate = value.get('frame_rate')
84             if frame_rate:
85                 flow_rate, flow_rate_unit = self.config.parse_rate(frame_rate)
86                 result[traffickey]['rate'] = flow_rate
87                 result[traffickey]['rate_unit'] = flow_rate_unit
88
89             outer_l2 = value.get('outer_l2')
90             if outer_l2:
91                 result[traffickey]['outer_l2'].update({
92                     'framesize': outer_l2.get('framesize'),
93                     'framesPerSecond': True,
94                     'QinQ': outer_l2.get('QinQ'),
95                     'srcmac': mac.get('src_mac_{}'.format(port_index)),
96                     'dstmac': mac.get('dst_mac_{}'.format(port_index)),
97                 })
98
99             if value.get('outer_l3v4'):
100                 outer_l3 = value['outer_l3v4']
101                 src_key, dst_key = 'srcip4', 'dstip4'
102             else:
103                 outer_l3 = value.get('outer_l3v6')
104                 src_key, dst_key = 'srcip6', 'dstip6'
105             if outer_l3:
106                 srcip = srcmask = dstip = dstmask = None
107                 if outer_l3.get(src_key):
108                     srcip, srcmask = self._get_ip_and_mask(outer_l3[src_key])
109                 if outer_l3.get(dst_key):
110                     dstip, dstmask = self._get_ip_and_mask(outer_l3[dst_key])
111
112                 result[traffickey]['outer_l3'].update({
113                     'count': outer_l3.get('count', 1),
114                     'dscp': outer_l3.get('dscp'),
115                     'ttl': outer_l3.get('ttl'),
116                     'srcseed': outer_l3.get('srcseed', 1),
117                     'dstseed': outer_l3.get('dstseed', 1),
118                     'srcip': srcip,
119                     'dstip': dstip,
120                     'srcmask': srcmask,
121                     'dstmask': dstmask,
122                     'type': key,
123                     'proto': outer_l3.get('proto'),
124                     'priority': outer_l3.get('priority')
125                 })
126
127             outer_l4 = value.get('outer_l4')
128             if outer_l4:
129                 src_port = src_port_mask = dst_port = dst_port_mask = None
130                 if outer_l4.get('srcport'):
131                     src_port, src_port_mask = (
132                         self._get_fixed_and_mask(outer_l4['srcport']))
133
134                 if outer_l4.get('dstport'):
135                     dst_port, dst_port_mask = (
136                         self._get_fixed_and_mask(outer_l4['dstport']))
137
138                 result[traffickey]['outer_l4'].update({
139                     'srcport': src_port,
140                     'dstport': dst_port,
141                     'srcportmask': src_port_mask,
142                     'dstportmask': dst_port_mask,
143                     'count': outer_l4.get('count', 1),
144                     'seed': outer_l4.get('seed', 1),
145                 })
146
147         return result
148
149     def _ixia_traffic_generate(self, traffic, ixia_obj, traffic_gen):
150         ixia_obj.update_frame(traffic, self.config.duration)
151         ixia_obj.update_ip_packet(traffic)
152         ixia_obj.update_l4(traffic)
153         self._update_traffic_tracking_options(traffic_gen)
154         ixia_obj.start_traffic()
155
156     def _update_traffic_tracking_options(self, traffic_gen):
157         traffic_gen.update_tracking_options()
158
159     def update_traffic_profile(self, traffic_generator):
160         def port_generator():
161             for vld_id, intfs in sorted(traffic_generator.networks.items()):
162                 if not vld_id.startswith((self.UPLINK, self.DOWNLINK)):
163                     continue
164                 profile_data = self.params.get(vld_id)
165                 if not profile_data:
166                     continue
167                 self.profile_data = profile_data
168                 self.full_profile.update({vld_id: self.profile_data})
169                 for intf in intfs:
170                     yield traffic_generator.vnfd_helper.port_num(intf)
171
172         self.ports = [port for port in port_generator()]
173
174     def execute_traffic(self, traffic_generator, ixia_obj=None, mac=None):
175         mac = {} if mac is None else mac
176         first_run = self.first_run
177         if self.first_run:
178             self.first_run = False
179             self.pg_id = 0
180             self.max_rate = self.rate
181             self.min_rate = 0.0
182         else:
183             self.rate = self._get_next_rate()
184
185         self.iteration = traffic_generator.rfc_helper.iteration.value
186         traffic = self._get_ixia_traffic_profile(self.full_profile, mac)
187         self._ixia_traffic_generate(traffic, ixia_obj, traffic_generator)
188         return first_run
189
190     # pylint: disable=unused-argument
191     def get_drop_percentage(self, samples, tol_min, tolerance, precision,
192                             resolution, first_run=False, tc_rfc2544_opts=None):
193         completed = False
194         drop_percent = 100.0
195         num_ifaces = len(samples)
196         duration = self.config.duration
197         in_packets_sum = sum(
198             [samples[iface]['InPackets'] for iface in samples])
199         out_packets_sum = sum(
200             [samples[iface]['OutPackets'] for iface in samples])
201         in_bytes_sum = sum(
202             [samples[iface]['InBytes'] for iface in samples])
203         out_bytes_sum = sum(
204             [samples[iface]['OutBytes'] for iface in samples])
205         rx_throughput = round(float(in_packets_sum) / duration, 3)
206         tx_throughput = round(float(out_packets_sum) / duration, 3)
207         # Rx throughput in Bps
208         rx_throughput_bps = round(float(in_bytes_sum) / duration, 3)
209         # Tx throughput in Bps
210         tx_throughput_bps = round(float(out_bytes_sum) / duration, 3)
211         packet_drop = abs(out_packets_sum - in_packets_sum)
212
213         try:
214             drop_percent = round(
215                 (packet_drop / float(out_packets_sum)) * 100,
216                 self.DROP_PERCENT_ROUND)
217         except ZeroDivisionError:
218             LOG.info('No traffic is flowing')
219
220         if first_run:
221             completed = True if drop_percent <= tolerance else False
222         if (first_run and
223                 self.rate_unit == tp_base.TrafficProfileConfig.RATE_FPS):
224             self.rate = float(out_packets_sum) / duration / num_ifaces
225
226         if drop_percent > tolerance:
227             self.max_rate = self.rate
228         elif drop_percent < tol_min:
229             self.min_rate = self.rate
230         else:
231             completed = True
232
233         last_rate = self.rate
234         next_rate = self._get_next_rate()
235         if abs(next_rate - self.rate) < resolution:
236             LOG.debug("rate=%s, next_rate=%s, resolution=%s", self.rate,
237                       next_rate, resolution)
238             # stop test if the difference between the rate transmission
239             # in two iterations is smaller than the value of the resolution
240             completed = True
241
242         LOG.debug("tolerance=%s, tolerance_precision=%s drop_percent=%s "
243                   "completed=%s", tolerance, precision, drop_percent,
244                   completed)
245
246         latency_ns_avg = float(sum(
247             [samples[iface]['LatencyAvg'] for iface in samples])) / num_ifaces
248         latency_ns_min = min([samples[iface]['LatencyMin'] for iface in samples])
249         latency_ns_max = max([samples[iface]['LatencyMax'] for iface in samples])
250
251         samples['Status'] = self.STATUS_FAIL
252         if round(drop_percent, precision) <= tolerance:
253             samples['Status'] = self.STATUS_SUCCESS
254
255         samples['TxThroughput'] = tx_throughput
256         samples['RxThroughput'] = rx_throughput
257         samples['TxThroughputBps'] = tx_throughput_bps
258         samples['RxThroughputBps'] = rx_throughput_bps
259         samples['DropPercentage'] = drop_percent
260         samples['LatencyAvg'] = latency_ns_avg
261         samples['LatencyMin'] = latency_ns_min
262         samples['LatencyMax'] = latency_ns_max
263         samples['Rate'] = last_rate
264         samples['PktSize'] = self._get_framesize()
265         samples['Iteration'] = self.iteration
266
267         return completed, samples
268
269
270 class IXIARFC2544PppoeScenarioProfile(IXIARFC2544Profile):
271     """Class handles BNG PPPoE scenario tests traffic profile"""
272
273     def __init__(self, yaml_data):
274         super(IXIARFC2544PppoeScenarioProfile, self).__init__(yaml_data)
275         self.full_profile = collections.OrderedDict()
276
277     def _get_flow_groups_params(self):
278         flows_data = [key for key in self.params.keys()
279                       if key.split('_')[0] in [self.UPLINK, self.DOWNLINK]]
280         for i in range(len(flows_data)):
281             uplink = '_'.join([self.UPLINK, str(i)])
282             downlink = '_'.join([self.DOWNLINK, str(i)])
283             if uplink in flows_data:
284                 self.full_profile.update({uplink: self.params[uplink]})
285             if downlink in flows_data:
286                 self.full_profile.update({downlink: self.params[downlink]})
287
288     def update_traffic_profile(self, traffic_generator):
289
290         networks = collections.OrderedDict()
291
292         # Sort network interfaces pairs
293         for i in range(len(traffic_generator.networks)):
294             uplink = '_'.join([self.UPLINK, str(i)])
295             downlink = '_'.join([self.DOWNLINK, str(i)])
296             if uplink in traffic_generator.networks:
297                 networks[uplink] = traffic_generator.networks[uplink]
298             if downlink in traffic_generator.networks:
299                 networks[downlink] = traffic_generator.networks[downlink]
300
301         def port_generator():
302             for intfs in networks.values():
303                 for intf in intfs:
304                     yield traffic_generator.vnfd_helper.port_num(intf)
305
306         self._get_flow_groups_params()
307         self.ports = [port for port in port_generator()]
308
309     def _get_prio_flows_drop_percentage(self, stats):
310         drop_percent = 100
311         for prio_id in stats:
312             prio_flow = stats[prio_id]
313             sum_packet_drop = abs(prio_flow['OutPackets'] - prio_flow['InPackets'])
314             try:
315                 drop_percent = round(
316                     (sum_packet_drop / float(prio_flow['OutPackets'])) * 100,
317                     self.DROP_PERCENT_ROUND)
318             except ZeroDivisionError:
319                 LOG.info('No traffic is flowing')
320             prio_flow['DropPercentage'] = drop_percent
321         return stats
322
323     def _get_summary_pppoe_subs_counters(self, samples):
324         result = {}
325         keys = ['SessionsUp',
326                 'SessionsDown',
327                 'SessionsNotStarted',
328                 'SessionsTotal']
329         for key in keys:
330             result[key] = \
331                 sum([samples[port][key] for port in samples
332                      if key in samples[port]])
333         return result
334
335     def get_drop_percentage(self, samples, tol_min, tolerance, precision,
336                             resolution, first_run=False, tc_rfc2544_opts=None):
337         completed = False
338         sum_drop_percent = 100
339         num_ifaces = len(samples)
340         duration = self.config.duration
341         last_rate = self.rate
342         priority_stats = samples.pop('priority_stats')
343         priority_stats = self._get_prio_flows_drop_percentage(priority_stats)
344         summary_subs_stats = self._get_summary_pppoe_subs_counters(samples)
345         in_packets_sum = sum(
346             [samples[iface]['InPackets'] for iface in samples])
347         out_packets_sum = sum(
348             [samples[iface]['OutPackets'] for iface in samples])
349         in_bytes_sum = sum(
350             [samples[iface]['InBytes'] for iface in samples])
351         out_bytes_sum = sum(
352             [samples[iface]['OutBytes'] for iface in samples])
353         rx_throughput = round(float(in_packets_sum) / duration, 3)
354         tx_throughput = round(float(out_packets_sum) / duration, 3)
355         # Rx throughput in Bps
356         rx_throughput_bps = round(float(in_bytes_sum) / duration, 3)
357         # Tx throughput in Bps
358         tx_throughput_bps = round(float(out_bytes_sum) / duration, 3)
359         sum_packet_drop = abs(out_packets_sum - in_packets_sum)
360
361         try:
362             sum_drop_percent = round(
363                 (sum_packet_drop / float(out_packets_sum)) * 100,
364                 self.DROP_PERCENT_ROUND)
365         except ZeroDivisionError:
366             LOG.info('No traffic is flowing')
367
368         latency_ns_avg = float(sum(
369             [samples[iface]['LatencyAvg'] for iface in samples])) / num_ifaces
370         latency_ns_min = min([samples[iface]['LatencyMin'] for iface in samples])
371         latency_ns_max = max([samples[iface]['LatencyMax'] for iface in samples])
372
373         samples['TxThroughput'] = tx_throughput
374         samples['RxThroughput'] = rx_throughput
375         samples['TxThroughputBps'] = tx_throughput_bps
376         samples['RxThroughputBps'] = rx_throughput_bps
377         samples['DropPercentage'] = sum_drop_percent
378         samples['LatencyAvg'] = latency_ns_avg
379         samples['LatencyMin'] = latency_ns_min
380         samples['LatencyMax'] = latency_ns_max
381         samples['Priority'] = priority_stats
382         samples['Rate'] = last_rate
383         samples['PktSize'] = self._get_framesize()
384         samples['Iteration'] = self.iteration
385         samples.update(summary_subs_stats)
386
387         if tc_rfc2544_opts:
388             priority = tc_rfc2544_opts.get('priority')
389             if priority:
390                 drop_percent = samples['Priority'][priority]['DropPercentage']
391             else:
392                 drop_percent = sum_drop_percent
393         else:
394             drop_percent = sum_drop_percent
395
396         if first_run:
397             completed = True if drop_percent <= tolerance else False
398         if (first_run and
399                 self.rate_unit == tp_base.TrafficProfileConfig.RATE_FPS):
400             self.rate = float(out_packets_sum) / duration / num_ifaces
401
402         if drop_percent > tolerance:
403             self.max_rate = self.rate
404         elif drop_percent < tol_min:
405             self.min_rate = self.rate
406         else:
407             completed = True
408
409         next_rate = self._get_next_rate()
410         if abs(next_rate - self.rate) < resolution:
411             LOG.debug("rate=%s, next_rate=%s, resolution=%s", self.rate,
412                       next_rate, resolution)
413             # stop test if the difference between the rate transmission
414             # in two iterations is smaller than the value of the resolution
415             completed = True
416
417         LOG.debug("tolerance=%s, tolerance_precision=%s drop_percent=%s "
418                   "completed=%s", tolerance, precision, drop_percent,
419                   completed)
420
421         samples['Status'] = self.STATUS_FAIL
422         if round(drop_percent, precision) <= tolerance:
423             samples['Status'] = self.STATUS_SUCCESS
424
425         return completed, samples