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