481c122e3f2cefdae76615ef773da8f88d108f47
[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 vpp_create_loopback(node, mac=None):
1066         """Create loopback interface on VPP node.
1067
1068         :param node: Node to create loopback interface on.
1069         :param mac: Optional MAC address for loopback interface.
1070         :type node: dict
1071         :type mac: str
1072         :returns: SW interface index.
1073         :rtype: int
1074         :raises RuntimeError: If it is not possible to create loopback on the
1075             node.
1076         """
1077         cmd = u"create_loopback_instance"
1078         args = dict(
1079             mac_address=L2Util.mac_to_bin(mac) if mac else 0,
1080             is_specified=False,
1081             user_instance=0,
1082         )
1083         err_msg = f"Failed to create loopback interface on host {node[u'host']}"
1084         with PapiSocketExecutor(node) as papi_exec:
1085             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1086
1087         if_key = Topology.add_new_port(node, u"loopback")
1088         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1089         ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1090         Topology.update_interface_name(node, if_key, ifc_name)
1091         if mac:
1092             mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1093             Topology.update_interface_mac_address(node, if_key, mac)
1094
1095         return sw_if_index
1096
1097     @staticmethod
1098     def vpp_create_bond_interface(
1099             node, mode, load_balance=None, mac=None, gso=False):
1100         """Create bond interface on VPP node.
1101
1102         :param node: DUT node from topology.
1103         :param mode: Link bonding mode.
1104         :param load_balance: Load balance (optional, valid for xor and lacp
1105             modes, otherwise ignored). Default: None.
1106         :param mac: MAC address to assign to the bond interface (optional).
1107             Default: None.
1108         :param gso: Enable GSO support (optional). Default: False.
1109         :type node: dict
1110         :type mode: str
1111         :type load_balance: str
1112         :type mac: str
1113         :type gso: bool
1114         :returns: Interface key (name) in topology.
1115         :rtype: str
1116         :raises RuntimeError: If it is not possible to create bond interface on
1117             the node.
1118         """
1119         cmd = u"bond_create2"
1120         args = dict(
1121             id=int(Constants.BITWISE_NON_ZERO),
1122             use_custom_mac=bool(mac is not None),
1123             mac_address=L2Util.mac_to_bin(mac) if mac else None,
1124             mode=getattr(
1125                 LinkBondMode,
1126                 f"BOND_API_MODE_{mode.replace(u'-', u'_').upper()}"
1127             ).value,
1128             lb=0 if load_balance is None else getattr(
1129                 LinkBondLoadBalanceAlgo,
1130                 f"BOND_API_LB_ALGO_{load_balance.upper()}"
1131             ).value,
1132             numa_only=False,
1133             enable_gso=gso
1134         )
1135         err_msg = f"Failed to create bond interface on host {node[u'host']}"
1136         with PapiSocketExecutor(node) as papi_exec:
1137             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1138
1139         InterfaceUtil.add_eth_interface(
1140             node, sw_if_index=sw_if_index, ifc_pfx=u"eth_bond"
1141         )
1142         if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1143
1144         return if_key
1145
1146     @staticmethod
1147     def add_eth_interface(
1148             node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
1149             host_if_key=None):
1150         """Add ethernet interface to current topology.
1151
1152         :param node: DUT node from topology.
1153         :param ifc_name: Name of the interface.
1154         :param sw_if_index: SW interface index.
1155         :param ifc_pfx: Interface key prefix.
1156         :param host_if_key: Host interface key from topology file.
1157         :type node: dict
1158         :type ifc_name: str
1159         :type sw_if_index: int
1160         :type ifc_pfx: str
1161         :type host_if_key: str
1162         """
1163         if_key = Topology.add_new_port(node, ifc_pfx)
1164
1165         if ifc_name and sw_if_index is None:
1166             sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1167                 node, ifc_name)
1168         Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1169         if sw_if_index and ifc_name is None:
1170             ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1171         Topology.update_interface_name(node, if_key, ifc_name)
1172         ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1173         Topology.update_interface_mac_address(node, if_key, ifc_mac)
1174         if host_if_key is not None:
1175             Topology.set_interface_numa_node(
1176                 node, if_key, Topology.get_interface_numa_node(
1177                     node, host_if_key
1178                 )
1179             )
1180             Topology.update_interface_pci_address(
1181                 node, if_key, Topology.get_interface_pci_addr(node, host_if_key)
1182             )
1183
1184     @staticmethod
1185     def vpp_create_avf_interface(
1186             node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0):
1187         """Create AVF interface on VPP node.
1188
1189         :param node: DUT node from topology.
1190         :param if_key: Interface key from topology file of interface
1191             to be bound to i40evf driver.
1192         :param num_rx_queues: Number of RX queues.
1193         :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1194         :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1195         :type node: dict
1196         :type if_key: str
1197         :type num_rx_queues: int
1198         :type rxq_size: int
1199         :type txq_size: int
1200         :returns: AVF interface key (name) in topology.
1201         :rtype: str
1202         :raises RuntimeError: If it is not possible to create AVF interface on
1203             the node.
1204         """
1205         PapiSocketExecutor.run_cli_cmd(
1206             node, u"set logging class avf level debug"
1207         )
1208
1209         cmd = u"avf_create"
1210         vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
1211         args = dict(
1212             pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1213             enable_elog=0,
1214             rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1215             rxq_size=rxq_size,
1216             txq_size=txq_size
1217         )
1218         err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1219         with PapiSocketExecutor(node) as papi_exec:
1220             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1221
1222         InterfaceUtil.add_eth_interface(
1223             node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
1224             host_if_key=if_key
1225         )
1226
1227         return Topology.get_interface_by_sw_index(node, sw_if_index)
1228
1229     @staticmethod
1230     def vpp_create_af_xdp_interface(
1231             node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
1232             mode=u"auto"):
1233         """Create AF_XDP interface on VPP node.
1234
1235         :param node: DUT node from topology.
1236         :param if_key: Physical interface key from topology file of interface
1237             to be bound to compatible driver.
1238         :param num_rx_queues: Number of RX queues. (Optional, Default: none)
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         :param mode: AF_XDP interface mode. (Optional, Default: auto).
1242         :type node: dict
1243         :type if_key: str
1244         :type num_rx_queues: int
1245         :type rxq_size: int
1246         :type txq_size: int
1247         :type mode: str
1248         :returns: Interface key (name) in topology file.
1249         :rtype: str
1250         :raises RuntimeError: If it is not possible to create AF_XDP interface
1251             on the node.
1252         """
1253         PapiSocketExecutor.run_cli_cmd(
1254             node, u"set logging class af_xdp level debug"
1255         )
1256
1257         cmd = u"af_xdp_create"
1258         pci_addr = Topology.get_interface_pci_addr(node, if_key)
1259         args = dict(
1260             name=InterfaceUtil.pci_to_eth(node, pci_addr),
1261             host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1262             rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1263             rxq_size=rxq_size,
1264             txq_size=txq_size,
1265             mode=getattr(AfXdpMode, f"AF_XDP_API_MODE_{mode.upper()}").value
1266         )
1267         err_msg = f"Failed to create AF_XDP interface on host {node[u'host']}"
1268         with PapiSocketExecutor(node) as papi_exec:
1269             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1270
1271         InterfaceUtil.vpp_set_interface_mac(
1272             node, sw_if_index, Topology.get_interface_mac(node, if_key)
1273         )
1274         InterfaceUtil.add_eth_interface(
1275             node, sw_if_index=sw_if_index, ifc_pfx=u"eth_af_xdp",
1276             host_if_key=if_key
1277         )
1278
1279         return Topology.get_interface_by_sw_index(node, sw_if_index)
1280
1281     @staticmethod
1282     def vpp_create_rdma_interface(
1283             node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
1284             mode=u"auto"):
1285         """Create RDMA interface on VPP node.
1286
1287         :param node: DUT node from topology.
1288         :param if_key: Physical interface key from topology file of interface
1289             to be bound to rdma-core driver.
1290         :param num_rx_queues: Number of RX queues.
1291         :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1292         :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1293         :param mode: RDMA interface mode - auto/ibv/dv.
1294         :type node: dict
1295         :type if_key: str
1296         :type num_rx_queues: int
1297         :type rxq_size: int
1298         :type txq_size: int
1299         :type mode: str
1300         :returns: Interface key (name) in topology file.
1301         :rtype: str
1302         :raises RuntimeError: If it is not possible to create RDMA interface on
1303             the node.
1304         """
1305         PapiSocketExecutor.run_cli_cmd(
1306             node, u"set logging class rdma level debug"
1307         )
1308
1309         cmd = u"rdma_create_v2"
1310         pci_addr = Topology.get_interface_pci_addr(node, if_key)
1311         args = dict(
1312             name=InterfaceUtil.pci_to_eth(node, pci_addr),
1313             host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1314             rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1315             rxq_size=rxq_size,
1316             txq_size=txq_size,
1317             mode=getattr(RdmaMode, f"RDMA_API_MODE_{mode.upper()}").value,
1318             # Note: Set True for non-jumbo packets.
1319             no_multi_seg=False,
1320             max_pktlen=0,
1321         )
1322         err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1323         with PapiSocketExecutor(node) as papi_exec:
1324             sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1325
1326         InterfaceUtil.vpp_set_interface_mac(
1327             node, sw_if_index, Topology.get_interface_mac(node, if_key)
1328         )
1329         InterfaceUtil.add_eth_interface(
1330             node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
1331             host_if_key=if_key
1332         )
1333
1334         return Topology.get_interface_by_sw_index(node, sw_if_index)
1335
1336     @staticmethod
1337     def vpp_add_bond_member(node, interface, bond_if):
1338         """Add member interface to bond interface on VPP node.
1339
1340         :param node: DUT node from topology.
1341         :param interface: Physical interface key from topology file.
1342         :param bond_if: Load balance
1343         :type node: dict
1344         :type interface: str
1345         :type bond_if: str
1346         :raises RuntimeError: If it is not possible to add member to bond
1347             interface on the node.
1348         """
1349         cmd = u"bond_add_member"
1350         args = dict(
1351             sw_if_index=Topology.get_interface_sw_index(node, interface),
1352             bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1353             is_passive=False,
1354             is_long_timeout=False
1355         )
1356         err_msg = f"Failed to add member {interface} to bond interface " \
1357             f"{bond_if} on host {node[u'host']}"
1358         with PapiSocketExecutor(node) as papi_exec:
1359             papi_exec.add(cmd, **args).get_reply(err_msg)
1360
1361     @staticmethod
1362     def vpp_show_bond_data_on_node(node, verbose=False):
1363         """Show (detailed) bond information on VPP node.
1364
1365         :param node: DUT node from topology.
1366         :param verbose: If detailed information is required or not.
1367         :type node: dict
1368         :type verbose: bool
1369         """
1370         cmd = u"sw_bond_interface_dump"
1371         err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1372
1373         data = f"Bond data on node {node[u'host']}:\n"
1374         with PapiSocketExecutor(node) as papi_exec:
1375             details = papi_exec.add(cmd).get_details(err_msg)
1376
1377         for bond in details:
1378             data += f"{bond[u'interface_name']}\n"
1379             data += u"  mode: {m}\n".format(
1380                 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1381             )
1382             data += u"  load balance: {lb}\n".format(
1383                 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1384             )
1385             data += f"  number of active members: {bond[u'active_members']}\n"
1386             if verbose:
1387                 member_data = InterfaceUtil.vpp_bond_member_dump(
1388                     node, Topology.get_interface_by_sw_index(
1389                         node, bond[u"sw_if_index"]
1390                     )
1391                 )
1392                 for member in member_data:
1393                     if not member[u"is_passive"]:
1394                         data += f"    {member[u'interface_name']}\n"
1395             data += f"  number of members: {bond[u'members']}\n"
1396             if verbose:
1397                 for member in member_data:
1398                     data += f"    {member[u'interface_name']}\n"
1399             data += f"  interface id: {bond[u'id']}\n"
1400             data += f"  sw_if_index: {bond[u'sw_if_index']}\n"
1401         logger.info(data)
1402
1403     @staticmethod
1404     def vpp_bond_member_dump(node, interface):
1405         """Get bond interface slave(s) data on VPP node.
1406
1407         :param node: DUT node from topology.
1408         :param interface: Physical interface key from topology file.
1409         :type node: dict
1410         :type interface: str
1411         :returns: Bond slave interface data.
1412         :rtype: dict
1413         """
1414         cmd = u"sw_member_interface_dump"
1415         args = dict(
1416             sw_if_index=Topology.get_interface_sw_index(node, interface)
1417         )
1418         err_msg = f"Failed to get slave dump on host {node[u'host']}"
1419
1420         with PapiSocketExecutor(node) as papi_exec:
1421             details = papi_exec.add(cmd, **args).get_details(err_msg)
1422
1423         logger.debug(f"Member data:\n{details}")
1424         return details
1425
1426     @staticmethod
1427     def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1428         """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1429
1430         :param nodes: Nodes in the topology.
1431         :param verbose: If detailed information is required or not.
1432         :type nodes: dict
1433         :type verbose: bool
1434         """
1435         for node_data in nodes.values():
1436             if node_data[u"type"] == NodeType.DUT:
1437                 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1438
1439     @staticmethod
1440     def vpp_enable_input_acl_interface(
1441             node, interface, ip_version, table_index):
1442         """Enable input acl on interface.
1443
1444         :param node: VPP node to setup interface for input acl.
1445         :param interface: Interface to setup input acl.
1446         :param ip_version: Version of IP protocol.
1447         :param table_index: Classify table index.
1448         :type node: dict
1449         :type interface: str or int
1450         :type ip_version: str
1451         :type table_index: int
1452         """
1453         cmd = u"input_acl_set_interface"
1454         args = dict(
1455             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1456             ip4_table_index=table_index if ip_version == u"ip4"
1457             else Constants.BITWISE_NON_ZERO,
1458             ip6_table_index=table_index if ip_version == u"ip6"
1459             else Constants.BITWISE_NON_ZERO,
1460             l2_table_index=table_index if ip_version == u"l2"
1461             else Constants.BITWISE_NON_ZERO,
1462             is_add=1)
1463         err_msg = f"Failed to enable input acl on interface {interface}"
1464         with PapiSocketExecutor(node) as papi_exec:
1465             papi_exec.add(cmd, **args).get_reply(err_msg)
1466
1467     @staticmethod
1468     def get_interface_classify_table(node, interface):
1469         """Get name of classify table for the given interface.
1470
1471         TODO: Move to Classify.py.
1472
1473         :param node: VPP node to get data from.
1474         :param interface: Name or sw_if_index of a specific interface.
1475         :type node: dict
1476         :type interface: str or int
1477         :returns: Classify table name.
1478         :rtype: str
1479         """
1480         if isinstance(interface, str):
1481             sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1482         else:
1483             sw_if_index = interface
1484
1485         cmd = u"classify_table_by_interface"
1486         args = dict(
1487             sw_if_index=sw_if_index
1488         )
1489         err_msg = f"Failed to get classify table name by interface {interface}"
1490         with PapiSocketExecutor(node) as papi_exec:
1491             reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1492
1493         return reply
1494
1495     @staticmethod
1496     def get_sw_if_index(node, interface_name):
1497         """Get sw_if_index for the given interface from actual interface dump.
1498
1499         FIXME: Delete and redirect callers to vpp_get_interface_sw_index.
1500
1501         :param node: VPP node to get interface data from.
1502         :param interface_name: Name of the specific interface.
1503         :type node: dict
1504         :type interface_name: str
1505         :returns: sw_if_index of the given interface.
1506         :rtype: str
1507         """
1508         interface_data = InterfaceUtil.vpp_get_interface_data(
1509             node, interface=interface_name
1510         )
1511         return interface_data.get(u"sw_if_index")
1512
1513     @staticmethod
1514     def vxlan_gpe_dump(node, interface_name=None):
1515         """Get VxLAN GPE data for the given interface.
1516
1517         :param node: VPP node to get interface data from.
1518         :param interface_name: Name of the specific interface. If None,
1519             information about all VxLAN GPE interfaces is returned.
1520         :type node: dict
1521         :type interface_name: str
1522         :returns: Dictionary containing data for the given VxLAN GPE interface
1523             or if interface=None, the list of dictionaries with all VxLAN GPE
1524             interfaces.
1525         :rtype: dict or list
1526         """
1527         def process_vxlan_gpe_dump(vxlan_dump):
1528             """Process vxlan_gpe dump.
1529
1530             :param vxlan_dump: Vxlan_gpe nterface dump.
1531             :type vxlan_dump: dict
1532             :returns: Processed vxlan_gpe interface dump.
1533             :rtype: dict
1534             """
1535             if vxlan_dump[u"is_ipv6"]:
1536                 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1537                 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1538             else:
1539                 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1540                 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1541             return vxlan_dump
1542
1543         if interface_name is not None:
1544             sw_if_index = InterfaceUtil.get_interface_index(
1545                 node, interface_name
1546             )
1547         else:
1548             sw_if_index = int(Constants.BITWISE_NON_ZERO)
1549
1550         cmd = u"vxlan_gpe_tunnel_dump"
1551         args = dict(
1552             sw_if_index=sw_if_index
1553         )
1554         err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1555         with PapiSocketExecutor(node) as papi_exec:
1556             details = papi_exec.add(cmd, **args).get_details(err_msg)
1557
1558         data = list() if interface_name is None else dict()
1559         for dump in details:
1560             if interface_name is None:
1561                 data.append(process_vxlan_gpe_dump(dump))
1562             elif dump[u"sw_if_index"] == sw_if_index:
1563                 data = process_vxlan_gpe_dump(dump)
1564                 break
1565
1566         logger.debug(f"VXLAN-GPE data:\n{data}")
1567         return data
1568
1569     @staticmethod
1570     def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1571         """Assign VPP interface to specific VRF/FIB table.
1572
1573         :param node: VPP node where the FIB and interface are located.
1574         :param interface: Interface to be assigned to FIB.
1575         :param table_id: VRF table ID.
1576         :param ipv6: Assign to IPv6 table. Default False.
1577         :type node: dict
1578         :type interface: str or int
1579         :type table_id: int
1580         :type ipv6: bool
1581         """
1582         cmd = u"sw_interface_set_table"
1583         args = dict(
1584             sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1585             is_ipv6=ipv6,
1586             vrf_id=int(table_id)
1587         )
1588         err_msg = f"Failed to assign interface {interface} to FIB table"
1589         with PapiSocketExecutor(node) as papi_exec:
1590             papi_exec.add(cmd, **args).get_reply(err_msg)
1591
1592     @staticmethod
1593     def set_linux_interface_mac(
1594             node, interface, mac, namespace=None, vf_id=None):
1595         """Set MAC address for interface in linux.
1596
1597         :param node: Node where to execute command.
1598         :param interface: Interface in namespace.
1599         :param mac: MAC to be assigned to interface.
1600         :param namespace: Execute command in namespace. Optional
1601         :param vf_id: Virtual Function id. Optional
1602         :type node: dict
1603         :type interface: str
1604         :type mac: str
1605         :type namespace: str
1606         :type vf_id: int
1607         """
1608         mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1609             else f"address {mac}"
1610         ns_str = f"ip netns exec {namespace}" if namespace else u""
1611
1612         cmd = f"{ns_str} ip link set {interface} {mac_str}"
1613         exec_cmd_no_error(node, cmd, sudo=True)
1614
1615     @staticmethod
1616     def set_linux_interface_promisc(
1617             node, interface, namespace=None, vf_id=None, state=u"on"):
1618         """Set promisc state for interface in linux.
1619
1620         :param node: Node where to execute command.
1621         :param interface: Interface in namespace.
1622         :param namespace: Exec command in namespace. (Optional, Default: None)
1623         :param vf_id: Virtual Function id. (Optional, Default: None)
1624         :param state: State of feature. (Optional, Default: on)
1625         :type node: dict
1626         :type interface: str
1627         :type namespace: str
1628         :type vf_id: int
1629         :type state: str
1630         """
1631         promisc_str = f"vf {vf_id} promisc {state}" if vf_id is not None \
1632             else f"promisc {state}"
1633         ns_str = f"ip netns exec {namespace}" if namespace else u""
1634
1635         cmd = f"{ns_str} ip link set dev {interface} {promisc_str}"
1636         exec_cmd_no_error(node, cmd, sudo=True)
1637
1638     @staticmethod
1639     def set_linux_interface_trust_on(
1640             node, interface, namespace=None, vf_id=None):
1641         """Set trust on (promisc) for interface in linux.
1642
1643         :param node: Node where to execute command.
1644         :param interface: Interface in namespace.
1645         :param namespace: Execute command in namespace. Optional
1646         :param vf_id: Virtual Function id. Optional
1647         :type node: dict
1648         :type interface: str
1649         :type namespace: str
1650         :type vf_id: int
1651         """
1652         trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1653         ns_str = f"ip netns exec {namespace}" if namespace else u""
1654
1655         cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1656         exec_cmd_no_error(node, cmd, sudo=True)
1657
1658     @staticmethod
1659     def set_linux_interface_spoof_off(
1660             node, interface, namespace=None, vf_id=None):
1661         """Set spoof off for interface in linux.
1662
1663         :param node: Node where to execute command.
1664         :param interface: Interface in namespace.
1665         :param namespace: Execute command in namespace. Optional
1666         :param vf_id: Virtual Function id. Optional
1667         :type node: dict
1668         :type interface: str
1669         :type namespace: str
1670         :type vf_id: int
1671         """
1672         spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1673             else u"spoof off"
1674         ns_str = f"ip netns exec {namespace}" if namespace else u""
1675
1676         cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1677         exec_cmd_no_error(node, cmd, sudo=True)
1678
1679     @staticmethod
1680     def set_linux_interface_state(
1681             node, interface, namespace=None, state=u"up"):
1682         """Set operational state for interface in linux.
1683
1684         :param node: Node where to execute command.
1685         :param interface: Interface in namespace.
1686         :param namespace: Execute command in namespace. Optional
1687         :param state: Up/Down.
1688         :type node: dict
1689         :type interface: str
1690         :type namespace: str
1691         :type state: str
1692         """
1693         ns_str = f"ip netns exec {namespace}" if namespace else u""
1694
1695         cmd = f"{ns_str} ip link set dev {interface} {state}"
1696         exec_cmd_no_error(node, cmd, sudo=True)
1697
1698     @staticmethod
1699     def init_interface(node, ifc_key, driver, numvfs=0, osi_layer=u"L2"):
1700         """Init PCI device. Check driver compatibility and bind to proper
1701         drivers. Optionally create NIC VFs.
1702
1703         :param node: DUT node.
1704         :param ifc_key: Interface key from topology file.
1705         :param driver: Base driver to use.
1706         :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1707         :param osi_layer: OSI Layer type to initialize TG with.
1708             Default value "L2" sets linux interface spoof off.
1709         :type node: dict
1710         :type ifc_key: str
1711         :type driver: str
1712         :type numvfs: int
1713         :type osi_layer: str
1714         :returns: Virtual Function topology interface keys.
1715         :rtype: list
1716         :raises RuntimeError: If a reason preventing initialization is found.
1717         """
1718         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1719         vf_keys = []
1720         if driver == u"avf":
1721             if kernel_driver not in (
1722                     u"ice", u"iavf", u"i40e", u"i40evf"):
1723                 raise RuntimeError(
1724                     f"AVF needs ice or i40e compatible driver, not "
1725                     f"{kernel_driver} at node {node[u'host']} ifc {ifc_key}"
1726                 )
1727             vf_keys = InterfaceUtil.init_generic_interface(
1728                 node, ifc_key, numvfs=numvfs, osi_layer=osi_layer
1729             )
1730         elif driver == u"af_xdp":
1731             if kernel_driver not in (
1732                     u"ice", u"iavf", u"i40e", u"i40evf", u"mlx5_core"):
1733                 raise RuntimeError(
1734                     f"AF_XDP needs ice or i40e or rdma compatible driver, not "
1735                     f"{kernel_driver} at node {node[u'host']} ifc {ifc_key}"
1736                 )
1737             vf_keys = InterfaceUtil.init_generic_interface(
1738                 node, ifc_key, numvfs=numvfs, osi_layer=osi_layer
1739             )
1740         return vf_keys
1741
1742     @staticmethod
1743     def init_generic_interface(node, ifc_key, numvfs=0, osi_layer=u"L2"):
1744         """Init PCI device. Bind to proper drivers. Optionally create NIC VFs.
1745
1746         :param node: DUT node.
1747         :param ifc_key: Interface key from topology file.
1748         :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1749         :param osi_layer: OSI Layer type to initialize TG with.
1750             Default value "L2" sets linux interface spoof off.
1751         :type node: dict
1752         :type ifc_key: str
1753         :type numvfs: int
1754         :type osi_layer: str
1755         :returns: Virtual Function topology interface keys.
1756         :rtype: list
1757         :raises RuntimeError: If a reason preventing initialization is found.
1758         """
1759         # Read PCI address and driver.
1760         pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1761         pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1762         uio_driver = Topology.get_uio_driver(node)
1763         kernel_driver = Topology.get_interface_driver(node, ifc_key)
1764         current_driver = DUTSetup.get_pci_dev_driver(
1765             node, pf_pci_addr.replace(u":", r"\:"))
1766         pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1767
1768         VPPUtil.stop_vpp_service(node)
1769         if current_driver != kernel_driver:
1770             # PCI device must be re-bound to kernel driver before creating VFs.
1771             DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1772             # Stop VPP to prevent deadlock.
1773             # Unbind from current driver.
1774             DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1775             # Bind to kernel driver.
1776             DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1777
1778         # Initialize PCI VFs.
1779         DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1780
1781         if not numvfs:
1782             if osi_layer == u"L2":
1783                 InterfaceUtil.set_linux_interface_promisc(node, pf_dev)
1784
1785         vf_ifc_keys = []
1786         # Set MAC address and bind each virtual function to uio driver.
1787         for vf_id in range(numvfs):
1788             vf_mac_addr = u":".join(
1789                 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1790                  pf_mac_addr[5], f"{vf_id:02x}"
1791                  ]
1792             )
1793
1794             InterfaceUtil.set_linux_interface_trust_on(
1795                 node, pf_dev, vf_id=vf_id
1796             )
1797             if osi_layer == u"L2":
1798                 InterfaceUtil.set_linux_interface_spoof_off(
1799                     node, pf_dev, vf_id=vf_id
1800                 )
1801             InterfaceUtil.set_linux_interface_mac(
1802                 node, pf_dev, vf_mac_addr, vf_id=vf_id
1803             )
1804             InterfaceUtil.set_linux_interface_state(
1805                 node, pf_dev, state=u"up"
1806             )
1807
1808             DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1809             DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1810
1811             # Add newly created ports into topology file
1812             vf_ifc_name = f"{ifc_key}_vif"
1813             vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1814             vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1815             Topology.update_interface_name(
1816                 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1817             )
1818             Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1819             Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1820             Topology.set_interface_numa_node(
1821                 node, vf_ifc_key, Topology.get_interface_numa_node(
1822                     node, ifc_key
1823                 )
1824             )
1825             vf_ifc_keys.append(vf_ifc_key)
1826
1827         return vf_ifc_keys
1828
1829     @staticmethod
1830     def vpp_sw_interface_rx_placement_dump(node):
1831         """Dump VPP interface RX placement on node.
1832
1833         :param node: Node to run command on.
1834         :type node: dict
1835         :returns: Thread mapping information as a list of dictionaries.
1836         :rtype: list
1837         """
1838         cmd = u"sw_interface_rx_placement_dump"
1839         err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1840         with PapiSocketExecutor(node) as papi_exec:
1841             for ifc in node[u"interfaces"].values():
1842                 if ifc[u"vpp_sw_index"] is not None:
1843                     papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1844             details = papi_exec.get_details(err_msg)
1845         return sorted(details, key=lambda k: k[u"sw_if_index"])
1846
1847     @staticmethod
1848     def vpp_sw_interface_rx_placement_dump_on_all_duts(nodes):
1849         """Dump VPP interface RX placement on all given nodes.
1850
1851         :param nodes: Nodes to run command on.
1852         :type nodes: dict
1853         :returns: Thread mapping information as a list of dictionaries.
1854         :rtype: list
1855         """
1856         for node in nodes.values():
1857             if node[u"type"] == NodeType.DUT:
1858                 InterfaceUtil.vpp_sw_interface_rx_placement_dump(node)
1859
1860     @staticmethod
1861     def vpp_sw_interface_set_rx_placement(
1862             node, sw_if_index, queue_id, worker_id):
1863         """Set interface RX placement to worker on node.
1864
1865         :param node: Node to run command on.
1866         :param sw_if_index: VPP SW interface index.
1867         :param queue_id: VPP interface queue ID.
1868         :param worker_id: VPP worker ID (indexing from 0).
1869         :type node: dict
1870         :type sw_if_index: int
1871         :type queue_id: int
1872         :type worker_id: int
1873         :raises RuntimeError: If failed to run command on host or if no API
1874             reply received.
1875         """
1876         cmd = u"sw_interface_set_rx_placement"
1877         err_msg = f"Failed to set interface RX placement to worker " \
1878             f"on host {node[u'host']}!"
1879         args = dict(
1880             sw_if_index=sw_if_index,
1881             queue_id=queue_id,
1882             worker_id=worker_id,
1883             is_main=False
1884         )
1885         with PapiSocketExecutor(node) as papi_exec:
1886             papi_exec.add(cmd, **args).get_reply(err_msg)
1887
1888     @staticmethod
1889     def vpp_round_robin_rx_placement(
1890             node, prefix, dp_worker_limit=None):
1891         """Set Round Robin interface RX placement on all worker threads
1892         on node.
1893
1894         If specified, dp_core_limit limits the number of physical cores used
1895         for data plane I/O work. Other cores are presumed to do something else,
1896         e.g. asynchronous crypto processing.
1897         None means all workers are used for data plane work.
1898         Note this keyword specifies workers, not cores.
1899
1900         :param node: Topology nodes.
1901         :param prefix: Interface name prefix.
1902         :param dp_worker_limit: How many cores for data plane work.
1903         :type node: dict
1904         :type prefix: str
1905         :type dp_worker_limit: Optional[int]
1906         """
1907         worker_id = 0
1908         worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1909         if dp_worker_limit is not None:
1910             worker_cnt = min(worker_cnt, dp_worker_limit)
1911         if not worker_cnt:
1912             return
1913         for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1914             for interface in node[u"interfaces"].values():
1915                 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
1916                     and prefix in interface[u"name"]:
1917                     InterfaceUtil.vpp_sw_interface_set_rx_placement(
1918                         node, placement[u"sw_if_index"], placement[u"queue_id"],
1919                         worker_id % worker_cnt
1920                     )
1921                     worker_id += 1
1922
1923     @staticmethod
1924     def vpp_round_robin_rx_placement_on_all_duts(
1925             nodes, prefix, dp_core_limit=None):
1926         """Set Round Robin interface RX placement on all worker threads
1927         on all DUTs.
1928
1929         If specified, dp_core_limit limits the number of physical cores used
1930         for data plane I/O work. Other cores are presumed to do something else,
1931         e.g. asynchronous crypto processing.
1932         None means all cores are used for data plane work.
1933         Note this keyword specifies cores, not workers.
1934
1935         :param nodes: Topology nodes.
1936         :param prefix: Interface name prefix.
1937         :param dp_worker_limit: How many cores for data plane work.
1938         :type nodes: dict
1939         :type prefix: str
1940         :type dp_worker_limit: Optional[int]
1941         """
1942         for node in nodes.values():
1943             if node[u"type"] == NodeType.DUT:
1944                 dp_worker_limit = CpuUtils.worker_count_from_cores_and_smt(
1945                     phy_cores=dp_core_limit,
1946                     smt_used=CpuUtils.is_smt_enabled(node[u"cpuinfo"]),
1947                 )
1948                 InterfaceUtil.vpp_round_robin_rx_placement(
1949                     node, prefix, dp_worker_limit
1950                 )