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