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