Enable ixnet traffic generator to run traffic
[yardstick.git] / yardstick / network_services / libs / ixia_libs / IxNet / IxNet.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 from __future__ import absolute_import
16 from __future__ import print_function
17 import sys
18 import logging
19
20 import re
21 from itertools import product
22
23 from yardstick.common.utils import ErrorClass
24
25 try:
26     import IxNetwork
27 except ImportError:
28     IxNetwork = ErrorClass
29
30 log = logging.getLogger(__name__)
31
32 IP_VERSION_4 = 4
33 IP_VERSION_6 = 6
34
35
36 class TrafficStreamHelper(object):
37
38     TEMPLATE = '{0.traffic_item}/{0.stream}:{0.param_id}/{1}'
39
40     def __init__(self, traffic_item, stream, param_id):
41         super(TrafficStreamHelper, self).__init__()
42         self.traffic_item = traffic_item
43         self.stream = stream
44         self.param_id = param_id
45
46     def __getattr__(self, item):
47         return self.TEMPLATE.format(self, item)
48
49
50 class FramesizeHelper(object):
51
52     def __init__(self):
53         super(FramesizeHelper, self).__init__()
54         self.weighted_pairs = []
55         self.weighted_range_pairs = []
56
57     @property
58     def weighted_pairs_arg(self):
59         return '-weightedPairs', self.weighted_pairs
60
61     @property
62     def weighted_range_pairs_arg(self):
63         return '-weightedRangePairs', self.weighted_range_pairs
64
65     def make_args(self, *args):
66         return self.weighted_pairs_arg + self.weighted_range_pairs_arg + args
67
68     def populate_data(self, framesize_data):
69         for key, value in framesize_data.items():
70             if value == '0':
71                 continue
72
73             replaced = re.sub('[Bb]', '', key)
74             self.weighted_pairs.extend([
75                 replaced,
76                 value,
77             ])
78             pairs = [
79                 replaced,
80                 replaced,
81                 value,
82             ]
83             self.weighted_range_pairs.append(pairs)
84
85
86 class IxNextgen(object):
87
88     STATS_NAME_MAP = {
89         "traffic_item": 'Traffic Item',
90         "Tx_Frames": 'Tx Frames',
91         "Rx_Frames": 'Rx Frames',
92         "Tx_Frame_Rate": 'Tx Frame Rate',
93         "Rx_Frame_Rate": 'Tx Frame Rate',
94         "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)',
95         "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)',
96         "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)',
97     }
98
99     PORT_STATS_NAME_MAP = {
100         "stat_name": 'Stat Name',
101         "Frames_Tx": 'Frames Tx.',
102         "Valid_Frames_Rx": 'Valid Frames Rx.',
103         "Frames_Tx_Rate": 'Frames Tx. Rate',
104         "Valid_Frames_Rx_Rate": 'Valid Frames Rx. Rate',
105         "Tx_Rate_Kbps": 'Tx. Rate (Kbps)',
106         "Rx_Rate_Kbps": 'Rx. Rate (Kbps)',
107         "Tx_Rate_Mbps": 'Tx. Rate (Mbps)',
108         "Rx_Rate_Mbps": 'Rx. Rate (Mbps)',
109     }
110
111     LATENCY_NAME_MAP = {
112         "Store-Forward_Avg_latency_ns": 'Store-Forward Avg Latency (ns)',
113         "Store-Forward_Min_latency_ns": 'Store-Forward Min Latency (ns)',
114         "Store-Forward_Max_latency_ns": 'Store-Forward Max Latency (ns)',
115     }
116
117     RANDOM_MASK_MAP = {
118         IP_VERSION_4: '0.0.0.255',
119         IP_VERSION_6: '0:0:0:0:0:0:0:ff',
120     }
121
122     MODE_SEEDS_MAP = {
123         0: ('private', ['256', '2048']),
124     }
125
126     MODE_SEEDS_DEFAULT = 'public', ['2048', '256']
127
128     @staticmethod
129     def find_view_obj(view_name, views):
130         edited_view_name = '::ixNet::OBJ-/statistics/view:"{}"'.format(view_name)
131         return next((view for view in views if edited_view_name == view), '')
132
133     @staticmethod
134     def get_config(tg_cfg):
135         external_interface = tg_cfg["vdu"][0]["external-interface"]
136         card_port0 = external_interface[0]["virtual-interface"]["vpci"]
137         card_port1 = external_interface[1]["virtual-interface"]["vpci"]
138         card0, port0 = card_port0.split(':')[:2]
139         card1, port1 = card_port1.split(':')[:2]
140         cfg = {
141             'py_lib_path': tg_cfg["mgmt-interface"]["tg-config"]["py_lib_path"],
142             'machine': tg_cfg["mgmt-interface"]["ip"],
143             'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"],
144             'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"],
145             'card1': card0,
146             'port1': port0,
147             'card2': card1,
148             'port2': port1,
149             'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"],
150             'version': tg_cfg["mgmt-interface"]["tg-config"]["version"],
151             'bidir': True,
152         }
153         return cfg
154
155     def __init__(self, ixnet=None):
156         self.ixnet = ixnet
157         self._objRefs = dict()
158         self._cfg = None
159         self._logger = logging.getLogger(__name__)
160         self._params = None
161         self._bidir = None
162
163     def iter_over_get_lists(self, x1, x2, y2, offset=0):
164         for x in self.ixnet.getList(x1, x2):
165             y_list = self.ixnet.getList(x, y2)
166             for i, y in enumerate(y_list, offset):
167                 yield x, y, i
168
169     def set_random_ip_multi_attribute(self, ipv4, seed, fixed_bits, random_mask, l3_count):
170         self.ixnet.setMultiAttribute(
171             ipv4,
172             '-seed', str(seed),
173             '-fixedBits', str(fixed_bits),
174             '-randomMask', str(random_mask),
175             '-valueType', 'random',
176             '-countValue', str(l3_count))
177
178     def set_random_ip_multi_attributes(self, ip, version, seeds, l3):
179         try:
180             random_mask = self.RANDOM_MASK_MAP[version]
181         except KeyError:
182             raise ValueError('Unknown version %s' % version)
183
184         l3_count = l3['count']
185         if "srcIp" in ip:
186             fixed_bits = l3['srcip4']
187             self.set_random_ip_multi_attribute(ip, seeds[0], fixed_bits, random_mask, l3_count)
188         if "dstIp" in ip:
189             fixed_bits = l3['dstip4']
190             self.set_random_ip_multi_attribute(ip, seeds[1], fixed_bits, random_mask, l3_count)
191
192     def add_ip_header(self, params, version):
193         for it, ep, i in self.iter_over_get_lists('/traffic', 'trafficItem', "configElement"):
194             mode, seeds = self.MODE_SEEDS_MAP.get(i, self.MODE_SEEDS_DEFAULT)
195             l3 = params[mode]['outer_l3']
196
197             for ip, ip_bits, _ in self.iter_over_get_lists(ep, 'stack', 'field'):
198                 self.set_random_ip_multi_attributes(ip_bits, version, seeds, l3)
199
200         self.ixnet.commit()
201
202     def _connect(self, tg_cfg):
203         self._cfg = self.get_config(tg_cfg)
204
205         sys.path.append(self._cfg["py_lib_path"])
206         self.ixnet = IxNetwork.IxNet()
207
208         machine = self._cfg['machine']
209         port = str(self._cfg['port'])
210         version = str(self._cfg['version'])
211         result = self.ixnet.connect(machine, '-port', port, '-version', version)
212         return result
213
214     def clear_ixia_config(self):
215         self.ixnet.execute('newConfig')
216
217     def load_ixia_profile(self, profile):
218         self.ixnet.execute('loadConfig', self.ixnet.readFrom(profile))
219
220     def ix_load_config(self, profile):
221         self.clear_ixia_config()
222         self.load_ixia_profile(profile)
223
224     def ix_assign_ports(self):
225         vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport')
226         ports = [
227             (self._cfg['chassis'], self._cfg['card1'], self._cfg['port1']),
228             (self._cfg['chassis'], self._cfg['card2'], self._cfg['port2']),
229         ]
230
231         vport_list = self.ixnet.getList("/", "vport")
232         self.ixnet.execute('assignPorts', ports, [], vport_list, True)
233         self.ixnet.commit()
234
235         for vport in vports:
236             if self.ixnet.getAttribute(vport, '-state') != 'up':
237                 log.error("Both thr ports are down...")
238
239     def ix_update_frame(self, params):
240         streams = ["configElement"]
241
242         for param in params.values():
243             framesize_data = FramesizeHelper()
244             traffic_items = self.ixnet.getList('/traffic', 'trafficItem')
245             param_id = param['id']
246             for traffic_item, stream in product(traffic_items, streams):
247                 helper = TrafficStreamHelper(traffic_item, stream, param_id)
248
249                 self.ixnet.setMultiAttribute(helper.transmissionControl,
250                                              '-type', '{0}'.format(param['traffic_type']),
251                                              '-duration', '{0}'.format(param['duration']))
252
253                 stream_frame_rate_path = helper.frameRate
254                 self.ixnet.setMultiAttribute(stream_frame_rate_path, '-rate', param['iload'])
255                 if param['outer_l2']['framesPerSecond']:
256                     self.ixnet.setMultiAttribute(stream_frame_rate_path,
257                                                  '-type', 'framesPerSecond')
258
259                 framesize_data.populate_data(param['outer_l2']['framesize'])
260
261                 make_attr_args = framesize_data.make_args('-incrementFrom', '66',
262                                                           '-randomMin', '66',
263                                                           '-quadGaussian', [],
264                                                           '-type', 'weightedPairs',
265                                                           '-presetDistribution', 'cisco',
266                                                           '-incrementTo', '1518')
267
268                 self.ixnet.setMultiAttribute(helper.frameSize, *make_attr_args)
269
270                 self.ixnet.commit()
271
272     def update_ether_multi_attribute(self, ether, mac_addr):
273         self.ixnet.setMultiAttribute(ether,
274                                      '-singleValue', mac_addr,
275                                      '-fieldValue', mac_addr,
276                                      '-valueType', 'singleValue')
277
278     def update_ether_multi_attributes(self, ether, l2):
279         if "ethernet.header.destinationAddress" in ether:
280             self.update_ether_multi_attribute(ether, str(l2['dstmac']))
281
282         if "ethernet.header.sourceAddress" in ether:
283             self.update_ether_multi_attribute(ether, str(l2['srcmac']))
284
285     def ix_update_ether(self, params):
286         for ti, ep, index in self.iter_over_get_lists('/traffic', 'trafficItem',
287                                                       "configElement", 1):
288             iter1 = (v['outer_l2'] for v in params.values() if str(v['id']) == str(index))
289             try:
290                 l2 = next(iter1, {})
291             except KeyError:
292                 continue
293
294             for ip, ether, _ in self.iter_over_get_lists(ep, 'stack', 'field'):
295                 self.update_ether_multi_attributes(ether, l2)
296
297         self.ixnet.commit()
298
299     def ix_update_udp(self, params):
300         pass
301
302     def ix_update_tcp(self, params):
303         pass
304
305     def ix_start_traffic(self):
306         tis = self.ixnet.getList('/traffic', 'trafficItem')
307         for ti in tis:
308             self.ixnet.execute('generate', [ti])
309             self.ixnet.execute('apply', '/traffic')
310             self.ixnet.execute('start', '/traffic')
311
312     def ix_stop_traffic(self):
313         tis = self.ixnet.getList('/traffic', 'trafficItem')
314         for _ in tis:
315             self.ixnet.execute('stop', '/traffic')
316
317     def build_stats_map(self, view_obj, name_map):
318         return {kl: self.execute_get_column_values(view_obj, kr) for kl, kr in name_map.items()}
319
320     def execute_get_column_values(self, view_obj, name):
321         return self.ixnet.execute('getColumnValues', view_obj, name)
322
323     def ix_get_statistics(self):
324         views = self.ixnet.getList('/statistics', 'view')
325         stats = {}
326         view_obj = self.find_view_obj("Traffic Item Statistics", views)
327         stats = self.build_stats_map(view_obj, self.STATS_NAME_MAP)
328
329         view_obj = self.find_view_obj("Port Statistics", views)
330         ports_stats = self.build_stats_map(view_obj, self.PORT_STATS_NAME_MAP)
331
332         view_obj = self.find_view_obj("Flow Statistics", views)
333         stats["latency"] = self.build_stats_map(view_obj, self.LATENCY_NAME_MAP)
334
335         return stats, ports_stats