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