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