CSIT-1473: Migrate Tap library from VAT to PAPI
[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
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=LinkBondMode(
1165                 bond['mode']).name.lower()))
1166             data += ('  load balance: {lb}\n'.format(lb=LinkBondLoadBalance(
1167                 bond['lb']).name.lower()))
1168             data += ('  number of active slaves: {n}\n'.format(
1169                 n=bond['active_slaves']))
1170             if verbose:
1171                 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1172                     node, Topology.get_interface_by_sw_index(
1173                         node, bond['sw_if_index']))
1174                 for slave in slave_data:
1175                     if not slave['is_passive']:
1176                         data += ('    {s}\n'.format(s=slave['interface_name']))
1177             data += ('  number of slaves: {n}\n'.format(n=bond['slaves']))
1178             if verbose:
1179                 for slave in slave_data:
1180                     data += ('    {s}\n'.format(s=slave['interface_name']))
1181             data += ('  interface id: {i}\n'.format(i=bond['id']))
1182             data += ('  sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1183         logger.info(data)
1184
1185     @staticmethod
1186     def vpp_bond_slave_dump(node, interface):
1187         """Get bond interface slave(s) data on VPP node.
1188
1189         :param node: DUT node from topology.
1190         :param interface: Physical interface key from topology file.
1191         :type node: dict
1192         :type interface: str
1193         :returns: Bond slave interface data.
1194         :rtype: dict
1195         """
1196         def process_slave_dump(slave_dump):
1197             """Process slave dump.
1198
1199             :param slave_dump: Slave interface dump.
1200             :type slave_dump: dict
1201             :returns: Processed slave interface dump.
1202             :rtype: dict
1203             """
1204             slave_dump['interface_name'] = slave_dump['interface_name'].\
1205                 rstrip('\x00')
1206             return slave_dump
1207
1208         cmd = 'sw_interface_slave_dump'
1209         args = dict(sw_if_index=Topology.get_interface_sw_index(
1210             node, interface))
1211         err_msg = 'Failed to get slave dump on host {host}'.format(
1212             host=node['host'])
1213
1214         with PapiSocketExecutor(node) as papi_exec:
1215             details = papi_exec.add(cmd, **args).get_details(err_msg)
1216
1217         for dump in details:
1218             # In-place edits.
1219             process_slave_dump(dump)
1220
1221         logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1222         return details
1223
1224     @staticmethod
1225     def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1226         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1227
1228         :param nodes: Nodes in the topology.
1229         :param verbose: If detailed information is required or not.
1230         :type nodes: dict
1231         :type verbose: bool
1232         """
1233         for node_data in nodes.values():
1234             if node_data['type'] == NodeType.DUT:
1235                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1236
1237     @staticmethod
1238     def vpp_enable_input_acl_interface(node, interface, ip_version,
1239                                        table_index):
1240         """Enable input acl on interface.
1241
1242         :param node: VPP node to setup interface for input acl.
1243         :param interface: Interface to setup input acl.
1244         :param ip_version: Version of IP protocol.
1245         :param table_index: Classify table index.
1246         :type node: dict
1247         :type interface: str or int
1248         :type ip_version: str
1249         :type table_index: int
1250         """
1251         cmd = 'input_acl_set_interface'
1252         args = dict(
1253             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1254             ip4_table_index=table_index if ip_version == 'ip4'
1255             else Constants.BITWISE_NON_ZERO,
1256             ip6_table_index=table_index if ip_version == 'ip6'
1257             else Constants.BITWISE_NON_ZERO,
1258             l2_table_index=table_index if ip_version == 'l2'
1259             else Constants.BITWISE_NON_ZERO,
1260             is_add=1)
1261         err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1262             ifc=interface)
1263         with PapiSocketExecutor(node) as papi_exec:
1264             papi_exec.add(cmd, **args).get_reply(err_msg)
1265
1266     @staticmethod
1267     def get_interface_classify_table(node, interface):
1268         """Get name of classify table for the given interface.
1269
1270         TODO: Move to Classify.py.
1271
1272         :param node: VPP node to get data from.
1273         :param interface: Name or sw_if_index of a specific interface.
1274         :type node: dict
1275         :type interface: str or int
1276         :returns: Classify table name.
1277         :rtype: str
1278         """
1279         if isinstance(interface, basestring):
1280             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1281         else:
1282             sw_if_index = interface
1283
1284         cmd = 'classify_table_by_interface'
1285         args = dict(sw_if_index=sw_if_index)
1286         err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1287             ifc=interface)
1288         with PapiSocketExecutor(node) as papi_exec:
1289             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1290
1291         return reply
1292
1293     @staticmethod
1294     def get_sw_if_index(node, interface_name):
1295         """Get sw_if_index for the given interface from actual interface dump.
1296
1297         :param node: VPP node to get interface data from.
1298         :param interface_name: Name of the specific interface.
1299         :type node: dict
1300         :type interface_name: str
1301         :returns: sw_if_index of the given interface.
1302         :rtype: str
1303         """
1304         interface_data = InterfaceUtil.vpp_get_interface_data(
1305             node, interface=interface_name)
1306         return interface_data.get('sw_if_index')
1307
1308     @staticmethod
1309     def vxlan_gpe_dump(node, interface_name=None):
1310         """Get VxLAN GPE data for the given interface.
1311
1312         :param node: VPP node to get interface data from.
1313         :param interface_name: Name of the specific interface. If None,
1314             information about all VxLAN GPE interfaces is returned.
1315         :type node: dict
1316         :type interface_name: str
1317         :returns: Dictionary containing data for the given VxLAN GPE interface
1318             or if interface=None, the list of dictionaries with all VxLAN GPE
1319             interfaces.
1320         :rtype: dict or list
1321         """
1322         def process_vxlan_gpe_dump(vxlan_dump):
1323             """Process vxlan_gpe dump.
1324
1325             :param vxlan_dump: Vxlan_gpe nterface dump.
1326             :type vxlan_dump: dict
1327             :returns: Processed vxlan_gpe interface dump.
1328             :rtype: dict
1329             """
1330             if vxlan_dump['is_ipv6']:
1331                 vxlan_dump['local'] = \
1332                     ip_address(unicode(vxlan_dump['local']))
1333                 vxlan_dump['remote'] = \
1334                     ip_address(unicode(vxlan_dump['remote']))
1335             else:
1336                 vxlan_dump['local'] = \
1337                     ip_address(unicode(vxlan_dump['local'][0:4]))
1338                 vxlan_dump['remote'] = \
1339                     ip_address(unicode(vxlan_dump['remote'][0:4]))
1340             return vxlan_dump
1341
1342         if interface_name is not None:
1343             sw_if_index = InterfaceUtil.get_interface_index(
1344                 node, interface_name)
1345         else:
1346             sw_if_index = int(Constants.BITWISE_NON_ZERO)
1347
1348         cmd = 'vxlan_gpe_tunnel_dump'
1349         args = dict(sw_if_index=sw_if_index)
1350         err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1351             host=node['host'])
1352         with PapiSocketExecutor(node) as papi_exec:
1353             details = papi_exec.add(cmd, **args).get_details(err_msg)
1354
1355         data = list() if interface_name is None else dict()
1356         for dump in details:
1357             if interface_name is None:
1358                 data.append(process_vxlan_gpe_dump(dump))
1359             elif dump['sw_if_index'] == sw_if_index:
1360                 data = process_vxlan_gpe_dump(dump)
1361                 break
1362
1363         logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1364             vxlan_gpe_data=data))
1365         return data
1366
1367     @staticmethod
1368     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1369         """Assign VPP interface to specific VRF/FIB table.
1370
1371         :param node: VPP node where the FIB and interface are located.
1372         :param interface: Interface to be assigned to FIB.
1373         :param table_id: VRF table ID.
1374         :param ipv6: Assign to IPv6 table. Default False.
1375         :type node: dict
1376         :type interface: str or int
1377         :type table_id: int
1378         :type ipv6: bool
1379         """
1380         cmd = 'sw_interface_set_table'
1381         args = dict(
1382             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1383             is_ipv6=1 if ipv6 else 0,
1384             vrf_id=int(table_id))
1385         err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1386             ifc=interface)
1387         with PapiSocketExecutor(node) as papi_exec:
1388             papi_exec.add(cmd, **args).get_reply(err_msg)
1389
1390     @staticmethod
1391     def set_linux_interface_mac(node, interface, mac, namespace=None,
1392                                 vf_id=None):
1393         """Set MAC address for interface in linux.
1394
1395         :param node: Node where to execute command.
1396         :param interface: Interface in namespace.
1397         :param mac: MAC to be assigned to interface.
1398         :param namespace: Execute command in namespace. Optional
1399         :param vf_id: Virtual Function id. Optional
1400         :type node: dict
1401         :type interface: str
1402         :type mac: str
1403         :type namespace: str
1404         :type vf_id: int
1405         """
1406         mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1407             if vf_id is not None else 'address {mac}'.format(mac=mac)
1408         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1409
1410         cmd = ('{ns} ip link set {interface} {mac}'.
1411                format(ns=ns_str, interface=interface, mac=mac_str))
1412         exec_cmd_no_error(node, cmd, sudo=True)
1413
1414     @staticmethod
1415     def set_linux_interface_trust_on(node, interface, namespace=None,
1416                                      vf_id=None):
1417         """Set trust on (promisc) for interface in linux.
1418
1419         :param node: Node where to execute command.
1420         :param interface: Interface in namespace.
1421         :param namespace: Execute command in namespace. Optional
1422         :param vf_id: Virtual Function id. Optional
1423         :type node: dict
1424         :type interface: str
1425         :type namespace: str
1426         :type vf_id: int
1427         """
1428         trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1429             if vf_id is not None else 'trust on'
1430         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1431
1432         cmd = ('{ns} ip link set dev {interface} {trust}'.
1433                format(ns=ns_str, interface=interface, trust=trust_str))
1434         exec_cmd_no_error(node, cmd, sudo=True)
1435
1436     @staticmethod
1437     def set_linux_interface_spoof_off(node, interface, namespace=None,
1438                                       vf_id=None):
1439         """Set spoof off for interface in linux.
1440
1441         :param node: Node where to execute command.
1442         :param interface: Interface in namespace.
1443         :param namespace: Execute command in namespace. Optional
1444         :param vf_id: Virtual Function id. Optional
1445         :type node: dict
1446         :type interface: str
1447         :type namespace: str
1448         :type vf_id: int
1449         """
1450         spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1451             if vf_id is not None else 'spoof off'
1452         ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1453
1454         cmd = ('{ns} ip link set dev {interface} {spoof}'.
1455                format(ns=ns_str, interface=interface, spoof=spoof_str))
1456         exec_cmd_no_error(node, cmd, sudo=True)
1457
1458     @staticmethod
1459     def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1460         """Init PCI device by creating VFs and bind them to vfio-pci for AVF
1461         driver testing on DUT.
1462
1463         :param node: DUT node.
1464         :param ifc_key: Interface key from topology file.
1465         :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
1466         :param osi_layer: OSI Layer type to initialize TG with.
1467             Default value "L2" sets linux interface spoof off.
1468         :type node: dict
1469         :type ifc_key: str
1470         :type numvfs: int
1471         :type osi_layer: str
1472         :returns: Virtual Function topology interface keys.
1473         :rtype: list
1474         :raises RuntimeError: If a reason preventing initialization is found.
1475         """
1476         # Read PCI address and driver.
1477         pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1478         pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1479         uio_driver = Topology.get_uio_driver(node)
1480         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1481         if kernel_driver not in ("i40e", "i40evf"):
1482             raise RuntimeError(
1483                 "AVF needs i40e-compatible driver, not {driver} at node {host}"
1484                 " ifc {ifc}".format(
1485                     driver=kernel_driver, host=node["host"], ifc=ifc_key))
1486         current_driver = DUTSetup.get_pci_dev_driver(
1487             node, pf_pci_addr.replace(':', r'\:'))
1488
1489         VPPUtil.stop_vpp_service(node)
1490         if current_driver != kernel_driver:
1491             # PCI device must be re-bound to kernel driver before creating VFs.
1492             DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1493             # Stop VPP to prevent deadlock.
1494             # Unbind from current driver.
1495             DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1496             # Bind to kernel driver.
1497             DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1498
1499         # Initialize PCI VFs.
1500         DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1501
1502         vf_ifc_keys = []
1503         # Set MAC address and bind each virtual function to uio driver.
1504         for vf_id in range(numvfs):
1505             vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1506                                     pf_mac_addr[3], pf_mac_addr[4],
1507                                     pf_mac_addr[5], "{:02x}".format(vf_id)])
1508
1509             pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1510                 format(pci=pf_pci_addr)
1511             InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1512                                                        vf_id=vf_id)
1513             if osi_layer == 'L2':
1514                 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1515                                                             vf_id=vf_id)
1516             InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1517                                                   vf_id=vf_id)
1518
1519             DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1520             DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1521
1522             # Add newly created ports into topology file
1523             vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key)
1524             vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1525             vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1526             Topology.update_interface_name(node, vf_ifc_key,
1527                                            vf_ifc_name+str(vf_id+1))
1528             Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1529             Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1530             vf_ifc_keys.append(vf_ifc_key)
1531
1532         return vf_ifc_keys
1533
1534     @staticmethod
1535     def vpp_sw_interface_rx_placement_dump(node):
1536         """Dump VPP interface RX placement on node.
1537
1538         :param node: Node to run command on.
1539         :type node: dict
1540         :returns: Thread mapping information as a list of dictionaries.
1541         :rtype: list
1542         """
1543         cmd = 'sw_interface_rx_placement_dump'
1544         err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1545             cmd=cmd, host=node['host'])
1546         with PapiSocketExecutor(node) as papi_exec:
1547             for ifc in node['interfaces'].values():
1548                 if ifc['vpp_sw_index'] is not None:
1549                     papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1550             details = papi_exec.get_details(err_msg)
1551         return sorted(details, key=lambda k: k['sw_if_index'])
1552
1553     @staticmethod
1554     def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1555                                           worker_id):
1556         """Set interface RX placement to worker on node.
1557
1558         :param node: Node to run command on.
1559         :param sw_if_index: VPP SW interface index.
1560         :param queue_id: VPP interface queue ID.
1561         :param worker_id: VPP worker ID (indexing from 0).
1562         :type node: dict
1563         :type sw_if_index: int
1564         :type queue_id: int
1565         :type worker_id: int
1566         :raises RuntimeError: If failed to run command on host or if no API
1567             reply received.
1568         """
1569         cmd = 'sw_interface_set_rx_placement'
1570         err_msg = "Failed to set interface RX placement to worker on host " \
1571                   "{host}!".format(host=node['host'])
1572         args = dict(sw_if_index=sw_if_index, queue_id=queue_id,
1573                     worker_id=worker_id)
1574         with PapiSocketExecutor(node) as papi_exec:
1575             papi_exec.add(cmd, **args).get_reply(err_msg)
1576
1577     @staticmethod
1578     def vpp_round_robin_rx_placement(node, prefix):
1579         """Set Round Robin interface RX placement on all worker threads
1580         on node.
1581
1582         :param node: Topology nodes.
1583         :param prefix: Interface name prefix.
1584         :type node: dict
1585         :type prefix: str
1586         """
1587         worker_id = 0
1588         worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1589         if not worker_cnt:
1590             return
1591         for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1592             for interface in node['interfaces'].values():
1593                 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1594                     and prefix in interface['name']:
1595                     InterfaceUtil.vpp_sw_interface_set_rx_placement(
1596                         node, placement['sw_if_index'], placement['queue_id'],
1597                         worker_id % worker_cnt)
1598                     worker_id += 1
1599
1600     @staticmethod
1601     def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1602         """Set Round Robin interface RX placement on all worker threads
1603         on all DUTs.
1604
1605         :param nodes: Topology nodes.
1606         :param prefix: Interface name prefix.
1607         :type nodes: dict
1608         :type prefix: str
1609         """
1610         for node in nodes.values():
1611             if node['type'] == NodeType.DUT:
1612                 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)