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