Merge "Extend vBNG PPPoE test cases functionality"
[yardstick.git] / yardstick / network_services / traffic_profile / ixia_rfc2544.py
1 # Copyright (c) 2016-2017 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     RATE_ROUND = 5
32     STATUS_SUCCESS = "Success"
33     STATUS_FAIL = "Failure"
34
35     def __init__(self, yaml_data):
36         super(IXIARFC2544Profile, self).__init__(yaml_data)
37         self.rate = self.config.frame_rate
38         self.rate_unit = self.config.rate_unit
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):
150         ixia_obj.update_frame(traffic, self.config.duration)
151         ixia_obj.update_ip_packet(traffic)
152         ixia_obj.update_l4(traffic)
153         ixia_obj.start_traffic()
154
155     def update_traffic_profile(self, traffic_generator):
156         def port_generator():
157             for vld_id, intfs in sorted(traffic_generator.networks.items()):
158                 if not vld_id.startswith((self.UPLINK, self.DOWNLINK)):
159                     continue
160                 profile_data = self.params.get(vld_id)
161                 if not profile_data:
162                     continue
163                 self.profile_data = profile_data
164                 self.full_profile.update({vld_id: self.profile_data})
165                 for intf in intfs:
166                     yield traffic_generator.vnfd_helper.port_num(intf)
167
168         self.ports = [port for port in port_generator()]
169
170     def execute_traffic(self, traffic_generator, ixia_obj=None, mac=None):
171         mac = {} if mac is None else mac
172         first_run = self.first_run
173         if self.first_run:
174             self.first_run = False
175             self.pg_id = 0
176             self.max_rate = self.rate
177             self.min_rate = 0.0
178         else:
179             self.rate = round(float(self.max_rate + self.min_rate) / 2.0,
180                               self.RATE_ROUND)
181
182         traffic = self._get_ixia_traffic_profile(self.full_profile, mac)
183         self._ixia_traffic_generate(traffic, ixia_obj)
184         return first_run
185
186     def get_drop_percentage(self, samples, tol_min, tolerance, precision,
187                             first_run=False):
188         completed = False
189         drop_percent = 100
190         num_ifaces = len(samples)
191         duration = self.config.duration
192         in_packets_sum = sum(
193             [samples[iface]['in_packets'] for iface in samples])
194         out_packets_sum = sum(
195             [samples[iface]['out_packets'] for iface in samples])
196         rx_throughput = round(float(in_packets_sum) / duration, 3)
197         tx_throughput = round(float(out_packets_sum) / duration, 3)
198         packet_drop = abs(out_packets_sum - in_packets_sum)
199
200         try:
201             drop_percent = round(
202                 (packet_drop / float(out_packets_sum)) * 100,
203                 self.DROP_PERCENT_ROUND)
204         except ZeroDivisionError:
205             LOG.info('No traffic is flowing')
206
207         if first_run:
208             completed = True if drop_percent <= tolerance else False
209         if (first_run and
210                 self.rate_unit == tp_base.TrafficProfileConfig.RATE_FPS):
211             self.rate = float(out_packets_sum) / duration / num_ifaces
212
213         if drop_percent > tolerance:
214             self.max_rate = self.rate
215         elif drop_percent < tol_min:
216             self.min_rate = self.rate
217         else:
218             completed = True
219
220         LOG.debug("tolerance=%s, tolerance_precision=%s drop_percent=%s "
221                   "completed=%s", tolerance, precision, drop_percent,
222                   completed)
223
224         latency_ns_avg = float(
225             sum([samples[iface]['Store-Forward_Avg_latency_ns']
226             for iface in samples])) / num_ifaces
227         latency_ns_min = float(
228             sum([samples[iface]['Store-Forward_Min_latency_ns']
229             for iface in samples])) / num_ifaces
230         latency_ns_max = float(
231             sum([samples[iface]['Store-Forward_Max_latency_ns']
232             for iface in samples])) / num_ifaces
233
234         samples['Status'] = self.STATUS_FAIL
235         if round(drop_percent, precision) <= tolerance:
236             samples['Status'] = self.STATUS_SUCCESS
237
238         samples['TxThroughput'] = tx_throughput
239         samples['RxThroughput'] = rx_throughput
240         samples['DropPercentage'] = drop_percent
241         samples['latency_ns_avg'] = latency_ns_avg
242         samples['latency_ns_min'] = latency_ns_min
243         samples['latency_ns_max'] = latency_ns_max
244
245         return completed, samples
246
247
248 class IXIARFC2544PppoeScenarioProfile(IXIARFC2544Profile):
249     """Class handles BNG PPPoE scenario tests traffic profile"""
250
251     def __init__(self, yaml_data):
252         super(IXIARFC2544PppoeScenarioProfile, self).__init__(yaml_data)
253         self.full_profile = collections.OrderedDict()
254
255     def _get_flow_groups_params(self):
256         flows_data = [key for key in self.params.keys()
257                       if key.split('_')[0] in [self.UPLINK, self.DOWNLINK]]
258         for i in range(len(flows_data)):
259             uplink = '_'.join([self.UPLINK, str(i)])
260             downlink = '_'.join([self.DOWNLINK, str(i)])
261             if uplink in flows_data:
262                 self.full_profile.update({uplink: self.params[uplink]})
263             if downlink in flows_data:
264                 self.full_profile.update({downlink: self.params[downlink]})
265
266     def update_traffic_profile(self, traffic_generator):
267         def port_generator():
268             for vld_id, intfs in sorted(traffic_generator.networks.items()):
269                 if not vld_id.startswith((self.UPLINK, self.DOWNLINK)):
270                     continue
271                 for intf in intfs:
272                     yield traffic_generator.vnfd_helper.port_num(intf)
273
274         self._get_flow_groups_params()
275         self.ports = [port for port in port_generator()]