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