add yardstick iruya 9.0.0 release notes
[yardstick.git] / yardstick / network_services / vnf_generic / vnf / vpp_helpers.py
1 # Copyright (c) 2019 Viosoft 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 binascii
16 import ipaddress
17 import json
18 import logging
19 import os
20 import re
21 import tempfile
22 import time
23 from collections import OrderedDict
24
25 from yardstick.common import constants
26 from yardstick.common import exceptions
27 from yardstick.network_services.helpers.cpu import CpuSysCores
28 from yardstick.network_services.vnf_generic.vnf.sample_vnf import \
29     DpdkVnfSetupEnvHelper
30
31 LOG = logging.getLogger(__name__)
32
33
34 class VppConfigGenerator(object):
35     VPP_LOG_FILE = '/tmp/vpe.log'
36
37     def __init__(self):
38         self._nodeconfig = {}
39         self._vpp_config = ''
40
41     def add_config_item(self, config, value, path):
42         if len(path) == 1:
43             config[path[0]] = value
44             return
45         if path[0] not in config:
46             config[path[0]] = {}
47         elif isinstance(config[path[0]], str):
48             config[path[0]] = {} if config[path[0]] == '' \
49                 else {config[path[0]]: ''}
50         self.add_config_item(config[path[0]], value, path[1:])
51
52     def add_unix_log(self, value=None):
53         path = ['unix', 'log']
54         if value is None:
55             value = self.VPP_LOG_FILE
56         self.add_config_item(self._nodeconfig, value, path)
57
58     def add_unix_cli_listen(self, value='/run/vpp/cli.sock'):
59         path = ['unix', 'cli-listen']
60         self.add_config_item(self._nodeconfig, value, path)
61
62     def add_unix_nodaemon(self):
63         path = ['unix', 'nodaemon']
64         self.add_config_item(self._nodeconfig, '', path)
65
66     def add_unix_coredump(self):
67         path = ['unix', 'full-coredump']
68         self.add_config_item(self._nodeconfig, '', path)
69
70     def add_dpdk_dev(self, *devices):
71         for device in devices:
72             if VppConfigGenerator.pci_dev_check(device):
73                 path = ['dpdk', 'dev {0}'.format(device)]
74                 self.add_config_item(self._nodeconfig, '', path)
75
76     def add_dpdk_cryptodev(self, count, cryptodev):
77         for i in range(count):
78             cryptodev_config = 'dev {0}'.format(
79                 re.sub(r'\d.\d$', '1.' + str(i), cryptodev))
80             path = ['dpdk', cryptodev_config]
81             self.add_config_item(self._nodeconfig, '', path)
82         self.add_dpdk_uio_driver('igb_uio')
83
84     def add_dpdk_sw_cryptodev(self, sw_pmd_type, socket_id, count):
85         for _ in range(count):
86             cryptodev_config = 'vdev cryptodev_{0}_pmd,socket_id={1}'. \
87                 format(sw_pmd_type, str(socket_id))
88             path = ['dpdk', cryptodev_config]
89             self.add_config_item(self._nodeconfig, '', path)
90
91     def add_dpdk_dev_default_rxq(self, value):
92         path = ['dpdk', 'dev default', 'num-rx-queues']
93         self.add_config_item(self._nodeconfig, value, path)
94
95     def add_dpdk_dev_default_rxd(self, value):
96         path = ['dpdk', 'dev default', 'num-rx-desc']
97         self.add_config_item(self._nodeconfig, value, path)
98
99     def add_dpdk_dev_default_txd(self, value):
100         path = ['dpdk', 'dev default', 'num-tx-desc']
101         self.add_config_item(self._nodeconfig, value, path)
102
103     def add_dpdk_log_level(self, value):
104         path = ['dpdk', 'log-level']
105         self.add_config_item(self._nodeconfig, value, path)
106
107     def add_dpdk_socketmem(self, value):
108         path = ['dpdk', 'socket-mem']
109         self.add_config_item(self._nodeconfig, value, path)
110
111     def add_dpdk_num_mbufs(self, value):
112         path = ['dpdk', 'num-mbufs']
113         self.add_config_item(self._nodeconfig, value, path)
114
115     def add_dpdk_uio_driver(self, value=None):
116         path = ['dpdk', 'uio-driver']
117         self.add_config_item(self._nodeconfig, value, path)
118
119     def add_cpu_main_core(self, value):
120         path = ['cpu', 'main-core']
121         self.add_config_item(self._nodeconfig, value, path)
122
123     def add_cpu_corelist_workers(self, value):
124         path = ['cpu', 'corelist-workers']
125         self.add_config_item(self._nodeconfig, value, path)
126
127     def add_heapsize(self, value):
128         path = ['heapsize']
129         self.add_config_item(self._nodeconfig, value, path)
130
131     def add_ip6_hash_buckets(self, value):
132         path = ['ip6', 'hash-buckets']
133         self.add_config_item(self._nodeconfig, value, path)
134
135     def add_ip6_heap_size(self, value):
136         path = ['ip6', 'heap-size']
137         self.add_config_item(self._nodeconfig, value, path)
138
139     def add_ip_heap_size(self, value):
140         path = ['ip', 'heap-size']
141         self.add_config_item(self._nodeconfig, value, path)
142
143     def add_statseg_size(self, value):
144         path = ['statseg', 'size']
145         self.add_config_item(self._nodeconfig, value, path)
146
147     def add_plugin(self, state, *plugins):
148         for plugin in plugins:
149             path = ['plugins', 'plugin {0}'.format(plugin), state]
150             self.add_config_item(self._nodeconfig, ' ', path)
151
152     def add_dpdk_no_multi_seg(self):
153         path = ['dpdk', 'no-multi-seg']
154         self.add_config_item(self._nodeconfig, '', path)
155
156     def add_dpdk_no_tx_checksum_offload(self):
157         path = ['dpdk', 'no-tx-checksum-offload']
158         self.add_config_item(self._nodeconfig, '', path)
159
160     def dump_config(self, obj=None, level=-1):
161         if obj is None:
162             obj = self._nodeconfig
163         obj = OrderedDict(sorted(obj.items()))
164
165         indent = '  '
166         if level >= 0:
167             self._vpp_config += '{}{{\n'.format(level * indent)
168         if isinstance(obj, dict):
169             for key, val in obj.items():
170                 if hasattr(val, '__iter__') and not isinstance(val, str):
171                     self._vpp_config += '{}{}\n'.format((level + 1) * indent,
172                                                         key)
173                     self.dump_config(val, level + 1)
174                 else:
175                     self._vpp_config += '{}{} {}\n'.format(
176                         (level + 1) * indent,
177                         key, val)
178         if level >= 0:
179             self._vpp_config += '{}}}\n'.format(level * indent)
180
181         return self._vpp_config
182
183     @staticmethod
184     def pci_dev_check(pci_dev):
185         pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:"
186                              "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$")
187         if not pattern.match(pci_dev):
188             raise ValueError('PCI address {addr} is not in valid format '
189                              'xxxx:xx:xx.x'.format(addr=pci_dev))
190         return True
191
192
193 class VppSetupEnvHelper(DpdkVnfSetupEnvHelper):
194     APP_NAME = "vpp"
195     CFG_CONFIG = "/etc/vpp/startup.conf"
196     CFG_SCRIPT = ""
197     PIPELINE_COMMAND = ""
198     QAT_DRIVER = "qat_dh895xcc"
199     VNF_TYPE = "IPSEC"
200     VAT_BIN_NAME = 'vpp_api_test'
201
202     def __init__(self, vnfd_helper, ssh_helper, scenario_helper):
203         super(VppSetupEnvHelper, self).__init__(vnfd_helper, ssh_helper,
204                                                 scenario_helper)
205         self.sys_cores = CpuSysCores(self.ssh_helper)
206
207     def kill_vnf(self):
208         ret_code, _, _ = \
209             self.ssh_helper.execute(
210                 'service {name} stop'.format(name=self.APP_NAME))
211         if int(ret_code):
212             raise RuntimeError(
213                 'Failed to stop service {name}'.format(name=self.APP_NAME))
214
215     def tear_down(self):
216         pass
217
218     def start_vpp_service(self):
219         ret_code, _, _ = \
220             self.ssh_helper.execute(
221                 'service {name} restart'.format(name=self.APP_NAME))
222         if int(ret_code):
223             raise RuntimeError(
224                 'Failed to start service {name}'.format(name=self.APP_NAME))
225
226     def _update_vnfd_helper(self, additional_data, iface_key=None):
227         for k, v in additional_data.items():
228             if iface_key is None:
229                 if isinstance(v, dict) and k in self.vnfd_helper:
230                     self.vnfd_helper[k].update(v)
231                 else:
232                     self.vnfd_helper[k] = v
233             else:
234                 if isinstance(v,
235                               dict) and k in self.vnfd_helper.find_virtual_interface(
236                     ifname=iface_key):
237                     self.vnfd_helper.find_virtual_interface(ifname=iface_key)[
238                         k].update(v)
239                 else:
240                     self.vnfd_helper.find_virtual_interface(ifname=iface_key)[
241                         k] = v
242
243     def get_value_by_interface_key(self, interface, key):
244         try:
245             return self.vnfd_helper.find_virtual_interface(
246                 ifname=interface).get(key)
247         except (KeyError, ValueError):
248             return None
249
250     def crypto_device_init(self, pci_addr, numvfs):
251         # QAT device must be re-bound to kernel driver before initialization.
252         self.dpdk_bind_helper.load_dpdk_driver(self.QAT_DRIVER)
253
254         # Stop VPP to prevent deadlock.
255         self.kill_vnf()
256
257         current_driver = self.get_pci_dev_driver(pci_addr.replace(':', r'\:'))
258         if current_driver is not None:
259             self.pci_driver_unbind(pci_addr)
260
261         # Bind to kernel driver.
262         self.dpdk_bind_helper.bind(pci_addr, self.QAT_DRIVER.replace('qat_', ''))
263
264         # Initialize QAT VFs.
265         if numvfs > 0:
266             self.set_sriov_numvfs(pci_addr, numvfs)
267
268     def get_sriov_numvfs(self, pf_pci_addr):
269         command = 'cat /sys/bus/pci/devices/{pci}/sriov_numvfs'. \
270             format(pci=pf_pci_addr.replace(':', r'\:'))
271         _, stdout, _ = self.ssh_helper.execute(command)
272         try:
273             return int(stdout)
274         except ValueError:
275             LOG.debug('Reading sriov_numvfs info failed')
276             return 0
277
278     def set_sriov_numvfs(self, pf_pci_addr, numvfs=0):
279         command = "sh -c 'echo {num} | tee /sys/bus/pci/devices/{pci}/sriov_numvfs'". \
280             format(num=numvfs, pci=pf_pci_addr.replace(':', r'\:'))
281         self.ssh_helper.execute(command)
282
283     def pci_driver_unbind(self, pci_addr):
284         command = "sh -c 'echo {pci} | tee /sys/bus/pci/devices/{pcie}/driver/unbind'". \
285             format(pci=pci_addr, pcie=pci_addr.replace(':', r'\:'))
286         self.ssh_helper.execute(command)
287
288     def get_pci_dev_driver(self, pci_addr):
289         cmd = 'lspci -vmmks {0}'.format(pci_addr)
290         ret_code, stdout, _ = self.ssh_helper.execute(cmd)
291         if int(ret_code):
292             raise RuntimeError("'{0}' failed".format(cmd))
293         for line in stdout.splitlines():
294             if not line:
295                 continue
296             name = None
297             value = None
298             try:
299                 name, value = line.split("\t", 1)
300             except ValueError:
301                 if name == "Driver:":
302                     return None
303             if name == 'Driver:':
304                 return value
305         return None
306
307     def vpp_create_ipsec_tunnels(self, if1_ip_addr, if2_ip_addr, if_name,
308                                  n_tunnels, n_connections, crypto_alg,
309                                  crypto_key, integ_alg, integ_key, addrs_ip,
310                                  spi_1=10000, spi_2=20000):
311         mask_length = 32
312         if n_connections <= n_tunnels:
313             count = 1
314         else:
315             count = int(n_connections / n_tunnels)
316         addr_ip_i = int(ipaddress.ip_address(str(addrs_ip)))
317         dst_start_ip = addr_ip_i
318
319         tmp_fd, tmp_path = tempfile.mkstemp()
320
321         vpp_ifname = self.get_value_by_interface_key(if_name, 'vpp_name')
322         ckey = binascii.hexlify(crypto_key.encode())
323         ikey = binascii.hexlify(integ_key.encode())
324
325         integ = ''
326         if crypto_alg.alg_name != 'aes-gcm-128':
327             integ = 'integ_alg {integ_alg} ' \
328                     'local_integ_key {local_integ_key} ' \
329                     'remote_integ_key {remote_integ_key} ' \
330                 .format(integ_alg=integ_alg.alg_name,
331                         local_integ_key=ikey,
332                         remote_integ_key=ikey)
333         create_tunnels_cmds = 'ipsec_tunnel_if_add_del ' \
334                               'local_spi {local_spi} ' \
335                               'remote_spi {remote_spi} ' \
336                               'crypto_alg {crypto_alg} ' \
337                               'local_crypto_key {local_crypto_key} ' \
338                               'remote_crypto_key {remote_crypto_key} ' \
339                               '{integ} ' \
340                               'local_ip {local_ip} ' \
341                               'remote_ip {remote_ip}\n'
342         start_tunnels_cmds = 'ip_add_del_route {raddr}/{mask} via {addr} ipsec{i}\n' \
343                              'exec set interface unnumbered ipsec{i} use {uifc}\n' \
344                              'sw_interface_set_flags ipsec{i} admin-up\n'
345
346         with os.fdopen(tmp_fd, 'w') as tmp_file:
347             for i in range(0, n_tunnels):
348                 create_tunnel = create_tunnels_cmds.format(local_spi=spi_1 + i,
349                                                            remote_spi=spi_2 + i,
350                                                            crypto_alg=crypto_alg.alg_name,
351                                                            local_crypto_key=ckey,
352                                                            remote_crypto_key=ckey,
353                                                            integ=integ,
354                                                            local_ip=if1_ip_addr,
355                                                            remote_ip=if2_ip_addr)
356                 tmp_file.write(create_tunnel)
357         self.execute_script(tmp_path, json_out=False, copy_on_execute=True)
358         os.remove(tmp_path)
359
360         tmp_fd, tmp_path = tempfile.mkstemp()
361
362         with os.fdopen(tmp_fd, 'w') as tmp_file:
363             for i in range(0, n_tunnels):
364                 if count > 1:
365                     dst_start_ip = addr_ip_i + i * count
366                     dst_end_ip = ipaddress.ip_address(dst_start_ip + count - 1)
367                     ips = [ipaddress.ip_address(ip) for ip in
368                            [str(ipaddress.ip_address(dst_start_ip)),
369                             str(dst_end_ip)]]
370                     lowest_ip, highest_ip = min(ips), max(ips)
371                     mask_length = self.get_prefix_length(int(lowest_ip),
372                                                          int(highest_ip),
373                                                          lowest_ip.max_prefixlen)
374                     # TODO check duplicate route for some IPs
375                 elif count == 1:
376                     dst_start_ip = addr_ip_i + i
377                 start_tunnel = start_tunnels_cmds.format(
378                     raddr=str(ipaddress.ip_address(dst_start_ip)),
379                     mask=mask_length,
380                     addr=if2_ip_addr,
381                     i=i, count=count,
382                     uifc=vpp_ifname)
383                 tmp_file.write(start_tunnel)
384             # TODO add route for remain IPs
385
386         self.execute_script(tmp_path, json_out=False, copy_on_execute=True)
387         os.remove(tmp_path)
388
389     def apply_config(self, vpp_cfg, restart_vpp=True):
390         vpp_config = vpp_cfg.dump_config()
391         ret, _, _ = \
392             self.ssh_helper.execute('echo "{config}" | sudo tee {filename}'.
393                                     format(config=vpp_config,
394                                            filename=self.CFG_CONFIG))
395         if ret != 0:
396             raise RuntimeError('Writing config file failed')
397         if restart_vpp:
398             self.start_vpp_service()
399
400     def vpp_route_add(self, network, prefix_len, gateway=None, interface=None,
401                       use_sw_index=True, resolve_attempts=10,
402                       count=1, vrf=None, lookup_vrf=None, multipath=False,
403                       weight=None, local=False):
404         if interface:
405             if use_sw_index:
406                 int_cmd = ('sw_if_index {}'.format(
407                     self.get_value_by_interface_key(interface,
408                                                     'vpp_sw_index')))
409             else:
410                 int_cmd = interface
411         else:
412             int_cmd = ''
413
414         rap = 'resolve-attempts {}'.format(resolve_attempts) \
415             if resolve_attempts else ''
416
417         via = 'via {}'.format(gateway) if gateway else ''
418
419         cnt = 'count {}'.format(count) \
420             if count else ''
421
422         vrf = 'vrf {}'.format(vrf) if vrf else ''
423
424         lookup_vrf = 'lookup-in-vrf {}'.format(
425             lookup_vrf) if lookup_vrf else ''
426
427         multipath = 'multipath' if multipath else ''
428
429         weight = 'weight {}'.format(weight) if weight else ''
430
431         local = 'local' if local else ''
432
433         with VatTerminal(self.ssh_helper, json_param=False) as vat:
434             vat.vat_terminal_exec_cmd_from_template('add_route.vat',
435                                                     network=network,
436                                                     prefix_length=prefix_len,
437                                                     via=via,
438                                                     vrf=vrf,
439                                                     interface=int_cmd,
440                                                     resolve_attempts=rap,
441                                                     count=cnt,
442                                                     lookup_vrf=lookup_vrf,
443                                                     multipath=multipath,
444                                                     weight=weight,
445                                                     local=local)
446
447     def add_arp_on_dut(self, iface_key, ip_address, mac_address):
448         with VatTerminal(self.ssh_helper) as vat:
449             return vat.vat_terminal_exec_cmd_from_template(
450                 'add_ip_neighbor.vat',
451                 sw_if_index=self.get_value_by_interface_key(iface_key,
452                                                             'vpp_sw_index'),
453                 ip_address=ip_address, mac_address=mac_address)
454
455     def set_ip(self, interface, address, prefix_length):
456         with VatTerminal(self.ssh_helper) as vat:
457             return vat.vat_terminal_exec_cmd_from_template(
458                 'add_ip_address.vat',
459                 sw_if_index=self.get_value_by_interface_key(interface,
460                                                             'vpp_sw_index'),
461                 address=address, prefix_length=prefix_length)
462
463     def set_interface_state(self, interface, state):
464         sw_if_index = self.get_value_by_interface_key(interface,
465                                                       'vpp_sw_index')
466
467         if state == 'up':
468             state = 'admin-up link-up'
469         elif state == 'down':
470             state = 'admin-down link-down'
471         else:
472             raise ValueError('Unexpected interface state: {}'.format(state))
473         with VatTerminal(self.ssh_helper) as vat:
474             return vat.vat_terminal_exec_cmd_from_template(
475                 'set_if_state.vat', sw_if_index=sw_if_index, state=state)
476
477     def vpp_set_interface_mtu(self, interface, mtu=9200):
478         sw_if_index = self.get_value_by_interface_key(interface,
479                                                       'vpp_sw_index')
480         if sw_if_index:
481             with VatTerminal(self.ssh_helper, json_param=False) as vat:
482                 vat.vat_terminal_exec_cmd_from_template(
483                     "hw_interface_set_mtu.vat", sw_if_index=sw_if_index,
484                     mtu=mtu)
485
486     def vpp_interfaces_ready_wait(self, timeout=30):
487         if_ready = False
488         not_ready = []
489         start = time.time()
490         while not if_ready:
491             out = self.vpp_get_interface_data()
492             if time.time() - start > timeout:
493                 for interface in out:
494                     if interface.get('admin_up_down') == 1:
495                         if interface.get('link_up_down') != 1:
496                             LOG.debug('%s link-down',
497                                       interface.get('interface_name'))
498                 raise RuntimeError('timeout, not up {0}'.format(not_ready))
499             not_ready = []
500             for interface in out:
501                 if interface.get('admin_up_down') == 1:
502                     if interface.get('link_up_down') != 1:
503                         not_ready.append(interface.get('interface_name'))
504             if not not_ready:
505                 if_ready = True
506             else:
507                 LOG.debug('Interfaces still in link-down state: %s, '
508                           'waiting...', not_ready)
509                 time.sleep(1)
510
511     def vpp_get_interface_data(self, interface=None):
512         with VatTerminal(self.ssh_helper) as vat:
513             response = vat.vat_terminal_exec_cmd_from_template(
514                 "interface_dump.vat")
515         data = response[0]
516         if interface is not None:
517             if isinstance(interface, str):
518                 param = "interface_name"
519             elif isinstance(interface, int):
520                 param = "sw_if_index"
521             else:
522                 raise TypeError
523             for data_if in data:
524                 if data_if[param] == interface:
525                     return data_if
526             return dict()
527         return data
528
529     def update_vpp_interface_data(self):
530         data = {}
531         interface_dump_json = self.execute_script_json_out(
532             "dump_interfaces.vat")
533         interface_list = json.loads(interface_dump_json)
534         for interface in self.vnfd_helper.interfaces:
535             if_mac = interface['virtual-interface']['local_mac']
536             interface_dict = VppSetupEnvHelper.get_vpp_interface_by_mac(
537                 interface_list, if_mac)
538             if not interface_dict:
539                 LOG.debug('Interface %s not found by MAC %s', interface,
540                           if_mac)
541                 continue
542             data[interface['virtual-interface']['ifname']] = {
543                 'vpp_name': interface_dict["interface_name"],
544                 'vpp_sw_index': interface_dict["sw_if_index"]
545             }
546         for iface_key, updated_vnfd in data.items():
547             self._update_vnfd_helper(updated_vnfd, iface_key)
548
549     def iface_update_numa(self):
550         iface_numa = {}
551         for interface in self.vnfd_helper.interfaces:
552             cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(
553                 interface["virtual-interface"]["vpci"])
554             ret, out, _ = self.ssh_helper.execute(cmd)
555             if ret == 0:
556                 try:
557                     numa_node = int(out)
558                     if numa_node < 0:
559                         if self.vnfd_helper["cpuinfo"][-1][3] + 1 == 1:
560                             iface_numa[
561                                 interface['virtual-interface']['ifname']] = {
562                                 'numa_node': 0
563                             }
564                         else:
565                             raise ValueError
566                     else:
567                         iface_numa[
568                             interface['virtual-interface']['ifname']] = {
569                             'numa_node': numa_node
570                         }
571                 except ValueError:
572                     LOG.debug(
573                         'Reading numa location failed for: %s',
574                         interface["virtual-interface"]["vpci"])
575         for iface_key, updated_vnfd in iface_numa.items():
576             self._update_vnfd_helper(updated_vnfd, iface_key)
577
578     def execute_script(self, vat_name, json_out=True, copy_on_execute=False):
579         if copy_on_execute:
580             self.ssh_helper.put_file(vat_name, vat_name)
581             remote_file_path = vat_name
582         else:
583             vat_path = self.ssh_helper.join_bin_path("vpp", "templates")
584             remote_file_path = '{0}/{1}'.format(vat_path, vat_name)
585
586         cmd = "{vat_bin} {json} in {vat_path} script".format(
587             vat_bin=self.VAT_BIN_NAME,
588             json="json" if json_out is True else "",
589             vat_path=remote_file_path)
590
591         try:
592             return self.ssh_helper.execute(cmd=cmd)
593         except Exception:
594             raise RuntimeError("VAT script execution failed: {0}".format(cmd))
595
596     def execute_script_json_out(self, vat_name):
597         vat_path = self.ssh_helper.join_bin_path("vpp", "templates")
598         remote_file_path = '{0}/{1}'.format(vat_path, vat_name)
599
600         _, stdout, _ = self.execute_script(vat_name, json_out=True)
601         return self.cleanup_vat_json_output(stdout, vat_file=remote_file_path)
602
603     @staticmethod
604     def cleanup_vat_json_output(json_output, vat_file=None):
605         retval = json_output
606         clutter = ['vat#', 'dump_interface_table error: Misc',
607                    'dump_interface_table:6019: JSON output supported only ' \
608                    'for VPE API calls and dump_stats_table']
609         if vat_file:
610             clutter.append("{0}(2):".format(vat_file))
611         for garbage in clutter:
612             retval = retval.replace(garbage, '')
613         return retval.strip()
614
615     @staticmethod
616     def _convert_mac_to_number_list(mac_address):
617         list_mac = []
618         for num in mac_address.split(":"):
619             list_mac.append(int(num, 16))
620         return list_mac
621
622     @staticmethod
623     def get_vpp_interface_by_mac(interfaces_list, mac_address):
624         interface_dict = {}
625         list_mac_address = VppSetupEnvHelper._convert_mac_to_number_list(
626             mac_address)
627         LOG.debug("MAC address %s converted to list %s.", mac_address,
628                   list_mac_address)
629         for interface in interfaces_list:
630             # TODO: create vat json integrity checking and move there
631             if "l2_address" not in interface:
632                 raise KeyError(
633                     "key l2_address not found in interface dict."
634                     "Probably input list is not parsed from correct VAT "
635                     "json output.")
636             if "l2_address_length" not in interface:
637                 raise KeyError(
638                     "key l2_address_length not found in interface "
639                     "dict. Probably input list is not parsed from correct "
640                     "VAT json output.")
641             mac_from_json = interface["l2_address"][:6]
642             if mac_from_json == list_mac_address:
643                 if interface["l2_address_length"] != 6:
644                     raise ValueError("l2_address_length value is not 6.")
645                 interface_dict = interface
646                 break
647         return interface_dict
648
649     @staticmethod
650     def get_prefix_length(number1, number2, bits):
651         for i in range(bits):
652             if number1 >> i == number2 >> i:
653                 return bits - i
654         return 0
655
656
657 class VatTerminal(object):
658
659     __VAT_PROMPT = ("vat# ",)
660     __LINUX_PROMPT = (":~# ", ":~$ ", "~]$ ", "~]# ")
661
662
663     def __init__(self, ssh_helper, json_param=True):
664         json_text = ' json' if json_param else ''
665         self.json = json_param
666         self.ssh_helper = ssh_helper
667         EXEC_RETRY = 3
668
669         try:
670             self._tty = self.ssh_helper.interactive_terminal_open()
671         except Exception:
672             raise RuntimeError("Cannot open interactive terminal")
673
674         for _ in range(EXEC_RETRY):
675             try:
676                 self.ssh_helper.interactive_terminal_exec_command(
677                     self._tty,
678                     'sudo -S {0}{1}'.format(VppSetupEnvHelper.VAT_BIN_NAME,
679                                             json_text),
680                     self.__VAT_PROMPT)
681             except exceptions.SSHTimeout:
682                 continue
683             else:
684                 break
685
686         self._exec_failure = False
687         self.vat_stdout = None
688
689     def __enter__(self):
690         return self
691
692     def __exit__(self, exc_type, exc_val, exc_tb):
693         self.vat_terminal_close()
694
695     def vat_terminal_exec_cmd(self, cmd):
696         try:
697             out = self.ssh_helper.interactive_terminal_exec_command(self._tty,
698                                                                     cmd,
699                                                                     self.__VAT_PROMPT)
700             self.vat_stdout = out
701         except exceptions.SSHTimeout:
702             self._exec_failure = True
703             raise RuntimeError(
704                 "VPP is not running on node. VAT command {0} execution failed".
705                     format(cmd))
706         if self.json:
707             obj_start = out.find('{')
708             obj_end = out.rfind('}')
709             array_start = out.find('[')
710             array_end = out.rfind(']')
711
712             if obj_start == -1 and array_start == -1:
713                 raise RuntimeError(
714                     "VAT command {0}: no JSON data.".format(cmd))
715
716             if obj_start < array_start or array_start == -1:
717                 start = obj_start
718                 end = obj_end + 1
719             else:
720                 start = array_start
721                 end = array_end + 1
722             out = out[start:end]
723             json_out = json.loads(out)
724             return json_out
725         else:
726             return None
727
728     def vat_terminal_close(self):
729         if not self._exec_failure:
730             try:
731                 self.ssh_helper.interactive_terminal_exec_command(self._tty,
732                                                                   'quit',
733                                                                   self.__LINUX_PROMPT)
734             except exceptions.SSHTimeout:
735                 raise RuntimeError("Failed to close VAT console")
736         try:
737             self.ssh_helper.interactive_terminal_close(self._tty)
738         except Exception:
739             raise RuntimeError("Cannot close interactive terminal")
740
741     def vat_terminal_exec_cmd_from_template(self, vat_template_file, **args):
742         file_path = os.path.join(constants.YARDSTICK_ROOT_PATH,
743                                  'yardstick/resources/templates/',
744                                  vat_template_file)
745         with open(file_path, 'r') as template_file:
746             cmd_template = template_file.readlines()
747         ret = []
748         for line_tmpl in cmd_template:
749             vat_cmd = line_tmpl.format(**args)
750             ret.append(self.vat_terminal_exec_cmd(vat_cmd.replace('\n', '')))
751         return ret