Merge "Add IxNextgen API for settings IP priority"
[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
17 from yardstick.common import utils
18 from yardstick.network_services.traffic_profile import base as tp_base
19 from yardstick.network_services.traffic_profile import trex_traffic_profile
20
21
22 LOG = logging.getLogger(__name__)
23
24
25 class IXIARFC2544Profile(trex_traffic_profile.TrexProfile):
26
27     UPLINK = 'uplink'
28     DOWNLINK = 'downlink'
29     DROP_PERCENT_ROUND = 6
30     RATE_ROUND = 5
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
39     def _get_ip_and_mask(self, ip_range):
40         _ip_range = ip_range.split('-')
41         if len(_ip_range) == 1:
42             return _ip_range[0], None
43
44         mask = utils.get_mask_from_ip_range(_ip_range[0], _ip_range[1])
45         return _ip_range[0], mask
46
47     def _get_fixed_and_mask(self, port_range):
48         _port_range = str(port_range).split('-')
49         if len(_port_range) == 1:
50             return int(_port_range[0]), 0
51
52         return int(_port_range[0]), int(_port_range[1])
53
54     def _get_ixia_traffic_profile(self, profile_data, mac=None):
55         mac = {} if mac is None else mac
56         result = {}
57         for traffickey, values in profile_data.items():
58             if not traffickey.startswith((self.UPLINK, self.DOWNLINK)):
59                 continue
60
61             # values should be single-item dict, so just grab the first item
62             try:
63                 key, value = next(iter(values.items()))
64             except StopIteration:
65                 result[traffickey] = {}
66                 continue
67
68             port_id = value.get('id', 1)
69             port_index = port_id - 1
70
71             result[traffickey] = {
72                 'bidir': False,
73                 'id': port_id,
74                 'rate': self.rate,
75                 'rate_unit': self.rate_unit,
76                 'outer_l2': {},
77                 'outer_l3': {},
78                 'outer_l4': {},
79             }
80
81             outer_l2 = value.get('outer_l2')
82             if outer_l2:
83                 result[traffickey]['outer_l2'].update({
84                     'framesize': outer_l2.get('framesize'),
85                     'framesPerSecond': True,
86                     'QinQ': outer_l2.get('QinQ'),
87                     'srcmac': mac.get('src_mac_{}'.format(port_index)),
88                     'dstmac': mac.get('dst_mac_{}'.format(port_index)),
89                 })
90
91             if value.get('outer_l3v4'):
92                 outer_l3 = value['outer_l3v4']
93                 src_key, dst_key = 'srcip4', 'dstip4'
94             else:
95                 outer_l3 = value.get('outer_l3v6')
96                 src_key, dst_key = 'srcip6', 'dstip6'
97             if outer_l3:
98                 srcip = srcmask = dstip = dstmask = None
99                 if outer_l3.get(src_key):
100                     srcip, srcmask = self._get_ip_and_mask(outer_l3[src_key])
101                 if outer_l3.get(dst_key):
102                     dstip, dstmask = self._get_ip_and_mask(outer_l3[dst_key])
103
104                 result[traffickey]['outer_l3'].update({
105                     'count': outer_l3.get('count', 1),
106                     'dscp': outer_l3.get('dscp'),
107                     'ttl': outer_l3.get('ttl'),
108                     'srcseed': outer_l3.get('srcseed', 1),
109                     'dstseed': outer_l3.get('dstseed', 1),
110                     'srcip': srcip,
111                     'dstip': dstip,
112                     'srcmask': srcmask,
113                     'dstmask': dstmask,
114                     'type': key,
115                     'proto': outer_l3.get('proto'),
116                     'priority': outer_l3.get('priority')
117                 })
118
119             outer_l4 = value.get('outer_l4')
120             if outer_l4:
121                 src_port = src_port_mask = dst_port = dst_port_mask = None
122                 if outer_l4.get('srcport'):
123                     src_port, src_port_mask = (
124                         self._get_fixed_and_mask(outer_l4['srcport']))
125
126                 if outer_l4.get('dstport'):
127                     dst_port, dst_port_mask = (
128                         self._get_fixed_and_mask(outer_l4['dstport']))
129
130                 result[traffickey]['outer_l4'].update({
131                     'srcport': src_port,
132                     'dstport': dst_port,
133                     'srcportmask': src_port_mask,
134                     'dstportmask': dst_port_mask,
135                     'count': outer_l4.get('count', 1),
136                     'seed': outer_l4.get('seed', 1),
137                 })
138
139         return result
140
141     def _ixia_traffic_generate(self, traffic, ixia_obj):
142         ixia_obj.update_frame(traffic, self.config.duration)
143         ixia_obj.update_ip_packet(traffic)
144         ixia_obj.update_l4(traffic)
145         ixia_obj.start_traffic()
146
147     def update_traffic_profile(self, traffic_generator):
148         def port_generator():
149             for vld_id, intfs in sorted(traffic_generator.networks.items()):
150                 if not vld_id.startswith((self.UPLINK, self.DOWNLINK)):
151                     continue
152                 profile_data = self.params.get(vld_id)
153                 if not profile_data:
154                     continue
155                 self.profile_data = profile_data
156                 self.full_profile.update({vld_id: self.profile_data})
157                 for intf in intfs:
158                     yield traffic_generator.vnfd_helper.port_num(intf)
159
160         self.ports = [port for port in port_generator()]
161
162     def execute_traffic(self, traffic_generator, ixia_obj=None, mac=None):
163         mac = {} if mac is None else mac
164         first_run = self.first_run
165         if self.first_run:
166             self.first_run = False
167             self.full_profile = {}
168             self.pg_id = 0
169             self.update_traffic_profile(traffic_generator)
170             self.max_rate = self.rate
171             self.min_rate = 0.0
172         else:
173             self.rate = round(float(self.max_rate + self.min_rate) / 2.0,
174                               self.RATE_ROUND)
175
176         traffic = self._get_ixia_traffic_profile(self.full_profile, mac)
177         self._ixia_traffic_generate(traffic, ixia_obj)
178         return first_run
179
180     def get_drop_percentage(self, samples, tol_min, tolerance, precision,
181                             first_run=False):
182         completed = False
183         drop_percent = 100
184         num_ifaces = len(samples)
185         duration = self.config.duration
186         in_packets_sum = sum(
187             [samples[iface]['in_packets'] for iface in samples])
188         out_packets_sum = sum(
189             [samples[iface]['out_packets'] for iface in samples])
190         rx_throughput = round(float(in_packets_sum) / duration, 3)
191         tx_throughput = round(float(out_packets_sum) / duration, 3)
192         packet_drop = abs(out_packets_sum - in_packets_sum)
193
194         try:
195             drop_percent = round(
196                 (packet_drop / float(out_packets_sum)) * 100,
197                 self.DROP_PERCENT_ROUND)
198         except ZeroDivisionError:
199             LOG.info('No traffic is flowing')
200
201         if first_run:
202             completed = True if drop_percent <= tolerance else False
203         if (first_run and
204                 self.rate_unit == tp_base.TrafficProfileConfig.RATE_FPS):
205             self.rate = float(out_packets_sum) / duration / num_ifaces
206
207         if drop_percent > tolerance:
208             self.max_rate = self.rate
209         elif drop_percent < tol_min:
210             self.min_rate = self.rate
211         else:
212             completed = True
213
214         LOG.debug("tolerance=%s, tolerance_precision=%s drop_percent=%s "
215                   "completed=%s", tolerance, precision, drop_percent,
216                   completed)
217
218         latency_ns_avg = float(
219             sum([samples[iface]['Store-Forward_Avg_latency_ns']
220             for iface in samples])) / num_ifaces
221         latency_ns_min = float(
222             sum([samples[iface]['Store-Forward_Min_latency_ns']
223             for iface in samples])) / num_ifaces
224         latency_ns_max = float(
225             sum([samples[iface]['Store-Forward_Max_latency_ns']
226             for iface in samples])) / num_ifaces
227
228         samples['Status'] = self.STATUS_FAIL
229         if round(drop_percent, precision) <= tolerance:
230             samples['Status'] = self.STATUS_SUCCESS
231
232         samples['TxThroughput'] = tx_throughput
233         samples['RxThroughput'] = rx_throughput
234         samples['DropPercentage'] = drop_percent
235         samples['latency_ns_avg'] = latency_ns_avg
236         samples['latency_ns_min'] = latency_ns_min
237         samples['latency_ns_max'] = latency_ns_max
238
239         return completed, samples