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