Merge "Added NSB descriptors for vCMTS testcase"
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / tg_rfc2544_ixia.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 ipaddress
16 import logging
17 import six
18 import collections
19
20 from six import moves
21 from yardstick.common import utils
22 from yardstick.common import exceptions
23 from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api
24 from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen
25 from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper
26 from yardstick.network_services.vnf_generic.vnf.sample_vnf import Rfc2544ResourceHelper
27
28
29 LOG = logging.getLogger(__name__)
30
31 WAIT_AFTER_CFG_LOAD = 10
32 WAIT_FOR_TRAFFIC = 30
33 WAIT_PROTOCOLS_STARTED = 420
34
35
36 class IxiaBasicScenario(object):
37     """Ixia Basic scenario for flow from port to port"""
38
39     def __init__(self, client, context_cfg, ixia_cfg):
40
41         self.client = client
42         self.context_cfg = context_cfg
43         self.ixia_cfg = ixia_cfg
44
45         self._uplink_vports = None
46         self._downlink_vports = None
47
48     def apply_config(self):
49         pass
50
51     def run_protocols(self):
52         pass
53
54     def stop_protocols(self):
55         pass
56
57     def create_traffic_model(self, traffic_profile):
58         vports = self.client.get_vports()
59         self._uplink_vports = vports[::2]
60         self._downlink_vports = vports[1::2]
61         self.client.create_traffic_model(self._uplink_vports,
62                                          self._downlink_vports,
63                                          traffic_profile)
64
65     def _get_stats(self):
66         return self.client.get_statistics()
67
68     def generate_samples(self, resource_helper, ports, duration):
69         stats = self._get_stats()
70
71         samples = {}
72         # this is not DPDK port num, but this is whatever number we gave
73         # when we selected ports and programmed the profile
74         for port_num in ports:
75             try:
76                 # reverse lookup port name from port_num so the stats dict is descriptive
77                 intf = resource_helper.vnfd_helper.find_interface_by_port(port_num)
78                 port_name = intf['name']
79                 avg_latency = stats['Store-Forward_Avg_latency_ns'][port_num]
80                 min_latency = stats['Store-Forward_Min_latency_ns'][port_num]
81                 max_latency = stats['Store-Forward_Max_latency_ns'][port_num]
82                 samples[port_name] = {
83                     'RxThroughputBps': float(stats['Bytes_Rx'][port_num]) / duration,
84                     'TxThroughputBps': float(stats['Bytes_Tx'][port_num]) / duration,
85                     'InPackets': int(stats['Valid_Frames_Rx'][port_num]),
86                     'OutPackets': int(stats['Frames_Tx'][port_num]),
87                     'InBytes': int(stats['Bytes_Rx'][port_num]),
88                     'OutBytes': int(stats['Bytes_Tx'][port_num]),
89                     'RxThroughput': float(stats['Valid_Frames_Rx'][port_num]) / duration,
90                     'TxThroughput': float(stats['Frames_Tx'][port_num]) / duration,
91                     'LatencyAvg': utils.safe_cast(avg_latency, int, 0),
92                     'LatencyMin': utils.safe_cast(min_latency, int, 0),
93                     'LatencyMax': utils.safe_cast(max_latency, int, 0)
94                 }
95             except IndexError:
96                 pass
97
98         return samples
99
100     def update_tracking_options(self):
101         pass
102
103     def get_tc_rfc2544_options(self):
104         pass
105
106
107 class IxiaL3Scenario(IxiaBasicScenario):
108     """Ixia scenario for L3 flow between static ip's"""
109
110     def _add_static_ips(self):
111         vports = self.client.get_vports()
112         uplink_intf_vport = [(self.client.get_static_interface(vport), vport)
113                              for vport in vports[::2]]
114         downlink_intf_vport = [(self.client.get_static_interface(vport), vport)
115                                for vport in vports[1::2]]
116
117         for index in range(len(uplink_intf_vport)):
118             intf, vport = uplink_intf_vport[index]
119             try:
120                 iprange = self.ixia_cfg['flow'].get('src_ip')[index]
121                 start_ip = utils.get_ip_range_start(iprange)
122                 count = utils.get_ip_range_count(iprange)
123                 self.client.add_static_ipv4(intf, vport, start_ip, count, '32')
124             except IndexError:
125                 raise exceptions.IncorrectFlowOption(
126                     option="src_ip", link="uplink_{}".format(index))
127
128             intf, vport = downlink_intf_vport[index]
129             try:
130                 iprange = self.ixia_cfg['flow'].get('dst_ip')[index]
131                 start_ip = utils.get_ip_range_start(iprange)
132                 count = utils.get_ip_range_count(iprange)
133                 self.client.add_static_ipv4(intf, vport, start_ip, count, '32')
134             except IndexError:
135                 raise exceptions.IncorrectFlowOption(
136                     option="dst_ip", link="downlink_{}".format(index))
137
138     def _add_interfaces(self):
139         vports = self.client.get_vports()
140         uplink_vports = (vport for vport in vports[::2])
141         downlink_vports = (vport for vport in vports[1::2])
142
143         ix_node = next(node for _, node in self.context_cfg['nodes'].items()
144                        if node['role'] == 'IxNet')
145
146         for intf in ix_node['interfaces'].values():
147             ip = intf.get('local_ip')
148             mac = intf.get('local_mac')
149             gateway = None
150             try:
151                 gateway = next(route.get('gateway')
152                                for route in ix_node.get('routing_table')
153                                if route.get('if') == intf.get('ifname'))
154             except StopIteration:
155                 LOG.debug("Gateway not provided")
156
157             if 'uplink' in intf.get('vld_id'):
158                 self.client.add_interface(next(uplink_vports),
159                                           ip, mac, gateway)
160             else:
161                 self.client.add_interface(next(downlink_vports),
162                                           ip, mac, gateway)
163
164     def apply_config(self):
165         self._add_interfaces()
166         self._add_static_ips()
167
168     def create_traffic_model(self, traffic_profile):
169         vports = self.client.get_vports()
170         self._uplink_vports = vports[::2]
171         self._downlink_vports = vports[1::2]
172
173         uplink_endpoints = [port + '/protocols/static'
174                             for port in self._uplink_vports]
175         downlink_endpoints = [port + '/protocols/static'
176                               for port in self._downlink_vports]
177
178         self.client.create_ipv4_traffic_model(uplink_endpoints,
179                                               downlink_endpoints,
180                                               traffic_profile)
181
182
183 class IxiaPppoeClientScenario(object):
184     def __init__(self, client, context_cfg, ixia_cfg):
185
186         self.client = client
187
188         self._uplink_vports = None
189         self._downlink_vports = None
190
191         self._access_topologies = []
192         self._core_topologies = []
193
194         self._context_cfg = context_cfg
195         self._ixia_cfg = ixia_cfg
196         self.protocols = []
197         self.device_groups = []
198
199     def apply_config(self):
200         vports = self.client.get_vports()
201         self._uplink_vports = vports[::2]
202         self._downlink_vports = vports[1::2]
203         self._fill_ixia_config()
204         self._apply_access_network_config()
205         self._apply_core_network_config()
206
207     def create_traffic_model(self, traffic_profile):
208         endpoints_id_pairs = self._get_endpoints_src_dst_id_pairs(
209             traffic_profile.full_profile)
210         endpoints_obj_pairs = \
211             self._get_endpoints_src_dst_obj_pairs(endpoints_id_pairs)
212         if endpoints_obj_pairs:
213             uplink_endpoints = endpoints_obj_pairs[::2]
214             downlink_endpoints = endpoints_obj_pairs[1::2]
215         else:
216             uplink_endpoints = self._access_topologies
217             downlink_endpoints = self._core_topologies
218         self.client.create_ipv4_traffic_model(uplink_endpoints,
219                                               downlink_endpoints,
220                                               traffic_profile)
221
222     def run_protocols(self):
223         LOG.info('PPPoE Scenario - Start Protocols')
224         self.client.start_protocols()
225         utils.wait_until_true(
226             lambda: self.client.is_protocols_running(self.protocols),
227             timeout=WAIT_PROTOCOLS_STARTED, sleep=2)
228
229     def stop_protocols(self):
230         LOG.info('PPPoE Scenario - Stop Protocols')
231         self.client.stop_protocols()
232
233     def _get_intf_addr(self, intf):
234         """Retrieve interface IP address and mask
235
236         :param intf: could be the string which represents IP address
237         with mask (e.g 192.168.10.2/24) or a dictionary with the host
238         name and the port (e.g. {'tg__0': 'xe1'})
239         :return: (tuple) pair of ip address and mask
240         """
241         if isinstance(intf, six.string_types):
242             ip, mask = tuple(intf.split('/'))
243             return ip, int(mask)
244
245         node_name, intf_name = next(iter(intf.items()))
246         node = self._context_cfg["nodes"].get(node_name, {})
247         interface = node.get("interfaces", {})[intf_name]
248         ip = interface["local_ip"]
249         mask = interface["netmask"]
250         ipaddr = ipaddress.ip_network(six.text_type('{}/{}'.format(ip, mask)),
251                                       strict=False)
252         return ip, ipaddr.prefixlen
253
254     @staticmethod
255     def _get_endpoints_src_dst_id_pairs(flows_params):
256         """Get list of flows src/dst port pairs
257
258         Create list of flows src/dst port pairs based on traffic profile
259         flows data. Each uplink/downlink pair in traffic profile represents
260         specific flows between the pair of ports.
261
262         Example ('port' key represents port on which flow will be created):
263
264         Input flows data:
265         uplink_0:
266           ipv4:
267             id: 1
268             port: xe0
269         downlink_0:
270           ipv4:
271             id: 2
272             port: xe1
273         uplink_1:
274           ipv4:
275             id: 3
276             port: xe2
277         downlink_1:
278           ipv4:
279             id: 4
280             port: xe3
281
282         Result list: ['xe0', 'xe1', 'xe2', 'xe3']
283
284         Result list means that the following flows pairs will be created:
285         - uplink 0: port xe0 <-> port xe1
286         - downlink 0: port xe1 <-> port xe0
287         - uplink 1: port xe2 <-> port xe3
288         - downlink 1: port xe3 <-> port xe2
289
290         :param flows_params: ordered dict of traffic profile flows params
291         :return: (list) list of flows src/dst ports
292         """
293         if len(flows_params) % 2:
294             raise RuntimeError('Number of uplink/downlink pairs'
295                                ' in traffic profile is not equal')
296         endpoint_pairs = []
297         for flow in flows_params:
298             port = flows_params[flow]['ipv4'].get('port')
299             if port is None:
300                 continue
301             endpoint_pairs.append(port)
302         return endpoint_pairs
303
304     def _get_endpoints_src_dst_obj_pairs(self, endpoints_id_pairs):
305         """Create list of uplink/downlink device groups pairs
306
307         Based on traffic profile options, create list of uplink/downlink
308         device groups pairs between which flow groups will be created:
309
310         1. In case uplink/downlink flows in traffic profile doesn't have
311            specified 'port' key, flows will be created between topologies
312            on corresponding access and core port.
313            E.g.:
314            Access topology on xe0: topology1
315            Core topology on xe1: topology2
316            Flows will be created between:
317            topology1 -> topology2
318            topology2 -> topology1
319
320         2. In case uplink/downlink flows in traffic profile have specified
321            'port' key, flows will be created between device groups on this
322            port.
323            E.g., for the following traffic profile
324            uplink_0:
325              port: xe0
326            downlink_0:
327              port: xe1
328            uplink_1:
329              port: xe0
330            downlink_0:
331              port: xe3
332            Flows will be created between:
333            Port xe0 (dg1) -> Port xe1 (dg1)
334            Port xe1 (dg1) -> Port xe0 (dg1)
335            Port xe0 (dg2) -> Port xe3 (dg1)
336            Port xe3 (dg3) -> Port xe0 (dg1)
337
338         :param endpoints_id_pairs: (list) List of uplink/downlink flows ports
339          pairs
340         :return: (list) list of uplink/downlink device groups descriptors pairs
341         """
342         pppoe = self._ixia_cfg['pppoe_client']
343         sessions_per_port = pppoe['sessions_per_port']
344         sessions_per_svlan = pppoe['sessions_per_svlan']
345         svlan_count = int(sessions_per_port / sessions_per_svlan)
346
347         uplink_ports = [p['tg__0'] for p in self._ixia_cfg['flow']['src_ip']]
348         downlink_ports = [p['tg__0'] for p in self._ixia_cfg['flow']['dst_ip']]
349         uplink_port_topology_map = zip(uplink_ports, self._access_topologies)
350         downlink_port_topology_map = zip(downlink_ports, self._core_topologies)
351
352         port_to_dev_group_mapping = {}
353         for port, topology in uplink_port_topology_map:
354             topology_dgs = self.client.get_topology_device_groups(topology)
355             port_to_dev_group_mapping[port] = topology_dgs
356         for port, topology in downlink_port_topology_map:
357             topology_dgs = self.client.get_topology_device_groups(topology)
358             port_to_dev_group_mapping[port] = topology_dgs
359
360         uplink_endpoints = endpoints_id_pairs[::2]
361         downlink_endpoints = endpoints_id_pairs[1::2]
362
363         uplink_dev_groups = []
364         group_up = [uplink_endpoints[i:i + svlan_count]
365                     for i in range(0, len(uplink_endpoints), svlan_count)]
366
367         for group in group_up:
368             for i, port in enumerate(group):
369                 uplink_dev_groups.append(port_to_dev_group_mapping[port][i])
370
371         downlink_dev_groups = []
372         for port in downlink_endpoints:
373             downlink_dev_groups.append(port_to_dev_group_mapping[port][0])
374
375         endpoint_obj_pairs = []
376         [endpoint_obj_pairs.extend([up, down])
377          for up, down in zip(uplink_dev_groups, downlink_dev_groups)]
378
379         return endpoint_obj_pairs
380
381     def _fill_ixia_config(self):
382         pppoe = self._ixia_cfg["pppoe_client"]
383         ipv4 = self._ixia_cfg["ipv4_client"]
384
385         _ip = [self._get_intf_addr(intf)[0] for intf in pppoe["ip"]]
386         self._ixia_cfg["pppoe_client"]["ip"] = _ip
387
388         _ip = [self._get_intf_addr(intf)[0] for intf in ipv4["gateway_ip"]]
389         self._ixia_cfg["ipv4_client"]["gateway_ip"] = _ip
390
391         addrs = [self._get_intf_addr(intf) for intf in ipv4["ip"]]
392         _ip = [addr[0] for addr in addrs]
393         _prefix = [addr[1] for addr in addrs]
394
395         self._ixia_cfg["ipv4_client"]["ip"] = _ip
396         self._ixia_cfg["ipv4_client"]["prefix"] = _prefix
397
398     def _apply_access_network_config(self):
399         pppoe = self._ixia_cfg["pppoe_client"]
400         sessions_per_port = pppoe['sessions_per_port']
401         sessions_per_svlan = pppoe['sessions_per_svlan']
402         svlan_count = int(sessions_per_port / sessions_per_svlan)
403
404         # add topology per uplink port (access network)
405         for access_tp_id, vport in enumerate(self._uplink_vports):
406             name = 'Topology access {}'.format(access_tp_id)
407             tp = self.client.add_topology(name, vport)
408             self._access_topologies.append(tp)
409             # add device group per svlan
410             for dg_id in range(svlan_count):
411                 s_vlan_id = int(pppoe['s_vlan']) + dg_id + access_tp_id * svlan_count
412                 s_vlan = ixnet_api.Vlan(vlan_id=s_vlan_id)
413                 c_vlan = ixnet_api.Vlan(vlan_id=pppoe['c_vlan'], vlan_id_step=1)
414                 name = 'SVLAN {}'.format(s_vlan_id)
415                 dg = self.client.add_device_group(tp, name, sessions_per_svlan)
416                 self.device_groups.append(dg)
417                 # add ethernet layer to device group
418                 ethernet = self.client.add_ethernet(dg, 'Ethernet')
419                 self.protocols.append(ethernet)
420                 self.client.add_vlans(ethernet, [s_vlan, c_vlan])
421                 # add ppp over ethernet
422                 if 'pap_user' in pppoe:
423                     ppp = self.client.add_pppox_client(ethernet, 'pap',
424                                                        pppoe['pap_user'],
425                                                        pppoe['pap_password'])
426                 else:
427                     ppp = self.client.add_pppox_client(ethernet, 'chap',
428                                                        pppoe['chap_user'],
429                                                        pppoe['chap_password'])
430                 self.protocols.append(ppp)
431
432     def _apply_core_network_config(self):
433         ipv4 = self._ixia_cfg["ipv4_client"]
434         sessions_per_port = ipv4['sessions_per_port']
435         sessions_per_vlan = ipv4['sessions_per_vlan']
436         vlan_count = int(sessions_per_port / sessions_per_vlan)
437
438         # add topology per downlink port (core network)
439         for core_tp_id, vport in enumerate(self._downlink_vports):
440             name = 'Topology core {}'.format(core_tp_id)
441             tp = self.client.add_topology(name, vport)
442             self._core_topologies.append(tp)
443             # add device group per vlan
444             for dg_id in range(vlan_count):
445                 name = 'Core port {}'.format(core_tp_id)
446                 dg = self.client.add_device_group(tp, name, sessions_per_vlan)
447                 self.device_groups.append(dg)
448                 # add ethernet layer to device group
449                 ethernet = self.client.add_ethernet(dg, 'Ethernet')
450                 self.protocols.append(ethernet)
451                 if 'vlan' in ipv4:
452                     vlan_id = int(ipv4['vlan']) + dg_id + core_tp_id * vlan_count
453                     vlan = ixnet_api.Vlan(vlan_id=vlan_id)
454                     self.client.add_vlans(ethernet, [vlan])
455                 # add ipv4 layer
456                 gw_ip = ipv4['gateway_ip'][core_tp_id]
457                 # use gw addr to generate ip addr from the same network
458                 ip_addr = ipaddress.IPv4Address(gw_ip) + 1
459                 ipv4_obj = self.client.add_ipv4(ethernet, name='ipv4',
460                                                 addr=ip_addr,
461                                                 addr_step='0.0.0.1',
462                                                 prefix=ipv4['prefix'][core_tp_id],
463                                                 gateway=gw_ip)
464                 self.protocols.append(ipv4_obj)
465                 if ipv4.get("bgp"):
466                     bgp_peer_obj = self.client.add_bgp(ipv4_obj,
467                                                        dut_ip=ipv4["bgp"]["dut_ip"],
468                                                        local_as=ipv4["bgp"]["as_number"],
469                                                        bgp_type=ipv4["bgp"].get("bgp_type"))
470                     self.protocols.append(bgp_peer_obj)
471
472     def update_tracking_options(self):
473         priority_map = {
474             'raw': 'ipv4Raw0',
475             'tos': {'precedence': 'ipv4Precedence0'},
476             'dscp': {'defaultPHB': 'ipv4DefaultPhb0',
477                      'selectorPHB': 'ipv4ClassSelectorPhb0',
478                      'assuredPHB': 'ipv4AssuredForwardingPhb0',
479                      'expeditedPHB': 'ipv4ExpeditedForwardingPhb0'}
480         }
481
482         prio_trackby_key = 'ipv4Precedence0'
483
484         try:
485             priority = list(self._ixia_cfg['priority'])[0]
486             if priority == 'raw':
487                 prio_trackby_key = priority_map[priority]
488             elif priority in ['tos', 'dscp']:
489                 priority_type = list(self._ixia_cfg['priority'][priority])[0]
490                 prio_trackby_key = priority_map[priority][priority_type]
491         except KeyError:
492             pass
493
494         tracking_options = ['flowGroup0', 'vlanVlanId0', prio_trackby_key]
495         self.client.set_flow_tracking(tracking_options)
496
497     def get_tc_rfc2544_options(self):
498         return self._ixia_cfg.get('rfc2544')
499
500     def _get_stats(self):
501         return self.client.get_pppoe_scenario_statistics()
502
503     @staticmethod
504     def get_flow_id_data(stats, flow_id, key):
505         result = [float(flow.get(key)) for flow in stats if flow['id'] == flow_id]
506         return sum(result) / len(result)
507
508     def get_priority_flows_stats(self, samples, duration):
509         results = {}
510         priorities = set([flow['IP_Priority'] for flow in samples])
511         for priority in priorities:
512             tx_frames = sum(
513                 [int(flow['Tx_Frames']) for flow in samples
514                  if flow['IP_Priority'] == priority])
515             rx_frames = sum(
516                 [int(flow['Rx_Frames']) for flow in samples
517                  if flow['IP_Priority'] == priority])
518             prio_flows_num = len([flow for flow in samples
519                                   if flow['IP_Priority'] == priority])
520             avg_latency_ns = sum(
521                 [int(flow['Store-Forward_Avg_latency_ns']) for flow in samples
522                  if flow['IP_Priority'] == priority]) / prio_flows_num
523             min_latency_ns = min(
524                 [int(flow['Store-Forward_Min_latency_ns']) for flow in samples
525                  if flow['IP_Priority'] == priority])
526             max_latency_ns = max(
527                 [int(flow['Store-Forward_Max_latency_ns']) for flow in samples
528                  if flow['IP_Priority'] == priority])
529             tx_throughput = float(tx_frames) / duration
530             rx_throughput = float(rx_frames) / duration
531             results[priority] = {
532                 'InPackets': rx_frames,
533                 'OutPackets': tx_frames,
534                 'RxThroughput': round(rx_throughput, 3),
535                 'TxThroughput': round(tx_throughput, 3),
536                 'LatencyAvg': utils.safe_cast(avg_latency_ns, int, 0),
537                 'LatencyMin': utils.safe_cast(min_latency_ns, int, 0),
538                 'LatencyMax': utils.safe_cast(max_latency_ns, int, 0)
539             }
540         return results
541
542     def generate_samples(self, resource_helper, ports, duration):
543
544         stats = self._get_stats()
545         samples = {}
546         ports_stats = stats['port_statistics']
547         flows_stats = stats['flow_statistic']
548         pppoe_subs_per_port = stats['pppox_client_per_port']
549
550         # Get sorted list of ixia ports names
551         ixia_port_names = sorted([data['port_name'] for data in ports_stats])
552
553         # Set 'port_id' key for ports stats items
554         for item in ports_stats:
555             port_id = item.pop('port_name').split('-')[-1].strip()
556             item['port_id'] = int(port_id)
557
558         # Set 'id' key for flows stats items
559         for item in flows_stats:
560             flow_id = item.pop('Flow_Group').split('-')[1].strip()
561             item['id'] = int(flow_id)
562
563         # Set 'port_id' key for pppoe subs per port stats
564         for item in pppoe_subs_per_port:
565             port_id = item.pop('subs_port').split('-')[-1].strip()
566             item['port_id'] = int(port_id)
567
568         # Map traffic flows to ports
569         port_flow_map = collections.defaultdict(set)
570         for item in flows_stats:
571             tx_port = item.pop('Tx_Port')
572             tx_port_index = ixia_port_names.index(tx_port)
573             port_flow_map[tx_port_index].update([item['id']])
574
575         # Sort ports stats
576         ports_stats = sorted(ports_stats, key=lambda k: k['port_id'])
577
578         # Get priority flows stats
579         prio_flows_stats = self.get_priority_flows_stats(flows_stats, duration)
580         samples['priority_stats'] = prio_flows_stats
581
582         # this is not DPDK port num, but this is whatever number we gave
583         # when we selected ports and programmed the profile
584         for port_num in ports:
585             try:
586                 # reverse lookup port name from port_num so the stats dict is descriptive
587                 intf = resource_helper.vnfd_helper.find_interface_by_port(port_num)
588                 port_name = intf['name']
589                 port_id = ports_stats[port_num]['port_id']
590                 port_subs_stats = \
591                     [port_data for port_data in pppoe_subs_per_port
592                      if port_data.get('port_id') == port_id]
593
594                 avg_latency = \
595                     sum([float(self.get_flow_id_data(
596                         flows_stats, flow, 'Store-Forward_Avg_latency_ns'))
597                         for flow in port_flow_map[port_num]]) / len(port_flow_map[port_num])
598                 min_latency = \
599                     min([float(self.get_flow_id_data(
600                         flows_stats, flow, 'Store-Forward_Min_latency_ns'))
601                         for flow in port_flow_map[port_num]])
602                 max_latency = \
603                     max([float(self.get_flow_id_data(
604                         flows_stats, flow, 'Store-Forward_Max_latency_ns'))
605                         for flow in port_flow_map[port_num]])
606
607                 samples[port_name] = {
608                     'RxThroughputBps': float(ports_stats[port_num]['Bytes_Rx']) / duration,
609                     'TxThroughputBps': float(ports_stats[port_num]['Bytes_Tx']) / duration,
610                     'InPackets': int(ports_stats[port_num]['Valid_Frames_Rx']),
611                     'OutPackets': int(ports_stats[port_num]['Frames_Tx']),
612                     'InBytes': int(ports_stats[port_num]['Bytes_Rx']),
613                     'OutBytes': int(ports_stats[port_num]['Bytes_Tx']),
614                     'RxThroughput': float(ports_stats[port_num]['Valid_Frames_Rx']) / duration,
615                     'TxThroughput': float(ports_stats[port_num]['Frames_Tx']) / duration,
616                     'LatencyAvg': utils.safe_cast(avg_latency, int, 0),
617                     'LatencyMin': utils.safe_cast(min_latency, int, 0),
618                     'LatencyMax': utils.safe_cast(max_latency, int, 0)
619                 }
620
621                 if port_subs_stats:
622                     samples[port_name].update(
623                         {'SessionsUp': int(port_subs_stats[0]['Sessions_Up']),
624                          'SessionsDown': int(port_subs_stats[0]['Sessions_Down']),
625                          'SessionsNotStarted': int(port_subs_stats[0]['Sessions_Not_Started']),
626                          'SessionsTotal': int(port_subs_stats[0]['Sessions_Total'])}
627                     )
628
629             except IndexError:
630                 pass
631
632         return samples
633
634
635 class IxiaRfc2544Helper(Rfc2544ResourceHelper):
636
637     def is_done(self):
638         return self.latency and self.iteration.value > 10
639
640
641 class IxiaResourceHelper(ClientResourceHelper):
642
643     LATENCY_TIME_SLEEP = 120
644
645     def __init__(self, setup_helper, rfc_helper_type=None):
646         super(IxiaResourceHelper, self).__init__(setup_helper)
647         self.scenario_helper = setup_helper.scenario_helper
648
649         self._ixia_scenarios = {
650             "IxiaBasic": IxiaBasicScenario,
651             "IxiaL3": IxiaL3Scenario,
652             "IxiaPppoeClient": IxiaPppoeClientScenario,
653         }
654
655         self.client = ixnet_api.IxNextgen()
656
657         if rfc_helper_type is None:
658             rfc_helper_type = IxiaRfc2544Helper
659
660         self.rfc_helper = rfc_helper_type(self.scenario_helper)
661         self.uplink_ports = None
662         self.downlink_ports = None
663         self.context_cfg = None
664         self._ix_scenario = None
665         self._connect()
666
667     def _connect(self, client=None):
668         self.client.connect(self.vnfd_helper)
669
670     def setup(self):
671         super(IxiaResourceHelper, self).setup()
672         self._init_ix_scenario()
673
674     def stop_collect(self):
675         self._ix_scenario.stop_protocols()
676         self._terminated.value = 1
677
678     def generate_samples(self, ports, duration):
679         return self._ix_scenario.generate_samples(self, ports, duration)
680
681     def _init_ix_scenario(self):
682         ixia_config = self.scenario_helper.scenario_cfg.get('ixia_config', 'IxiaBasic')
683
684         if ixia_config in self._ixia_scenarios:
685             scenario_type = self._ixia_scenarios[ixia_config]
686
687             self._ix_scenario = scenario_type(self.client, self.context_cfg,
688                                               self.scenario_helper.scenario_cfg['options'])
689         else:
690             raise RuntimeError(
691                 "IXIA config type '{}' not supported".format(ixia_config))
692
693     def _initialize_client(self, traffic_profile):
694         """Initialize the IXIA IxNetwork client and configure the server"""
695         self.client.clear_config()
696         self.client.assign_ports()
697         self._ix_scenario.apply_config()
698         self._ix_scenario.create_traffic_model(traffic_profile)
699
700     def update_tracking_options(self):
701         self._ix_scenario.update_tracking_options()
702
703     def run_traffic(self, traffic_profile):
704         if self._terminated.value:
705             return
706
707         min_tol = self.rfc_helper.tolerance_low
708         max_tol = self.rfc_helper.tolerance_high
709         precision = self.rfc_helper.tolerance_precision
710         resolution = self.rfc_helper.resolution
711         default = "00:00:00:00:00:00"
712
713         self._build_ports()
714         traffic_profile.update_traffic_profile(self)
715         self._initialize_client(traffic_profile)
716
717         mac = {}
718         for port_name in self.vnfd_helper.port_pairs.all_ports:
719             intf = self.vnfd_helper.find_interface(name=port_name)
720             virt_intf = intf["virtual-interface"]
721             # we only know static traffic id by reading the json
722             # this is used by _get_ixia_trafficrofile
723             port_num = self.vnfd_helper.port_num(intf)
724             mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default)
725             mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default)
726
727         self._ix_scenario.run_protocols()
728
729         try:
730             while not self._terminated.value:
731                 first_run = traffic_profile.execute_traffic(self, self.client,
732                                                             mac)
733                 self.client_started.value = 1
734                 # pylint: disable=unnecessary-lambda
735                 utils.wait_until_true(lambda: self.client.is_traffic_stopped(),
736                                       timeout=traffic_profile.config.duration * 2)
737                 rfc2544_opts = self._ix_scenario.get_tc_rfc2544_options()
738                 samples = self.generate_samples(traffic_profile.ports,
739                                                 traffic_profile.config.duration)
740
741                 completed, samples = traffic_profile.get_drop_percentage(
742                     samples, min_tol, max_tol, precision, resolution,
743                     first_run=first_run, tc_rfc2544_opts=rfc2544_opts)
744                 self._queue.put(samples)
745
746                 if completed:
747                     self._terminated.value = 1
748
749         except Exception:  # pylint: disable=broad-except
750             LOG.exception('Run Traffic terminated')
751
752         self._ix_scenario.stop_protocols()
753         self.client_started.value = 0
754         self._terminated.value = 1
755
756     def run_test(self, traffic_profile, tasks_queue, results_queue, *args): # pragma: no cover
757         LOG.info("Ixia resource_helper run_test")
758         if self._terminated.value:
759             return
760
761         min_tol = self.rfc_helper.tolerance_low
762         max_tol = self.rfc_helper.tolerance_high
763         precision = self.rfc_helper.tolerance_precision
764         resolution = self.rfc_helper.resolution
765         default = "00:00:00:00:00:00"
766
767         self._build_ports()
768         traffic_profile.update_traffic_profile(self)
769         self._initialize_client(traffic_profile)
770
771         mac = {}
772         for port_name in self.vnfd_helper.port_pairs.all_ports:
773             intf = self.vnfd_helper.find_interface(name=port_name)
774             virt_intf = intf["virtual-interface"]
775             # we only know static traffic id by reading the json
776             # this is used by _get_ixia_trafficrofile
777             port_num = self.vnfd_helper.port_num(intf)
778             mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default)
779             mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default)
780
781         self._ix_scenario.run_protocols()
782
783         try:
784             completed = False
785             self.rfc_helper.iteration.value = 0
786             self.client_started.value = 1
787             while completed is False and not self._terminated.value:
788                 LOG.info("Wait for task ...")
789
790                 try:
791                     task = tasks_queue.get(True, 5)
792                 except moves.queue.Empty:
793                     continue
794                 else:
795                     if task != 'RUN_TRAFFIC':
796                         continue
797
798                 self.rfc_helper.iteration.value += 1
799                 LOG.info("Got %s task, start iteration %d", task,
800                          self.rfc_helper.iteration.value)
801                 first_run = traffic_profile.execute_traffic(self, self.client,
802                                                             mac)
803                 # pylint: disable=unnecessary-lambda
804                 utils.wait_until_true(lambda: self.client.is_traffic_stopped(),
805                                       timeout=traffic_profile.config.duration * 2)
806                 samples = self.generate_samples(traffic_profile.ports,
807                                                 traffic_profile.config.duration)
808
809                 completed, samples = traffic_profile.get_drop_percentage(
810                     samples, min_tol, max_tol, precision, resolution,
811                     first_run=first_run)
812                 self._queue.put(samples)
813
814                 if completed:
815                     LOG.debug("IxiaResourceHelper::run_test - test completed")
816                     results_queue.put('COMPLETE')
817                 else:
818                     results_queue.put('CONTINUE')
819                 tasks_queue.task_done()
820
821         except Exception:  # pylint: disable=broad-except
822             LOG.exception('Run Traffic terminated')
823
824         self._ix_scenario.stop_protocols()
825         self.client_started.value = 0
826         LOG.debug("IxiaResourceHelper::run_test done")
827
828
829 class IxiaTrafficGen(SampleVNFTrafficGen):
830
831     APP_NAME = 'Ixia'
832
833     def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None):
834         if resource_helper_type is None:
835             resource_helper_type = IxiaResourceHelper
836
837         super(IxiaTrafficGen, self).__init__(name, vnfd, setup_env_helper_type,
838                                              resource_helper_type)
839         self._ixia_traffic_gen = None
840         self.ixia_file_name = ''
841         self.vnf_port_pairs = []
842
843     def _check_status(self):
844         pass
845
846     def terminate(self):
847         self.resource_helper.stop_collect()
848         super(IxiaTrafficGen, self).terminate()