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