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