898d7fe9ae994f0907846a4525015201bab64bee
[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):
1049         """Create loopback interface on VPP node.
1050
1051         :param node: Node to create loopback interface on.
1052         :type node: dict
1053         :returns: SW interface index.
1054         :rtype: int
1055         :raises RuntimeError: If it is not possible to create loopback on the
1056             node.
1057         """
1058         cmd = 'create_loopback'
1059         args = dict(mac_address=0)
1060         err_msg = 'Failed to create loopback interface on host {host}'.format(
1061             host=node['host'])
1062         with PapiSocketExecutor(node) as papi_exec:
1063             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1064
1065         if_key = Topology.add_new_port(node, 'loopback')
1066         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1067         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1068         Topology.update_interface_name(node, if_key, ifc_name)
1069
1070         return sw_if_index
1071
1072     @staticmethod
1073     def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1074         """Create bond interface on VPP node.
1075
1076         :param node: DUT node from topology.
1077         :param mode: Link bonding mode.
1078         :param load_balance: Load balance (optional, valid for xor and lacp
1079             modes, otherwise ignored).
1080         :param mac: MAC address to assign to the bond interface (optional).
1081         :type node: dict
1082         :type mode: str
1083         :type load_balance: str
1084         :type mac: str
1085         :returns: Interface key (name) in topology.
1086         :rtype: str
1087         :raises RuntimeError: If it is not possible to create bond interface on
1088             the node.
1089         """
1090         cmd = 'bond_create'
1091         args = dict(id=int(Constants.BITWISE_NON_ZERO),
1092                     use_custom_mac=0 if mac is None else 1,
1093                     mac_address=0 if mac is None else L2Util.mac_to_bin(mac),
1094                     mode=getattr(LinkBondMode, '{md}'.format(
1095                         md=mode.replace('-', '_').upper())).value,
1096                     lb=0 if load_balance is None else getattr(
1097                         LinkBondLoadBalance, '{lb}'.format(
1098                             lb=load_balance.upper())).value)
1099         err_msg = 'Failed to create bond interface on host {host}'.format(
1100             host=node['host'])
1101         with PapiSocketExecutor(node) as papi_exec:
1102             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1103
1104         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1105                                         ifc_pfx='eth_bond')
1106         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1107
1108         return if_key
1109
1110     @staticmethod
1111     def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1112         """Add ethernet interface to current topology.
1113
1114         :param node: DUT node from topology.
1115         :param ifc_name: Name of the interface.
1116         :param sw_if_index: SW interface index.
1117         :param ifc_pfx: Interface key prefix.
1118         :type node: dict
1119         :type ifc_name: str
1120         :type sw_if_index: int
1121         :type ifc_pfx: str
1122         """
1123         if_key = Topology.add_new_port(node, ifc_pfx)
1124
1125         if ifc_name and sw_if_index is None:
1126             sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1127                 node, ifc_name)
1128         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1129         if sw_if_index and ifc_name is None:
1130             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1131         Topology.update_interface_name(node, if_key, ifc_name)
1132         ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1133         Topology.update_interface_mac_address(node, if_key, ifc_mac)
1134
1135     @staticmethod
1136     def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1137         """Create AVF interface on VPP node.
1138
1139         :param node: DUT node from topology.
1140         :param vf_pci_addr: Virtual Function PCI address.
1141         :param num_rx_queues: Number of RX queues.
1142         :type node: dict
1143         :type vf_pci_addr: str
1144         :type num_rx_queues: int
1145         :returns: Interface key (name) in topology.
1146         :rtype: str
1147         :raises RuntimeError: If it is not possible to create AVF interface on
1148             the node.
1149         """
1150         cmd = 'avf_create'
1151         args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1152                     enable_elog=0,
1153                     rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1154                     rxq_size=0,
1155                     txq_size=0)
1156         err_msg = 'Failed to create AVF interface on host {host}'.format(
1157             host=node['host'])
1158         with PapiSocketExecutor(node) as papi_exec:
1159             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1160
1161         InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1162                                         ifc_pfx='eth_avf')
1163         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1164
1165         return if_key
1166
1167     @staticmethod
1168     def vpp_enslave_physical_interface(node, interface, bond_if):
1169         """Enslave physical interface to bond interface on VPP node.
1170
1171         :param node: DUT node from topology.
1172         :param interface: Physical interface key from topology file.
1173         :param bond_if: Load balance
1174         :type node: dict
1175         :type interface: str
1176         :type bond_if: str
1177         :raises RuntimeError: If it is not possible to enslave physical
1178             interface to bond interface on the node.
1179         """
1180         cmd = 'bond_enslave'
1181         args = dict(
1182             sw_if_index=Topology.get_interface_sw_index(node, interface),
1183             bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1184             is_passive=0,
1185             is_long_timeout=0)
1186         err_msg = 'Failed to enslave physical interface {ifc} to bond ' \
1187                   'interface {bond} on host {host}'.format(ifc=interface,
1188                                                            bond=bond_if,
1189                                                            host=node['host'])
1190         with PapiSocketExecutor(node) as papi_exec:
1191             papi_exec.add(cmd, **args).get_reply(err_msg)
1192
1193     @staticmethod
1194     def vpp_show_bond_data_on_node(node, verbose=False):
1195         """Show (detailed) bond information on VPP node.
1196
1197         :param node: DUT node from topology.
1198         :param verbose: If detailed information is required or not.
1199         :type node: dict
1200         :type verbose: bool
1201         """
1202         cmd = 'sw_interface_bond_dump'
1203         err_msg = 'Failed to get bond interface dump on host {host}'.format(
1204             host=node['host'])
1205
1206         data = ('Bond data on node {host}:\n'.format(host=node['host']))
1207         with PapiSocketExecutor(node) as papi_exec:
1208             details = papi_exec.add(cmd).get_details(err_msg)
1209
1210         for bond in details:
1211             data += ('{b}\n'.format(b=bond['interface_name'].rstrip('\x00')))
1212             data += ('  mode: {m}\n'.format(m=LinkBondMode(
1213                 bond['mode']).name.lower()))
1214             data += ('  load balance: {lb}\n'.format(lb=LinkBondLoadBalance(
1215                 bond['lb']).name.lower()))
1216             data += ('  number of active slaves: {n}\n'.format(
1217                 n=bond['active_slaves']))
1218             if verbose:
1219                 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1220                     node, Topology.get_interface_by_sw_index(
1221                         node, bond['sw_if_index']))
1222                 for slave in slave_data:
1223                     if not slave['is_passive']:
1224                         data += ('    {s}\n'.format(s=slave['interface_name']))
1225             data += ('  number of slaves: {n}\n'.format(n=bond['slaves']))
1226             if verbose:
1227                 for slave in slave_data:
1228                     data += ('    {s}\n'.format(s=slave['interface_name']))
1229             data += ('  interface id: {i}\n'.format(i=bond['id']))
1230             data += ('  sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1231         logger.info(data)
1232
1233     @staticmethod
1234     def vpp_bond_slave_dump(node, interface):
1235         """Get bond interface slave(s) data on VPP node.
1236
1237         :param node: DUT node from topology.
1238         :param interface: Physical interface key from topology file.
1239         :type node: dict
1240         :type interface: str
1241         :returns: Bond slave interface data.
1242         :rtype: dict
1243         """
1244         cmd = 'sw_interface_slave_dump'
1245         args = dict(sw_if_index=Topology.get_interface_sw_index(
1246             node, interface))
1247         err_msg = 'Failed to get slave dump on host {host}'.format(
1248             host=node['host'])
1249
1250         with PapiSocketExecutor(node) as papi_exec:
1251             details = papi_exec.add(cmd, **args).get_details(err_msg)
1252
1253         def process_slave_dump(slave_dump):
1254             """Process slave dump.
1255
1256             :param slave_dump: Slave interface dump.
1257             :type slave_dump: dict
1258             :returns: Processed slave interface dump.
1259             :rtype: dict
1260             """
1261             slave_dump['interface_name'] = slave_dump['interface_name'].\
1262                 rstrip('\x00')
1263             return slave_dump
1264
1265         for slave_dump in details:
1266             # In-place edits.
1267             process_slave_dump(slave_dump)
1268
1269         logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1270         return details
1271
1272     @staticmethod
1273     def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1274         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1275
1276         :param nodes: Nodes in the topology.
1277         :param verbose: If detailed information is required or not.
1278         :type nodes: dict
1279         :type verbose: bool
1280         """
1281         for node_data in nodes.values():
1282             if node_data['type'] == NodeType.DUT:
1283                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1284
1285     @staticmethod
1286     def vpp_enable_input_acl_interface(node, interface, ip_version,
1287                                        table_index):
1288         """Enable input acl on interface.
1289
1290         :param node: VPP node to setup interface for input acl.
1291         :param interface: Interface to setup input acl.
1292         :param ip_version: Version of IP protocol.
1293         :param table_index: Classify table index.
1294         :type node: dict
1295         :type interface: str or int
1296         :type ip_version: str
1297         :type table_index: int
1298         """
1299         cmd = 'input_acl_set_interface'
1300         args = dict(
1301             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1302             ip4_table_index=table_index if ip_version == 'ip4'
1303             else Constants.BITWISE_NON_ZERO,
1304             ip6_table_index=table_index if ip_version == 'ip6'
1305             else Constants.BITWISE_NON_ZERO,
1306             l2_table_index=table_index if ip_version == 'l2'
1307             else Constants.BITWISE_NON_ZERO,
1308             is_add=1)
1309         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1310             ifc=interface)
1311         with PapiSocketExecutor(node) as papi_exec:
1312             papi_exec.add(cmd, **args).get_reply(err_msg)
1313
1314     @staticmethod
1315     def get_interface_classify_table(node, interface):
1316         """Get name of classify table for the given interface.
1317
1318         TODO: Move to Classify.py.
1319
1320         :param node: VPP node to get data from.
1321         :param interface: Name or sw_if_index of a specific interface.
1322         :type node: dict
1323         :type interface: str or int
1324         :returns: Classify table name.
1325         :rtype: str
1326         """
1327         if isinstance(interface, basestring):
1328             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1329         else:
1330             sw_if_index = interface
1331
1332         cmd = 'classify_table_by_interface'
1333         args = dict(sw_if_index=sw_if_index)
1334         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1335             ifc=interface)
1336         with PapiSocketExecutor(node) as papi_exec:
1337             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1338
1339         return reply
1340
1341     @staticmethod
1342     def get_sw_if_index(node, interface_name):
1343         """Get sw_if_index for the given interface from actual interface dump.
1344
1345         :param node: VPP node to get interface data from.
1346         :param interface_name: Name of the specific interface.
1347         :type node: dict
1348         :type interface_name: str
1349         :returns: sw_if_index of the given interface.
1350         :rtype: str
1351         """
1352         interface_data = InterfaceUtil.vpp_get_interface_data(
1353             node, interface=interface_name)
1354         return interface_data.get('sw_if_index')
1355
1356     @staticmethod
1357     def vxlan_gpe_dump(node, interface_name=None):
1358         """Get VxLAN GPE data for the given interface.
1359
1360         :param node: VPP node to get interface data from.
1361         :param interface_name: Name of the specific interface. If None,
1362             information about all VxLAN GPE interfaces is returned.
1363         :type node: dict
1364         :type interface_name: str
1365         :returns: Dictionary containing data for the given VxLAN GPE interface
1366             or if interface=None, the list of dictionaries with all VxLAN GPE
1367             interfaces.
1368         :rtype: dict or list
1369         """
1370         if interface_name is not None:
1371             sw_if_index = InterfaceUtil.get_interface_index(
1372                 node, interface_name)
1373         else:
1374             sw_if_index = int(Constants.BITWISE_NON_ZERO)
1375
1376         cmd = 'vxlan_gpe_tunnel_dump'
1377         args = dict(sw_if_index=sw_if_index)
1378         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1379             host=node['host'])
1380         with PapiSocketExecutor(node) as papi_exec:
1381             details = papi_exec.add(cmd, **args).get_details(err_msg)
1382
1383         def process_vxlan_gpe_dump(vxlan_dump):
1384             """Process vxlan_gpe dump.
1385
1386             :param vxlan_dump: Vxlan_gpe nterface dump.
1387             :type vxlan_dump: dict
1388             :returns: Processed vxlan_gpe interface dump.
1389             :rtype: dict
1390             """
1391             if vxlan_dump['is_ipv6']:
1392                 vxlan_dump['local'] = \
1393                     inet_ntop(AF_INET6, vxlan_dump['local'])
1394                 vxlan_dump['remote'] = \
1395                     inet_ntop(AF_INET6, vxlan_dump['remote'])
1396             else:
1397                 vxlan_dump['local'] = \
1398                     inet_ntop(AF_INET, vxlan_dump['local'][0:4])
1399                 vxlan_dump['remote'] = \
1400                     inet_ntop(AF_INET, vxlan_dump['remote'][0:4])
1401             return vxlan_dump
1402
1403         data = list() if interface_name is None else dict()
1404         for vxlan_dump in details:
1405             if interface_name is None:
1406                 data.append(process_vxlan_gpe_dump(vxlan_dump))
1407             elif vxlan_dump['sw_if_index'] == sw_if_index:
1408                 data = process_vxlan_gpe_dump(vxlan_dump)
1409                 break
1410
1411         logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1412             vxlan_gpe_data=data))
1413         return data
1414
1415     @staticmethod
1416     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1417         """Assign VPP interface to specific VRF/FIB table.
1418
1419         :param node: VPP node where the FIB and interface are located.
1420         :param interface: Interface to be assigned to FIB.
1421         :param table_id: VRF table ID.
1422         :param ipv6: Assign to IPv6 table. Default False.
1423         :type node: dict
1424         :type interface: str or int
1425         :type table_id: int
1426         :type ipv6: bool
1427         """
1428         cmd = 'sw_interface_set_table'
1429         args = dict(
1430             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1431             is_ipv6=1 if ipv6 else 0,
1432             vrf_id=int(table_id))
1433         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1434             ifc=interface)
1435         with PapiSocketExecutor(node) as papi_exec:
1436             papi_exec.add(cmd, **args).get_reply(err_msg)
1437
1438     @staticmethod
1439     def set_linux_interface_mac(node, interface, mac, namespace=None,
1440                                 vf_id=None):
1441         """Set MAC address for interface in linux.
1442
1443         :param node: Node where to execute command.
1444         :param interface: Interface in namespace.
1445         :param mac: MAC to be assigned to interface.
1446         :param namespace: Execute command in namespace. Optional
1447         :param vf_id: Virtual Function id. Optional
1448         :type node: dict
1449         :type interface: str
1450         :type mac: str
1451         :type namespace: str
1452         :type vf_id: int
1453         """
1454         mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1455             if vf_id is not None else 'address {mac}'.format(mac=mac)
1456         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1457
1458         cmd = ('{ns} ip link set {interface} {mac}'.
1459                format(ns=ns_str, interface=interface, mac=mac_str))
1460         exec_cmd_no_error(node, cmd, sudo=True)
1461
1462     @staticmethod
1463     def set_linux_interface_trust_on(node, interface, namespace=None,
1464                                      vf_id=None):
1465         """Set trust on (promisc) for interface in linux.
1466
1467         :param node: Node where to execute command.
1468         :param interface: Interface in namespace.
1469         :param namespace: Execute command in namespace. Optional
1470         :param vf_id: Virtual Function id. Optional
1471         :type node: dict
1472         :type interface: str
1473         :type namespace: str
1474         :type vf_id: int
1475         """
1476         trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1477             if vf_id is not None else 'trust on'
1478         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1479
1480         cmd = ('{ns} ip link set dev {interface} {trust}'.
1481                format(ns=ns_str, interface=interface, trust=trust_str))
1482         exec_cmd_no_error(node, cmd, sudo=True)
1483
1484     @staticmethod
1485     def set_linux_interface_spoof_off(node, interface, namespace=None,
1486                                       vf_id=None):
1487         """Set spoof off for interface in linux.
1488
1489         :param node: Node where to execute command.
1490         :param interface: Interface in namespace.
1491         :param namespace: Execute command in namespace. Optional
1492         :param vf_id: Virtual Function id. Optional
1493         :type node: dict
1494         :type interface: str
1495         :type namespace: str
1496         :type vf_id: int
1497         """
1498         spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1499             if vf_id is not None else 'spoof off'
1500         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1501
1502         cmd = ('{ns} ip link set dev {interface} {spoof}'.
1503                format(ns=ns_str, interface=interface, spoof=spoof_str))
1504         exec_cmd_no_error(node, cmd, sudo=True)
1505
1506     @staticmethod
1507     def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1508         """Init PCI device by creating VFs and bind them to vfio-pci for AVF
1509         driver testing on DUT.
1510
1511         :param node: DUT node.
1512         :param ifc_key: Interface key from topology file.
1513         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
1514         :param osi_layer: OSI Layer type to initialize TG with.
1515             Default value "L2" sets linux interface spoof off.
1516         :type node: dict
1517         :type ifc_key: str
1518         :type numvfs: int
1519         :type osi_layer: str
1520         :returns: Virtual Function topology interface keys.
1521         :rtype: list
1522         :raises RuntimeError: If a reason preventing initialization is found.
1523         """
1524         # Read PCI address and driver.
1525         pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1526         pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1527         uio_driver = Topology.get_uio_driver(node)
1528         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1529         if kernel_driver not in ("i40e", "i40evf"):
1530             raise RuntimeError(
1531                 "AVF needs i40e-compatible driver, not {driver} at node {host}"
1532                 " ifc {ifc}".format(
1533                     driver=kernel_driver, host=node["host"], ifc=ifc_key))
1534         current_driver = DUTSetup.get_pci_dev_driver(
1535             node, pf_pci_addr.replace(':', r'\:'))
1536
1537         VPPUtil.stop_vpp_service(node)
1538         if current_driver != kernel_driver:
1539             # PCI device must be re-bound to kernel driver before creating VFs.
1540             DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1541             # Stop VPP to prevent deadlock.
1542             # Unbind from current driver.
1543             DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1544             # Bind to kernel driver.
1545             DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1546
1547         # Initialize PCI VFs.
1548         DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1549
1550         vf_ifc_keys = []
1551         # Set MAC address and bind each virtual function to uio driver.
1552         for vf_id in range(numvfs):
1553             vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1554                                     pf_mac_addr[3], pf_mac_addr[4],
1555                                     pf_mac_addr[5], "{:02x}".format(vf_id)])
1556
1557             pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1558                 format(pci=pf_pci_addr)
1559             InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1560                                                        vf_id=vf_id)
1561             if osi_layer == 'L2':
1562                 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1563                                                             vf_id=vf_id)
1564             InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1565                                                   vf_id=vf_id)
1566
1567             DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1568             DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1569
1570             # Add newly created ports into topology file
1571             vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key)
1572             vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1573             vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1574             Topology.update_interface_name(node, vf_ifc_key,
1575                                            vf_ifc_name+str(vf_id+1))
1576             Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1577             Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1578             vf_ifc_keys.append(vf_ifc_key)
1579
1580         return vf_ifc_keys
1581
1582     @staticmethod
1583     def vpp_sw_interface_rx_placement_dump(node):
1584         """Dump VPP interface RX placement on node.
1585
1586         :param node: Node to run command on.
1587         :type node: dict
1588         :returns: Thread mapping information as a list of dictionaries.
1589         :rtype: list
1590         """
1591         cmd = 'sw_interface_rx_placement_dump'
1592         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1593             cmd=cmd, host=node['host'])
1594         with PapiSocketExecutor(node) as papi_exec:
1595             for ifc in node['interfaces'].values():
1596                 if ifc['vpp_sw_index'] is not None:
1597                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1598             details = papi_exec.get_details(err_msg)
1599         return sorted(details, key=lambda k: k['sw_if_index'])
1600
1601     @staticmethod
1602     def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1603                                           worker_id):
1604         """Set interface RX placement to worker on node.
1605
1606         :param node: Node to run command on.
1607         :param sw_if_index: VPP SW interface index.
1608         :param queue_id: VPP interface queue ID.
1609         :param worker_id: VPP worker ID (indexing from 0).
1610         :type node: dict
1611         :type sw_if_index: int
1612         :type queue_id: int
1613         :type worker_id: int
1614         :raises RuntimeError: If failed to run command on host or if no API
1615             reply received.
1616         """
1617         cmd = 'sw_interface_set_rx_placement'
1618         err_msg = "Failed to set interface RX placement to worker on host " \
1619                   "{host}!".format(host=node['host'])
1620         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
1621                     worker_id=worker_id)
1622         with PapiSocketExecutor(node) as papi_exec:
1623             papi_exec.add(cmd, **args).get_reply(err_msg)
1624
1625     @staticmethod
1626     def vpp_round_robin_rx_placement(node, prefix):
1627         """Set Round Robin interface RX placement on all worker threads
1628         on node.
1629
1630         :param node: Topology nodes.
1631         :param prefix: Interface name prefix.
1632         :type node: dict
1633         :type prefix: str
1634         """
1635         worker_id = 0
1636         worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1637         if not worker_cnt:
1638             return
1639         for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1640             for interface in node['interfaces'].values():
1641                 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1642                     and prefix in interface['name']:
1643                     InterfaceUtil.vpp_sw_interface_set_rx_placement(
1644                         node, placement['sw_if_index'], placement['queue_id'],
1645                         worker_id % worker_cnt)
1646                     worker_id += 1
1647
1648     @staticmethod
1649     def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1650         """Set Round Robin interface RX placement on all worker threads
1651         on all DUTs.
1652
1653         :param nodes: Topology nodes.
1654         :param prefix: Interface name prefix.
1655         :type nodes: dict
1656         :type prefix: str
1657         """
1658         for node in nodes.values():
1659             if node['type'] == NodeType.DUT:
1660                 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)