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