Add: Dot1Q + L2BD + GBP
[csit.git] / resources / libraries / python / InterfaceUtil.py
1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Interface util library."""
15
16 from socket import AF_INET, AF_INET6, inet_ntop
17 from time import sleep
18
19 from enum import IntEnum
20 from ipaddress import ip_address
21 from robot.api import logger
22
23 from resources.libraries.python.Constants import Constants
24 from resources.libraries.python.CpuUtils import CpuUtils
25 from resources.libraries.python.DUTSetup import DUTSetup
26 from resources.libraries.python.L2Util import L2Util
27 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
28 from resources.libraries.python.parsers.JsonParser import JsonParser
29 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
30 from resources.libraries.python.topology import NodeType, Topology
31 from resources.libraries.python.VPPUtil import VPPUtil
32
33
34 class LinkBondLoadBalance(IntEnum):
35     """Link bonding load balance."""
36     L2 = 0
37     L34 = 1
38     L23 = 2
39
40
41 class LinkBondMode(IntEnum):
42     """Link bonding load balance."""
43     ROUND_ROBIN = 1
44     ACTIVE_BACKUP = 2
45     XOR = 3
46     BROADCAST = 4
47     LACP = 5
48
49
50 class InterfaceUtil(object):
51     """General utilities for managing interfaces"""
52
53     __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
54
55     @staticmethod
56     def pci_to_int(pci_str):
57         """Convert PCI address from string format (0000:18:0a.0) to
58         integer representation (169345024).
59
60         :param pci_str: PCI address in string representation.
61         :type pci_str: str
62         :returns: Integer representation of PCI address.
63         :rtype: int
64         """
65         pci = list(pci_str.split(':')[0:2])
66         pci.extend(pci_str.split(':')[2].split('.'))
67
68         return (int(pci[0], 16) | int(pci[1], 16) << 16 |
69                 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
70
71     @staticmethod
72     def get_interface_index(node, interface):
73         """Get interface sw_if_index from topology file.
74
75         :param node: Node where the interface is.
76         :param interface: Numeric index or name string of a specific interface.
77         :type node: dict
78         :type interface: str or int
79         :returns: SW interface index.
80         :rtype: int
81         """
82         try:
83             sw_if_index = int(interface)
84         except ValueError:
85             sw_if_index = Topology.get_interface_sw_index(node, interface)
86             if sw_if_index is None:
87                 sw_if_index = \
88                     Topology.get_interface_sw_index_by_name(node, interface)
89         except TypeError as err:
90             raise TypeError('Wrong interface format {ifc}: {err}'.format(
91                 ifc=interface, err=err.message))
92
93         return sw_if_index
94
95     @staticmethod
96     def set_interface_state(node, interface, state, if_type='key'):
97         """Set interface state on a node.
98
99         Function can be used for DUTs as well as for TGs.
100
101         :param node: Node where the interface is.
102         :param interface: Interface key or sw_if_index or name.
103         :param state: One of 'up' or 'down'.
104         :param if_type: Interface type
105         :type node: dict
106         :type interface: str or int
107         :type state: str
108         :type if_type: str
109         :returns: Nothing.
110         :raises ValueError: If the interface type is unknown.
111         :raises ValueError: If the state of interface is unexpected.
112         :raises ValueError: If the node has an unknown node type.
113         """
114         if if_type == 'key':
115             if isinstance(interface, basestring):
116                 sw_if_index = Topology.get_interface_sw_index(node, interface)
117                 iface_name = Topology.get_interface_name(node, interface)
118             else:
119                 sw_if_index = interface
120         elif if_type == 'name':
121             iface_key = Topology.get_interface_by_name(node, interface)
122             if iface_key is not None:
123                 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
124             iface_name = interface
125         else:
126             raise ValueError('Unknown if_type: {type}'.format(type=if_type))
127
128         if node['type'] == NodeType.DUT:
129             if state == 'up':
130                 admin_up_down = 1
131             elif state == 'down':
132                 admin_up_down = 0
133             else:
134                 raise ValueError('Unexpected interface state: {state}'.format(
135                     state=state))
136             cmd = 'sw_interface_set_flags'
137             err_msg = 'Failed to set interface state on host {host}'.format(
138                 host=node['host'])
139             args = dict(sw_if_index=sw_if_index,
140                         admin_up_down=admin_up_down)
141             with PapiSocketExecutor(node) as papi_exec:
142                 papi_exec.add(cmd, **args).get_reply(err_msg)
143         elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
144             cmd = 'ip link set {ifc} {state}'.format(
145                 ifc=iface_name, state=state)
146             exec_cmd_no_error(node, cmd, sudo=True)
147         else:
148             raise ValueError('Node {} has unknown NodeType: "{}"'
149                              .format(node['host'], node['type']))
150
151     @staticmethod
152     def set_interface_ethernet_mtu(node, iface_key, mtu):
153         """Set Ethernet MTU for specified interface.
154
155         Function can be used only for TGs.
156
157         :param node: Node where the interface is.
158         :param iface_key: Interface key from topology file.
159         :param mtu: MTU to set.
160         :type node: dict
161         :type iface_key: str
162         :type mtu: int
163         :returns: Nothing.
164         :raises ValueError: If the node type is "DUT".
165         :raises ValueError: If the node has an unknown node type.
166         """
167         if node['type'] == NodeType.DUT:
168             raise ValueError('Node {}: Setting Ethernet MTU for interface '
169                              'on DUT nodes not supported', node['host'])
170         elif node['type'] == NodeType.TG:
171             iface_name = Topology.get_interface_name(node, iface_key)
172             cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
173             exec_cmd_no_error(node, cmd, sudo=True)
174         else:
175             raise ValueError('Node {} has unknown NodeType: "{}"'
176                              .format(node['host'], node['type']))
177
178     @staticmethod
179     def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
180         """Set default Ethernet MTU on all interfaces on node.
181
182         Function can be used only for TGs.
183
184         :param node: Node where to set default MTU.
185         :type node: dict
186         :returns: Nothing.
187         """
188         for ifc in node['interfaces']:
189             InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
190
191     @staticmethod
192     def vpp_set_interface_mtu(node, interface, mtu=9200):
193         """Set Ethernet MTU on interface.
194
195         :param node: VPP node.
196         :param interface: Interface to setup MTU. Default: 9200.
197         :param mtu: Ethernet MTU size in Bytes.
198         :type node: dict
199         :type interface: str or int
200         :type mtu: int
201         """
202         if isinstance(interface, basestring):
203             sw_if_index = Topology.get_interface_sw_index(node, interface)
204         else:
205             sw_if_index = interface
206
207         cmd = 'hw_interface_set_mtu'
208         err_msg = 'Failed to set interface MTU on host {host}'.format(
209             host=node['host'])
210         args = dict(sw_if_index=sw_if_index,
211                     mtu=int(mtu))
212         try:
213             with PapiSocketExecutor(node) as papi_exec:
214                 papi_exec.add(cmd, **args).get_reply(err_msg)
215         except AssertionError as err:
216             # TODO: Make failure tolerance optional.
217             logger.debug("Setting MTU failed. Expected?\n{err}".format(
218                 err=err))
219
220     @staticmethod
221     def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
222         """Set Ethernet MTU on all interfaces.
223
224         :param node: VPP node.
225         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
226         :type node: dict
227         :type mtu: int
228         """
229         for interface in node['interfaces']:
230             InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
231
232     @staticmethod
233     def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
234         """Set Ethernet MTU on all interfaces on all DUTs.
235
236         :param nodes: VPP nodes.
237         :param mtu: Ethernet MTU size in Bytes. Default: 9200.
238         :type nodes: dict
239         :type mtu: int
240         """
241         for node in nodes.values():
242             if node['type'] == NodeType.DUT:
243                 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
244
245     @staticmethod
246     def vpp_node_interfaces_ready_wait(node, retries=15):
247         """Wait until all interfaces with admin-up are in link-up state.
248
249         :param node: Node to wait on.
250         :param retries: Number of retries to check interface status (optional,
251             default 15).
252         :type node: dict
253         :type retries: int
254         :returns: Nothing.
255         :raises RuntimeError: If any interface is not in link-up state after
256             defined number of retries.
257         """
258         for _ in xrange(0, retries):
259             not_ready = list()
260             out = InterfaceUtil.vpp_get_interface_data(node)
261             for interface in out:
262                 if interface.get('admin_up_down') == 1:
263                     if interface.get('link_up_down') != 1:
264                         not_ready.append(interface.get('interface_name'))
265             if not not_ready:
266                 break
267             else:
268                 logger.debug('Interfaces still in link-down state:\n{ifs} '
269                              '\nWaiting...'.format(ifs=not_ready))
270                 sleep(1)
271         else:
272             err = 'Timeout, interfaces not up:\n{ifs}'.format(ifs=not_ready) \
273                 if 'not_ready' in locals() else 'No check executed!'
274             raise RuntimeError(err)
275
276     @staticmethod
277     def all_vpp_interfaces_ready_wait(nodes, retries=15):
278         """Wait until all interfaces with admin-up are in link-up state for all
279         nodes in the topology.
280
281         :param nodes: Nodes in the topology.
282         :param retries: Number of retries to check interface status (optional,
283             default 15).
284         :type nodes: dict
285         :type retries: int
286         :returns: Nothing.
287         """
288         for node in nodes.values():
289             if node['type'] == NodeType.DUT:
290                 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
291
292     @staticmethod
293     def vpp_get_interface_data(node, interface=None):
294         """Get all interface data from a VPP node. If a name or
295         sw_interface_index is provided, return only data for the matching
296         interface(s).
297
298         :param node: VPP node to get interface data from.
299         :param interface: Numeric index or name string of a specific interface.
300         :type node: dict
301         :type interface: int or str
302         :returns: List of dictionaries containing data for each interface, or a
303             single dictionary for the specified interface.
304         :rtype: list or dict
305         :raises TypeError: if the data type of interface is neither basestring
306             nor int.
307         """
308         if interface is not None:
309             if isinstance(interface, basestring):
310                 param = 'interface_name'
311             elif isinstance(interface, int):
312                 param = 'sw_if_index'
313             else:
314                 raise TypeError('Wrong interface format {ifc}'.format(
315                     ifc=interface))
316         else:
317             param = ''
318
319         cmd = 'sw_interface_dump'
320         args = dict(name_filter_valid=0,
321                     name_filter='')
322         err_msg = 'Failed to get interface dump on host {host}'.format(
323             host=node['host'])
324         with PapiSocketExecutor(node) as papi_exec:
325             details = papi_exec.add(cmd, **args).get_details(err_msg)
326
327         def process_if_dump(if_dump):
328             """Process interface dump.
329
330             :param if_dump: Interface dump.
331             :type if_dump: dict
332             :returns: Processed interface dump.
333             :rtype: dict
334             """
335             if_dump['interface_name'] = if_dump['interface_name'].rstrip('\x00')
336             if_dump['tag'] = if_dump['tag'].rstrip('\x00')
337             if_dump['l2_address'] = L2Util.bin_to_mac(if_dump['l2_address'])
338             if_dump['b_dmac'] = L2Util.bin_to_mac(if_dump['b_dmac'])
339             if_dump['b_smac'] = L2Util.bin_to_mac(if_dump['b_smac'])
340             return if_dump
341
342         data = list() if interface is None else dict()
343         for if_dump in details:
344             if interface is None:
345                 data.append(process_if_dump(if_dump))
346             elif str(if_dump.get(param)).rstrip('\x00') == str(interface):
347                 data = process_if_dump(if_dump)
348                 break
349
350         logger.debug('Interface data:\n{if_data}'.format(if_data=data))
351         return data
352
353     @staticmethod
354     def vpp_get_interface_name(node, sw_if_index):
355         """Get interface name for the given SW interface index from actual
356         interface dump.
357
358         :param node: VPP node to get interface data from.
359         :param sw_if_index: SW interface index of the specific interface.
360         :type node: dict
361         :type sw_if_index: int
362         :returns: Name of the given interface.
363         :rtype: str
364         """
365         if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
366         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
367             if_data = InterfaceUtil.vpp_get_interface_data(
368                 node, if_data['sup_sw_if_index'])
369
370         return if_data.get('interface_name')
371
372     @staticmethod
373     def vpp_get_interface_sw_index(node, interface_name):
374         """Get interface name for the given SW interface index from actual
375         interface dump.
376
377         :param node: VPP node to get interface data from.
378         :param interface_name: Interface name.
379         :type node: dict
380         :type interface_name: str
381         :returns: Name of the given interface.
382         :rtype: str
383         """
384         if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
385
386         return if_data.get('sw_if_index')
387
388     @staticmethod
389     def vpp_get_interface_mac(node, interface):
390         """Get MAC address for the given interface from actual interface dump.
391
392         :param node: VPP node to get interface data from.
393         :param interface: Numeric index or name string of a specific interface.
394         :type node: dict
395         :type interface: int or str
396         :returns: MAC address.
397         :rtype: str
398         """
399         if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
400         if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
401             if_data = InterfaceUtil.vpp_get_interface_data(
402                 node, if_data['sup_sw_if_index'])
403
404         return if_data.get('l2_address')
405
406     @staticmethod
407     def tg_set_interface_driver(node, pci_addr, driver):
408         """Set interface driver on the TG node.
409
410         :param node: Node to set interface driver on (must be TG node).
411         :param pci_addr: PCI address of the interface.
412         :param driver: Driver name.
413         :type node: dict
414         :type pci_addr: str
415         :type driver: str
416         :raises RuntimeError: If unbinding from the current driver fails.
417         :raises RuntimeError: If binding to the new driver fails.
418         """
419         old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
420         if old_driver == driver:
421             return
422
423         ssh = SSH()
424         ssh.connect(node)
425
426         # Unbind from current driver
427         if old_driver is not None:
428             cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
429                 .format(pci_addr, old_driver)
430             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
431             if int(ret_code) != 0:
432                 raise RuntimeError("'{0}' failed on '{1}'"
433                                    .format(cmd, node['host']))
434
435         # Bind to the new driver
436         cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
437             .format(pci_addr, driver)
438         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
439         if int(ret_code) != 0:
440             raise RuntimeError("'{0}' failed on '{1}'"
441                                .format(cmd, node['host']))
442
443     @staticmethod
444     def tg_get_interface_driver(node, pci_addr):
445         """Get interface driver from the TG node.
446
447         :param node: Node to get interface driver on (must be TG node).
448         :param pci_addr: PCI address of the interface.
449         :type node: dict
450         :type pci_addr: str
451         :returns: Interface driver or None if not found.
452         :rtype: str
453         :raises RuntimeError: If PCI rescan or lspci command execution failed.
454         """
455         return DUTSetup.get_pci_dev_driver(node, pci_addr)
456
457     @staticmethod
458     def tg_set_interfaces_udev_rules(node):
459         """Set udev rules for interfaces.
460
461         Create udev rules file in /etc/udev/rules.d where are rules for each
462         interface used by TG node, based on MAC interface has specific name.
463         So after unbind and bind again to kernel driver interface has same
464         name as before. This must be called after TG has set name for each
465         port in topology dictionary.
466         udev rule example
467         SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
468         NAME="eth1"
469
470         :param node: Node to set udev rules on (must be TG node).
471         :type node: dict
472         :raises RuntimeError: If setting of udev rules fails.
473         """
474         ssh = SSH()
475         ssh.connect(node)
476
477         cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
478         (ret_code, _, _) = ssh.exec_command_sudo(cmd)
479         if int(ret_code) != 0:
480             raise RuntimeError("'{0}' failed on '{1}'"
481                                .format(cmd, node['host']))
482
483         for interface in node['interfaces'].values():
484             rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
485                    '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
486                    interface['name'] + '\\"'
487             cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
488                 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
489             (ret_code, _, _) = ssh.exec_command_sudo(cmd)
490             if int(ret_code) != 0:
491                 raise RuntimeError("'{0}' failed on '{1}'"
492                                    .format(cmd, node['host']))
493
494         cmd = '/etc/init.d/udev restart'
495         ssh.exec_command_sudo(cmd)
496
497     @staticmethod
498     def tg_set_interfaces_default_driver(node):
499         """Set interfaces default driver specified in topology yaml file.
500
501         :param node: Node to setup interfaces driver on (must be TG node).
502         :type node: dict
503         """
504         for interface in node['interfaces'].values():
505             InterfaceUtil.tg_set_interface_driver(node,
506                                                   interface['pci_address'],
507                                                   interface['driver'])
508
509     @staticmethod
510     def update_vpp_interface_data_on_node(node):
511         """Update vpp generated interface data for a given node in DICT__nodes.
512
513         Updates interface names, software if index numbers and any other details
514         generated specifically by vpp that are unknown before testcase run.
515         It does this by dumping interface list from all devices using python
516         api, and pairing known information from topology (mac address) to state
517         from VPP.
518
519         :param node: Node selected from DICT__nodes.
520         :type node: dict
521         """
522         interface_list = InterfaceUtil.vpp_get_interface_data(node)
523         interface_dict = dict()
524         for ifc in interface_list:
525             interface_dict[ifc['l2_address']] = ifc
526
527         for if_name, if_data in node['interfaces'].items():
528             ifc_dict = interface_dict.get(if_data['mac_address'])
529             if ifc_dict is not None:
530                 if_data['name'] = ifc_dict['interface_name']
531                 if_data['vpp_sw_index'] = ifc_dict['sw_if_index']
532                 if_data['mtu'] = ifc_dict['mtu'][0]
533                 logger.trace('Interface {ifc} found by MAC {mac}'.format(
534                     ifc=if_name, mac=if_data['mac_address']))
535             else:
536                 logger.trace('Interface {ifc} not found by MAC {mac}'.format(
537                     ifc=if_name, mac=if_data['mac_address']))
538                 if_data['vpp_sw_index'] = None
539
540     @staticmethod
541     def update_nic_interface_names(node):
542         """Update interface names based on nic type and PCI address.
543
544         This method updates interface names in the same format as VPP does.
545
546         :param node: Node dictionary.
547         :type node: dict
548         """
549         for ifc in node['interfaces'].values():
550             if_pci = ifc['pci_address'].replace('.', ':').split(':')
551             bus = '{:x}'.format(int(if_pci[1], 16))
552             dev = '{:x}'.format(int(if_pci[2], 16))
553             fun = '{:x}'.format(int(if_pci[3], 16))
554             loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
555             if ifc['model'] == 'Intel-XL710':
556                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
557             elif ifc['model'] == 'Intel-X710':
558                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
559             elif ifc['model'] == 'Intel-X520-DA2':
560                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
561             elif ifc['model'] == 'Cisco-VIC-1385':
562                 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
563             elif ifc['model'] == 'Cisco-VIC-1227':
564                 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
565             else:
566                 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
567
568     @staticmethod
569     def update_nic_interface_names_on_all_duts(nodes):
570         """Update interface names based on nic type and PCI address on all DUTs.
571
572         This method updates interface names in the same format as VPP does.
573
574         :param nodes: Topology nodes.
575         :type nodes: dict
576         """
577         for node in nodes.values():
578             if node['type'] == NodeType.DUT:
579                 InterfaceUtil.update_nic_interface_names(node)
580
581     @staticmethod
582     def update_tg_interface_data_on_node(node, skip_tg_udev=False):
583         """Update interface name for TG/linux node in DICT__nodes.
584
585         .. note::
586             # for dev in `ls /sys/class/net/`;
587             > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
588             "52:54:00:9f:82:63": "eth0"
589             "52:54:00:77:ae:a9": "eth1"
590             "52:54:00:e1:8a:0f": "eth2"
591             "00:00:00:00:00:00": "lo"
592
593         :param node: Node selected from DICT__nodes.
594         :param skip_tg_udev: Skip udev rename on TG node.
595         :type node: dict
596         :type skip_tg_udev: bool
597         :raises RuntimeError: If getting of interface name and MAC fails.
598         """
599         # First setup interface driver specified in yaml file
600         InterfaceUtil.tg_set_interfaces_default_driver(node)
601
602         # Get interface names
603         ssh = SSH()
604         ssh.connect(node)
605
606         cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
607                '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
608
609         (ret_code, stdout, _) = ssh.exec_command(cmd)
610         if int(ret_code) != 0:
611             raise RuntimeError('Get interface name and MAC failed')
612         tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
613         interfaces = JsonParser().parse_data(tmp)
614         for interface in node['interfaces'].values():
615             name = interfaces.get(interface['mac_address'])
616             if name is None:
617                 continue
618             interface['name'] = name
619
620         # Set udev rules for interfaces
621         if not skip_tg_udev:
622             InterfaceUtil.tg_set_interfaces_udev_rules(node)
623
624     @staticmethod
625     def iface_update_numa_node(node):
626         """For all interfaces from topology file update numa node based on
627            information from the node.
628
629         :param node: Node from topology.
630         :type node: dict
631         :returns: Nothing.
632         :raises ValueError: If numa node ia less than 0.
633         :raises RuntimeError: If update of numa node failes.
634         """
635         ssh = SSH()
636         for if_key in Topology.get_node_interfaces(node):
637             if_pci = Topology.get_interface_pci_addr(node, if_key)
638             ssh.connect(node)
639             cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
640             for _ in range(3):
641                 (ret, out, _) = ssh.exec_command(cmd)
642                 if ret == 0:
643                     try:
644                         numa_node = int(out)
645                         if numa_node < 0:
646                             if CpuUtils.cpu_node_count(node) == 1:
647                                 numa_node = 0
648                             else:
649                                 raise ValueError
650                     except ValueError:
651                         logger.trace('Reading numa location failed for: {0}'
652                                      .format(if_pci))
653                     else:
654                         Topology.set_interface_numa_node(node, if_key,
655                                                          numa_node)
656                         break
657             else:
658                 raise RuntimeError('Update numa node failed for: {0}'
659                                    .format(if_pci))
660
661     @staticmethod
662     def update_all_numa_nodes(nodes, skip_tg=False):
663         """For all nodes and all their interfaces from topology file update numa
664         node information based on information from the node.
665
666         :param nodes: Nodes in the topology.
667         :param skip_tg: Skip TG node
668         :type nodes: dict
669         :type skip_tg: bool
670         :returns: Nothing.
671         """
672         for node in nodes.values():
673             if node['type'] == NodeType.DUT:
674                 InterfaceUtil.iface_update_numa_node(node)
675             elif node['type'] == NodeType.TG and not skip_tg:
676                 InterfaceUtil.iface_update_numa_node(node)
677
678     @staticmethod
679     def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
680                                                skip_tg_udev=False,
681                                                numa_node=False):
682         """Update interface names on all nodes in DICT__nodes.
683
684         This method updates the topology dictionary by querying interface lists
685         of all nodes mentioned in the topology dictionary.
686
687         :param nodes: Nodes in the topology.
688         :param skip_tg: Skip TG node.
689         :param skip_tg_udev: Skip udev rename on TG node.
690         :param numa_node: Retrieve numa_node location.
691         :type nodes: dict
692         :type skip_tg: bool
693         :type skip_tg_udev: bool
694         :type numa_node: bool
695         """
696         for node_data in nodes.values():
697             if node_data['type'] == NodeType.DUT:
698                 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
699             elif node_data['type'] == NodeType.TG and not skip_tg:
700                 InterfaceUtil.update_tg_interface_data_on_node(
701                     node_data, skip_tg_udev)
702
703             if numa_node:
704                 if node_data['type'] == NodeType.DUT:
705                     InterfaceUtil.iface_update_numa_node(node_data)
706                 elif node_data['type'] == NodeType.TG and not skip_tg:
707                     InterfaceUtil.iface_update_numa_node(node_data)
708
709     @staticmethod
710     def create_vlan_subinterface(node, interface, vlan):
711         """Create VLAN sub-interface on node.
712
713         :param node: Node to add VLAN subinterface on.
714         :param interface: Interface name or index on which create VLAN
715             subinterface.
716         :param vlan: VLAN ID of the subinterface to be created.
717         :type node: dict
718         :type interface: str on int
719         :type vlan: int
720         :returns: Name and index of created subinterface.
721         :rtype: tuple
722         :raises RuntimeError: if it is unable to create VLAN subinterface on the
723             node or interface cannot be converted.
724         """
725         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
726
727         cmd = 'create_vlan_subif'
728         args = dict(sw_if_index=sw_if_index,
729                     vlan_id=int(vlan))
730         err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
731             host=node['host'])
732         with PapiSocketExecutor(node) as papi_exec:
733             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
734
735         if_key = Topology.add_new_port(node, 'vlan_subif')
736         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
737         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
738         Topology.update_interface_name(node, if_key, ifc_name)
739
740         return '{ifc}.{vlan}'.format(ifc=interface, vlan=vlan), sw_if_index
741
742     @staticmethod
743     def create_vxlan_interface(node, vni, source_ip, destination_ip):
744         """Create VXLAN interface and return sw if index of created interface.
745
746         :param node: Node where to create VXLAN interface.
747         :param vni: VXLAN Network Identifier.
748         :param source_ip: Source IP of a VXLAN Tunnel End Point.
749         :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
750         :type node: dict
751         :type vni: int
752         :type source_ip: str
753         :type destination_ip: str
754         :returns: SW IF INDEX of created interface.
755         :rtype: int
756         :raises RuntimeError: if it is unable to create VxLAN interface on the
757             node.
758         """
759         src_address = ip_address(unicode(source_ip))
760         dst_address = ip_address(unicode(destination_ip))
761
762         cmd = 'vxlan_add_del_tunnel'
763         args = dict(is_add=1,
764                     is_ipv6=1 if src_address.version == 6 else 0,
765                     instance=Constants.BITWISE_NON_ZERO,
766                     src_address=src_address.packed,
767                     dst_address=dst_address.packed,
768                     mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
769                     encap_vrf_id=0,
770                     decap_next_index=Constants.BITWISE_NON_ZERO,
771                     vni=int(vni))
772         err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
773             format(host=node['host'])
774         with PapiSocketExecutor(node) as papi_exec:
775             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
776
777         if_key = Topology.add_new_port(node, 'vxlan_tunnel')
778         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
779         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
780         Topology.update_interface_name(node, if_key, ifc_name)
781
782         return sw_if_index
783
784     @staticmethod
785     def set_vxlan_bypass(node, interface=None):
786         """Add the 'ip4-vxlan-bypass' graph node for a given interface.
787
788         By adding the IPv4 vxlan-bypass graph node to an interface, the node
789         checks for and validate input vxlan packet and bypass ip4-lookup,
790         ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
791         This node will cause extra overhead to for non-vxlan packets which is
792         kept at a minimum.
793
794         :param node: Node where to set VXLAN bypass.
795         :param interface: Numeric index or name string of a specific interface.
796         :type node: dict
797         :type interface: int or str
798         :raises RuntimeError: if it failed to set VXLAN bypass on interface.
799         """
800         sw_if_index = InterfaceUtil.get_interface_index(node, interface)
801
802         cmd = 'sw_interface_set_vxlan_bypass'
803         args = dict(is_ipv6=0,
804                     sw_if_index=sw_if_index,
805                     enable=1)
806         err_msg = 'Failed to set VXLAN bypass on interface on host {host}'.\
807             format(host=node['host'])
808         with PapiSocketExecutor(node) as papi_exec:
809             papi_exec.add(cmd, **args).get_replies(err_msg)
810
811     @staticmethod
812     def vxlan_dump(node, interface=None):
813         """Get VxLAN data for the given interface.
814
815         :param node: VPP node to get interface data from.
816         :param interface: Numeric index or name string of a specific interface.
817             If None, information about all VxLAN interfaces is returned.
818         :type node: dict
819         :type interface: int or str
820         :returns: Dictionary containing data for the given VxLAN interface or if
821             interface=None, the list of dictionaries with all VxLAN interfaces.
822         :rtype: dict or list
823         :raises TypeError: if the data type of interface is neither basestring
824             nor int.
825         """
826         if interface is not None:
827             sw_if_index = InterfaceUtil.get_interface_index(node, interface)
828         else:
829             sw_if_index = int(Constants.BITWISE_NON_ZERO)
830
831         cmd = 'vxlan_tunnel_dump'
832         args = dict(sw_if_index=sw_if_index)
833         err_msg = 'Failed to get VXLAN dump on host {host}'.format(
834             host=node['host'])
835         with PapiSocketExecutor(node) as papi_exec:
836             details = papi_exec.add(cmd, **args).get_details(err_msg)
837
838         def process_vxlan_dump(vxlan_dump):
839             """Process vxlan dump.
840
841             :param vxlan_dump: Vxlan interface dump.
842             :type vxlan_dump: dict
843             :returns: Processed vxlan interface dump.
844             :rtype: dict
845             """
846             if vxlan_dump['is_ipv6']:
847                 vxlan_dump['src_address'] = \
848                     inet_ntop(AF_INET6, vxlan_dump['src_address'])
849                 vxlan_dump['dst_address'] = \
850                     inet_ntop(AF_INET6, vxlan_dump['dst_address'])
851             else:
852                 vxlan_dump['src_address'] = \
853                     inet_ntop(AF_INET, vxlan_dump['src_address'][0:4])
854                 vxlan_dump['dst_address'] = \
855                     inet_ntop(AF_INET, vxlan_dump['dst_address'][0:4])
856             return vxlan_dump
857
858         data = list() if interface is None else dict()
859         for vxlan_dump in details:
860             if interface is None:
861                 data.append(process_vxlan_dump(vxlan_dump))
862             elif vxlan_dump['sw_if_index'] == sw_if_index:
863                 data = process_vxlan_dump(vxlan_dump)
864                 break
865
866         logger.debug('VXLAN data:\n{vxlan_data}'.format(vxlan_data=data))
867         return data
868
869     @staticmethod
870     def vhost_user_dump(node):
871         """Get vhost-user data for the given node.
872
873         TODO: Move to VhostUser.py
874
875         :param node: VPP node to get interface data from.
876         :type node: dict
877         :returns: List of dictionaries with all vhost-user interfaces.
878         :rtype: list
879         """
880         cmd = 'sw_interface_vhost_user_dump'
881         err_msg = 'Failed to get vhost-user dump on host {host}'.format(
882             host=node['host'])
883         with PapiSocketExecutor(node) as papi_exec:
884             details = papi_exec.add(cmd).get_details(err_msg)
885
886         def process_vhost_dump(vhost_dump):
887             """Process vhost dump.
888
889             :param vhost_dump: Vhost interface dump.
890             :type vhost_dump: dict
891             :returns: Processed vhost interface dump.
892             :rtype: dict
893             """
894             vhost_dump['interface_name'] = \
895                 vhost_dump['interface_name'].rstrip('\x00')
896             vhost_dump['sock_filename'] = \
897                 vhost_dump['sock_filename'].rstrip('\x00')
898             return vhost_dump
899
900         for vhost_dump in details:
901             # In-place edits.
902             process_vhost_dump(vhost_dump)
903
904         logger.debug('Vhost-user details:\n{vhost_details}'.format(
905             vhost_details=details))
906         return details
907
908     @staticmethod
909     def tap_dump(node, name=None):
910         """Get all TAP interface data from the given node, or data about
911         a specific TAP interface.
912
913         TODO: Move to Tap.py
914
915         :param node: VPP node to get data from.
916         :param name: Optional name of a specific TAP interface.
917         :type node: dict
918         :type name: str
919         :returns: Dictionary of information about a specific TAP interface, or
920             a List of dictionaries containing all TAP data for the given node.
921         :rtype: dict or list
922         """
923         cmd = 'sw_interface_tap_v2_dump'
924         err_msg = 'Failed to get TAP dump on host {host}'.format(
925             host=node['host'])
926         with PapiSocketExecutor(node) as papi_exec:
927             details = papi_exec.add(cmd).get_details(err_msg)
928
929         def process_tap_dump(tap_dump):
930             """Process tap dump.
931
932             :param tap_dump: Tap interface dump.
933             :type tap_dump: dict
934             :returns: Processed tap interface dump.
935             :rtype: dict
936             """
937             tap_dump['dev_name'] = tap_dump['dev_name'].rstrip('\x00')
938             tap_dump['host_if_name'] = tap_dump['host_if_name'].rstrip('\x00')
939             tap_dump['host_namespace'] = \
940                 tap_dump['host_namespace'].rstrip('\x00')
941             tap_dump['host_mac_addr'] = \
942                 L2Util.bin_to_mac(tap_dump['host_mac_addr'])
943             tap_dump['host_ip4_addr'] = \
944                 inet_ntop(AF_INET, tap_dump['host_ip4_addr'])
945             tap_dump['host_ip6_addr'] = \
946                 inet_ntop(AF_INET6, tap_dump['host_ip6_addr'])
947             return tap_dump
948
949         data = list() if name is None else dict()
950         for tap_dump in details:
951             if name is None:
952                 data.append(process_tap_dump(tap_dump))
953             elif tap_dump.get('dev_name').rstrip('\x00') == name:
954                 data = process_tap_dump(tap_dump)
955                 break
956
957         logger.debug('TAP data:\n{tap_data}'.format(tap_data=data))
958         return data
959
960     @staticmethod
961     def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
962                             inner_vlan_id=None, type_subif=None):
963         """Create sub-interface on node. It is possible to set required
964         sub-interface type and VLAN tag(s).
965
966         :param node: Node to add sub-interface.
967         :param interface: Interface name on which create sub-interface.
968         :param sub_id: ID of the sub-interface to be created.
969         :param outer_vlan_id: Optional outer VLAN ID.
970         :param inner_vlan_id: Optional inner VLAN ID.
971         :param type_subif: Optional type of sub-interface. Values supported by
972             VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
973             [default_sub]
974         :type node: dict
975         :type interface: str or int
976         :type sub_id: int
977         :type outer_vlan_id: int
978         :type inner_vlan_id: int
979         :type type_subif: str
980         :returns: Name and index of created sub-interface.
981         :rtype: tuple
982         :raises RuntimeError: If it is not possible to create sub-interface.
983         """
984         subif_types = type_subif.split()
985
986         cmd = 'create_subif'
987         args = dict(
988             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
989             sub_id=int(sub_id),
990             no_tags=1 if 'no_tags' in subif_types else 0,
991             one_tag=1 if 'one_tag' in subif_types else 0,
992             two_tags=1 if 'two_tags' in subif_types else 0,
993             dot1ad=1 if 'dot1ad' in subif_types else 0,
994             exact_match=1 if 'exact_match' in subif_types else 0,
995             default_sub=1 if 'default_sub' in subif_types else 0,
996             outer_vlan_id_any=1 if type_subif == 'default_sub' else 0,
997             inner_vlan_id_any=1 if type_subif == 'default_sub' else 0,
998             outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
999             inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0)
1000         err_msg = 'Failed to create sub-interface on host {host}'.format(
1001             host=node['host'])
1002         with PapiSocketExecutor(node) as papi_exec:
1003             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1004
1005         if_key = Topology.add_new_port(node, 'subinterface')
1006         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1007         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1008         Topology.update_interface_name(node, if_key, ifc_name)
1009
1010         return '{ifc}.{s_id}'.format(ifc=interface, s_id=sub_id), sw_if_index
1011
1012     @staticmethod
1013     def create_gre_tunnel_interface(node, source_ip, destination_ip):
1014         """Create GRE tunnel interface on node.
1015
1016         :param node: VPP node to add tunnel interface.
1017         :param source_ip: Source of the GRE tunnel.
1018         :param destination_ip: Destination of the GRE tunnel.
1019         :type node: dict
1020         :type source_ip: str
1021         :type destination_ip: str
1022         :returns: Name and index of created GRE tunnel interface.
1023         :rtype: tuple
1024         :raises RuntimeError: If unable to create GRE tunnel interface.
1025         """
1026         cmd = 'gre_tunnel_add_del'
1027         tunnel = dict(type=0,
1028                       instance=Constants.BITWISE_NON_ZERO,
1029                       src=str(source_ip),
1030                       dst=str(destination_ip),
1031                       outer_fib_id=0,
1032                       session_id=0)
1033         args = dict(is_add=1,
1034                     tunnel=tunnel)
1035         err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
1036             host=node['host'])
1037         with PapiSocketExecutor(node) as papi_exec:
1038             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1039
1040         if_key = Topology.add_new_port(node, 'gre_tunnel')
1041         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1042         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1043         Topology.update_interface_name(node, if_key, ifc_name)
1044
1045         return ifc_name, sw_if_index
1046
1047     @staticmethod
1048     def vpp_create_loopback(node, mac=None):
1049         """Create loopback interface on VPP node.
1050
1051         :param node: Node to create loopback interface on.
1052         :param mac: Optional MAC address for Loopback interface.
1053         :type node: dict
1054         :type mac: str
1055         :returns: SW interface index.
1056         :rtype: int
1057         :raises RuntimeError: If it is not possible to create loopback on the
1058             node.
1059         """
1060         cmd = 'create_loopback'
1061         args = dict(mac_address=L2Util.mac_to_bin(mac) if mac else 0)
1062         err_msg = 'Failed to create loopback interface on host {host}'.format(
1063             host=node['host'])
1064         with PapiSocketExecutor(node) as papi_exec:
1065             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1066
1067         if_key = Topology.add_new_port(node, 'loopback')
1068         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1069         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1070         Topology.update_interface_name(node, if_key, ifc_name)
1071         if mac:
1072             mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1073             Topology.update_interface_mac_address(node, if_key, mac)
1074
1075         return sw_if_index
1076
1077     @staticmethod
1078     def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1079         """Create bond interface on VPP node.
1080
1081         :param node: DUT node from topology.
1082         :param mode: Link bonding mode.
1083         :param load_balance: Load balance (optional, valid for xor and lacp
1084             modes, otherwise ignored).
1085         :param mac: MAC address to assign to the bond interface (optional).
1086         :type node: dict
1087         :type mode: str
1088         :type load_balance: str
1089         :type mac: str
1090         :returns: Interface key (name) in topology.
1091         :rtype: str
1092         :raises RuntimeError: If it is not possible to create bond interface on
1093             the node.
1094         """
1095         cmd = 'bond_create'
1096         args = dict(id=int(Constants.BITWISE_NON_ZERO),
1097                     use_custom_mac=0 if mac is None else 1,
1098                     mac_address=0 if mac is None else L2Util.mac_to_bin(mac),
1099                     mode=getattr(LinkBondMode, '{md}'.format(
1100                         md=mode.replace('-', '_').upper())).value,
1101                     lb=0 if load_balance is None else getattr(
1102                         LinkBondLoadBalance, '{lb}'.format(
1103                             lb=load_balance.upper())).value)
1104         err_msg = 'Failed to create bond interface on host {host}'.format(
1105             host=node['host'])
1106         with PapiSocketExecutor(node) as papi_exec:
1107             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1108
1109         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1110                                         ifc_pfx='eth_bond')
1111         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1112
1113         return if_key
1114
1115     @staticmethod
1116     def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1117         """Add ethernet interface to current topology.
1118
1119         :param node: DUT node from topology.
1120         :param ifc_name: Name of the interface.
1121         :param sw_if_index: SW interface index.
1122         :param ifc_pfx: Interface key prefix.
1123         :type node: dict
1124         :type ifc_name: str
1125         :type sw_if_index: int
1126         :type ifc_pfx: str
1127         """
1128         if_key = Topology.add_new_port(node, ifc_pfx)
1129
1130         if ifc_name and sw_if_index is None:
1131             sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1132                 node, ifc_name)
1133         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1134         if sw_if_index and ifc_name is None:
1135             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1136         Topology.update_interface_name(node, if_key, ifc_name)
1137         ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1138         Topology.update_interface_mac_address(node, if_key, ifc_mac)
1139
1140     @staticmethod
1141     def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1142         """Create AVF interface on VPP node.
1143
1144         :param node: DUT node from topology.
1145         :param vf_pci_addr: Virtual Function PCI address.
1146         :param num_rx_queues: Number of RX queues.
1147         :type node: dict
1148         :type vf_pci_addr: str
1149         :type num_rx_queues: int
1150         :returns: Interface key (name) in topology.
1151         :rtype: str
1152         :raises RuntimeError: If it is not possible to create AVF interface on
1153             the node.
1154         """
1155         cmd = 'avf_create'
1156         args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1157                     enable_elog=0,
1158                     rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1159                     rxq_size=0,
1160                     txq_size=0)
1161         err_msg = 'Failed to create AVF interface on host {host}'.format(
1162             host=node['host'])
1163         with PapiSocketExecutor(node) as papi_exec:
1164             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1165
1166         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1167                                         ifc_pfx='eth_avf')
1168         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1169
1170         return if_key
1171
1172     @staticmethod
1173     def vpp_enslave_physical_interface(node, interface, bond_if):
1174         """Enslave physical interface to bond interface on VPP node.
1175
1176         :param node: DUT node from topology.
1177         :param interface: Physical interface key from topology file.
1178         :param bond_if: Load balance
1179         :type node: dict
1180         :type interface: str
1181         :type bond_if: str
1182         :raises RuntimeError: If it is not possible to enslave physical
1183             interface to bond interface on the node.
1184         """
1185         cmd = 'bond_enslave'
1186         args = dict(
1187             sw_if_index=Topology.get_interface_sw_index(node, interface),
1188             bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1189             is_passive=0,
1190             is_long_timeout=0)
1191         err_msg = 'Failed to enslave physical interface {ifc} to bond ' \
1192                   'interface {bond} on host {host}'.format(ifc=interface,
1193                                                            bond=bond_if,
1194                                                            host=node['host'])
1195         with PapiSocketExecutor(node) as papi_exec:
1196             papi_exec.add(cmd, **args).get_reply(err_msg)
1197
1198     @staticmethod
1199     def vpp_show_bond_data_on_node(node, verbose=False):
1200         """Show (detailed) bond information on VPP node.
1201
1202         :param node: DUT node from topology.
1203         :param verbose: If detailed information is required or not.
1204         :type node: dict
1205         :type verbose: bool
1206         """
1207         cmd = 'sw_interface_bond_dump'
1208         err_msg = 'Failed to get bond interface dump on host {host}'.format(
1209             host=node['host'])
1210
1211         data = ('Bond data on node {host}:\n'.format(host=node['host']))
1212         with PapiSocketExecutor(node) as papi_exec:
1213             details = papi_exec.add(cmd).get_details(err_msg)
1214
1215         for bond in details:
1216             data += ('{b}\n'.format(b=bond['interface_name'].rstrip('\x00')))
1217             data += ('  mode: {m}\n'.format(m=LinkBondMode(
1218                 bond['mode']).name.lower()))
1219             data += ('  load balance: {lb}\n'.format(lb=LinkBondLoadBalance(
1220                 bond['lb']).name.lower()))
1221             data += ('  number of active slaves: {n}\n'.format(
1222                 n=bond['active_slaves']))
1223             if verbose:
1224                 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1225                     node, Topology.get_interface_by_sw_index(
1226                         node, bond['sw_if_index']))
1227                 for slave in slave_data:
1228                     if not slave['is_passive']:
1229                         data += ('    {s}\n'.format(s=slave['interface_name']))
1230             data += ('  number of slaves: {n}\n'.format(n=bond['slaves']))
1231             if verbose:
1232                 for slave in slave_data:
1233                     data += ('    {s}\n'.format(s=slave['interface_name']))
1234             data += ('  interface id: {i}\n'.format(i=bond['id']))
1235             data += ('  sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1236         logger.info(data)
1237
1238     @staticmethod
1239     def vpp_bond_slave_dump(node, interface):
1240         """Get bond interface slave(s) data on VPP node.
1241
1242         :param node: DUT node from topology.
1243         :param interface: Physical interface key from topology file.
1244         :type node: dict
1245         :type interface: str
1246         :returns: Bond slave interface data.
1247         :rtype: dict
1248         """
1249         cmd = 'sw_interface_slave_dump'
1250         args = dict(sw_if_index=Topology.get_interface_sw_index(
1251             node, interface))
1252         err_msg = 'Failed to get slave dump on host {host}'.format(
1253             host=node['host'])
1254
1255         with PapiSocketExecutor(node) as papi_exec:
1256             details = papi_exec.add(cmd, **args).get_details(err_msg)
1257
1258         def process_slave_dump(slave_dump):
1259             """Process slave dump.
1260
1261             :param slave_dump: Slave interface dump.
1262             :type slave_dump: dict
1263             :returns: Processed slave interface dump.
1264             :rtype: dict
1265             """
1266             slave_dump['interface_name'] = slave_dump['interface_name'].\
1267                 rstrip('\x00')
1268             return slave_dump
1269
1270         for slave_dump in details:
1271             # In-place edits.
1272             process_slave_dump(slave_dump)
1273
1274         logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1275         return details
1276
1277     @staticmethod
1278     def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1279         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1280
1281         :param nodes: Nodes in the topology.
1282         :param verbose: If detailed information is required or not.
1283         :type nodes: dict
1284         :type verbose: bool
1285         """
1286         for node_data in nodes.values():
1287             if node_data['type'] == NodeType.DUT:
1288                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1289
1290     @staticmethod
1291     def vpp_enable_input_acl_interface(node, interface, ip_version,
1292                                        table_index):
1293         """Enable input acl on interface.
1294
1295         :param node: VPP node to setup interface for input acl.
1296         :param interface: Interface to setup input acl.
1297         :param ip_version: Version of IP protocol.
1298         :param table_index: Classify table index.
1299         :type node: dict
1300         :type interface: str or int
1301         :type ip_version: str
1302         :type table_index: int
1303         """
1304         cmd = 'input_acl_set_interface'
1305         args = dict(
1306             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1307             ip4_table_index=table_index if ip_version == 'ip4'
1308             else Constants.BITWISE_NON_ZERO,
1309             ip6_table_index=table_index if ip_version == 'ip6'
1310             else Constants.BITWISE_NON_ZERO,
1311             l2_table_index=table_index if ip_version == 'l2'
1312             else Constants.BITWISE_NON_ZERO,
1313             is_add=1)
1314         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1315             ifc=interface)
1316         with PapiSocketExecutor(node) as papi_exec:
1317             papi_exec.add(cmd, **args).get_reply(err_msg)
1318
1319     @staticmethod
1320     def get_interface_classify_table(node, interface):
1321         """Get name of classify table for the given interface.
1322
1323         TODO: Move to Classify.py.
1324
1325         :param node: VPP node to get data from.
1326         :param interface: Name or sw_if_index of a specific interface.
1327         :type node: dict
1328         :type interface: str or int
1329         :returns: Classify table name.
1330         :rtype: str
1331         """
1332         if isinstance(interface, basestring):
1333             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1334         else:
1335             sw_if_index = interface
1336
1337         cmd = 'classify_table_by_interface'
1338         args = dict(sw_if_index=sw_if_index)
1339         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1340             ifc=interface)
1341         with PapiSocketExecutor(node) as papi_exec:
1342             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1343
1344         return reply
1345
1346     @staticmethod
1347     def get_sw_if_index(node, interface_name):
1348         """Get sw_if_index for the given interface from actual interface dump.
1349
1350         :param node: VPP node to get interface data from.
1351         :param interface_name: Name of the specific interface.
1352         :type node: dict
1353         :type interface_name: str
1354         :returns: sw_if_index of the given interface.
1355         :rtype: str
1356         """
1357         interface_data = InterfaceUtil.vpp_get_interface_data(
1358             node, interface=interface_name)
1359         return interface_data.get('sw_if_index')
1360
1361     @staticmethod
1362     def vxlan_gpe_dump(node, interface_name=None):
1363         """Get VxLAN GPE data for the given interface.
1364
1365         :param node: VPP node to get interface data from.
1366         :param interface_name: Name of the specific interface. If None,
1367             information about all VxLAN GPE interfaces is returned.
1368         :type node: dict
1369         :type interface_name: str
1370         :returns: Dictionary containing data for the given VxLAN GPE interface
1371             or if interface=None, the list of dictionaries with all VxLAN GPE
1372             interfaces.
1373         :rtype: dict or list
1374         """
1375         if interface_name is not None:
1376             sw_if_index = InterfaceUtil.get_interface_index(
1377                 node, interface_name)
1378         else:
1379             sw_if_index = int(Constants.BITWISE_NON_ZERO)
1380
1381         cmd = 'vxlan_gpe_tunnel_dump'
1382         args = dict(sw_if_index=sw_if_index)
1383         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1384             host=node['host'])
1385         with PapiSocketExecutor(node) as papi_exec:
1386             details = papi_exec.add(cmd, **args).get_details(err_msg)
1387
1388         def process_vxlan_gpe_dump(vxlan_dump):
1389             """Process vxlan_gpe dump.
1390
1391             :param vxlan_dump: Vxlan_gpe nterface dump.
1392             :type vxlan_dump: dict
1393             :returns: Processed vxlan_gpe interface dump.
1394             :rtype: dict
1395             """
1396             if vxlan_dump['is_ipv6']:
1397                 vxlan_dump['local'] = \
1398                     inet_ntop(AF_INET6, vxlan_dump['local'])
1399                 vxlan_dump['remote'] = \
1400                     inet_ntop(AF_INET6, vxlan_dump['remote'])
1401             else:
1402                 vxlan_dump['local'] = \
1403                     inet_ntop(AF_INET, vxlan_dump['local'][0:4])
1404                 vxlan_dump['remote'] = \
1405                     inet_ntop(AF_INET, vxlan_dump['remote'][0:4])
1406             return vxlan_dump
1407
1408         data = list() if interface_name is None else dict()
1409         for vxlan_dump in details:
1410             if interface_name is None:
1411                 data.append(process_vxlan_gpe_dump(vxlan_dump))
1412             elif vxlan_dump['sw_if_index'] == sw_if_index:
1413                 data = process_vxlan_gpe_dump(vxlan_dump)
1414                 break
1415
1416         logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1417             vxlan_gpe_data=data))
1418         return data
1419
1420     @staticmethod
1421     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1422         """Assign VPP interface to specific VRF/FIB table.
1423
1424         :param node: VPP node where the FIB and interface are located.
1425         :param interface: Interface to be assigned to FIB.
1426         :param table_id: VRF table ID.
1427         :param ipv6: Assign to IPv6 table. Default False.
1428         :type node: dict
1429         :type interface: str or int
1430         :type table_id: int
1431         :type ipv6: bool
1432         """
1433         cmd = 'sw_interface_set_table'
1434         args = dict(
1435             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1436             is_ipv6=1 if ipv6 else 0,
1437             vrf_id=int(table_id))
1438         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1439             ifc=interface)
1440         with PapiSocketExecutor(node) as papi_exec:
1441             papi_exec.add(cmd, **args).get_reply(err_msg)
1442
1443     @staticmethod
1444     def set_linux_interface_mac(node, interface, mac, namespace=None,
1445                                 vf_id=None):
1446         """Set MAC address for interface in linux.
1447
1448         :param node: Node where to execute command.
1449         :param interface: Interface in namespace.
1450         :param mac: MAC to be assigned to interface.
1451         :param namespace: Execute command in namespace. Optional
1452         :param vf_id: Virtual Function id. Optional
1453         :type node: dict
1454         :type interface: str
1455         :type mac: str
1456         :type namespace: str
1457         :type vf_id: int
1458         """
1459         mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1460             if vf_id is not None else 'address {mac}'.format(mac=mac)
1461         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1462
1463         cmd = ('{ns} ip link set {interface} {mac}'.
1464                format(ns=ns_str, interface=interface, mac=mac_str))
1465         exec_cmd_no_error(node, cmd, sudo=True)
1466
1467     @staticmethod
1468     def set_linux_interface_trust_on(node, interface, namespace=None,
1469                                      vf_id=None):
1470         """Set trust on (promisc) for interface in linux.
1471
1472         :param node: Node where to execute command.
1473         :param interface: Interface in namespace.
1474         :param namespace: Execute command in namespace. Optional
1475         :param vf_id: Virtual Function id. Optional
1476         :type node: dict
1477         :type interface: str
1478         :type namespace: str
1479         :type vf_id: int
1480         """
1481         trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1482             if vf_id is not None else 'trust on'
1483         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1484
1485         cmd = ('{ns} ip link set dev {interface} {trust}'.
1486                format(ns=ns_str, interface=interface, trust=trust_str))
1487         exec_cmd_no_error(node, cmd, sudo=True)
1488
1489     @staticmethod
1490     def set_linux_interface_spoof_off(node, interface, namespace=None,
1491                                       vf_id=None):
1492         """Set spoof off for interface in linux.
1493
1494         :param node: Node where to execute command.
1495         :param interface: Interface in namespace.
1496         :param namespace: Execute command in namespace. Optional
1497         :param vf_id: Virtual Function id. Optional
1498         :type node: dict
1499         :type interface: str
1500         :type namespace: str
1501         :type vf_id: int
1502         """
1503         spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1504             if vf_id is not None else 'spoof off'
1505         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1506
1507         cmd = ('{ns} ip link set dev {interface} {spoof}'.
1508                format(ns=ns_str, interface=interface, spoof=spoof_str))
1509         exec_cmd_no_error(node, cmd, sudo=True)
1510
1511     @staticmethod
1512     def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1513         """Init PCI device by creating VFs and bind them to vfio-pci for AVF
1514         driver testing on DUT.
1515
1516         :param node: DUT node.
1517         :param ifc_key: Interface key from topology file.
1518         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
1519         :param osi_layer: OSI Layer type to initialize TG with.
1520             Default value "L2" sets linux interface spoof off.
1521         :type node: dict
1522         :type ifc_key: str
1523         :type numvfs: int
1524         :type osi_layer: str
1525         :returns: Virtual Function topology interface keys.
1526         :rtype: list
1527         :raises RuntimeError: If a reason preventing initialization is found.
1528         """
1529         # Read PCI address and driver.
1530         pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1531         pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1532         uio_driver = Topology.get_uio_driver(node)
1533         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1534         if kernel_driver not in ("i40e", "i40evf"):
1535             raise RuntimeError(
1536                 "AVF needs i40e-compatible driver, not {driver} at node {host}"
1537                 " ifc {ifc}".format(
1538                     driver=kernel_driver, host=node["host"], ifc=ifc_key))
1539         current_driver = DUTSetup.get_pci_dev_driver(
1540             node, pf_pci_addr.replace(':', r'\:'))
1541
1542         VPPUtil.stop_vpp_service(node)
1543         if current_driver != kernel_driver:
1544             # PCI device must be re-bound to kernel driver before creating VFs.
1545             DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1546             # Stop VPP to prevent deadlock.
1547             # Unbind from current driver.
1548             DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1549             # Bind to kernel driver.
1550             DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1551
1552         # Initialize PCI VFs.
1553         DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1554
1555         vf_ifc_keys = []
1556         # Set MAC address and bind each virtual function to uio driver.
1557         for vf_id in range(numvfs):
1558             vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1559                                     pf_mac_addr[3], pf_mac_addr[4],
1560                                     pf_mac_addr[5], "{:02x}".format(vf_id)])
1561
1562             pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1563                 format(pci=pf_pci_addr)
1564             InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1565                                                        vf_id=vf_id)
1566             if osi_layer == 'L2':
1567                 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1568                                                             vf_id=vf_id)
1569             InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1570                                                   vf_id=vf_id)
1571
1572             DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1573             DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1574
1575             # Add newly created ports into topology file
1576             vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key)
1577             vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1578             vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1579             Topology.update_interface_name(node, vf_ifc_key,
1580                                            vf_ifc_name+str(vf_id+1))
1581             Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1582             Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1583             vf_ifc_keys.append(vf_ifc_key)
1584
1585         return vf_ifc_keys
1586
1587     @staticmethod
1588     def vpp_sw_interface_rx_placement_dump(node):
1589         """Dump VPP interface RX placement on node.
1590
1591         :param node: Node to run command on.
1592         :type node: dict
1593         :returns: Thread mapping information as a list of dictionaries.
1594         :rtype: list
1595         """
1596         cmd = 'sw_interface_rx_placement_dump'
1597         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1598             cmd=cmd, host=node['host'])
1599         with PapiSocketExecutor(node) as papi_exec:
1600             for ifc in node['interfaces'].values():
1601                 if ifc['vpp_sw_index'] is not None:
1602                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1603             details = papi_exec.get_details(err_msg)
1604         return sorted(details, key=lambda k: k['sw_if_index'])
1605
1606     @staticmethod
1607     def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1608                                           worker_id):
1609         """Set interface RX placement to worker on node.
1610
1611         :param node: Node to run command on.
1612         :param sw_if_index: VPP SW interface index.
1613         :param queue_id: VPP interface queue ID.
1614         :param worker_id: VPP worker ID (indexing from 0).
1615         :type node: dict
1616         :type sw_if_index: int
1617         :type queue_id: int
1618         :type worker_id: int
1619         :raises RuntimeError: If failed to run command on host or if no API
1620             reply received.
1621         """
1622         cmd = 'sw_interface_set_rx_placement'
1623         err_msg = "Failed to set interface RX placement to worker on host " \
1624                   "{host}!".format(host=node['host'])
1625         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
1626                     worker_id=worker_id)
1627         with PapiSocketExecutor(node) as papi_exec:
1628             papi_exec.add(cmd, **args).get_reply(err_msg)
1629
1630     @staticmethod
1631     def vpp_round_robin_rx_placement(node, prefix):
1632         """Set Round Robin interface RX placement on all worker threads
1633         on node.
1634
1635         :param node: Topology nodes.
1636         :param prefix: Interface name prefix.
1637         :type node: dict
1638         :type prefix: str
1639         """
1640         worker_id = 0
1641         worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1642         if not worker_cnt:
1643             return
1644         for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1645             for interface in node['interfaces'].values():
1646                 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1647                     and prefix in interface['name']:
1648                     InterfaceUtil.vpp_sw_interface_set_rx_placement(
1649                         node, placement['sw_if_index'], placement['queue_id'],
1650                         worker_id % worker_cnt)
1651                     worker_id += 1
1652
1653     @staticmethod
1654     def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1655         """Set Round Robin interface RX placement on all worker threads
1656         on all DUTs.
1657
1658         :param nodes: Topology nodes.
1659         :param prefix: Interface name prefix.
1660         :type nodes: dict
1661         :type prefix: str
1662         """
1663         for node in nodes.values():
1664             if node['type'] == NodeType.DUT:
1665                 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)