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