1 # Copyright (c) 2023 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Interface util library."""
16 from time import sleep
17 from enum import IntEnum
19 from ipaddress import ip_address
20 from robot.api import logger
22 from resources.libraries.python.Constants import Constants
23 from resources.libraries.python.DUTSetup import DUTSetup
24 from resources.libraries.python.IPAddress import IPAddress
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, exec_cmd_no_error
29 from resources.libraries.python.topology import NodeType, Topology
30 from resources.libraries.python.VPPUtil import VPPUtil
33 class InterfaceStatusFlags(IntEnum):
34 """Interface status flags."""
35 IF_STATUS_API_FLAG_ADMIN_UP = 1
36 IF_STATUS_API_FLAG_LINK_UP = 2
39 class MtuProto(IntEnum):
44 MTU_PROTO_API_MPLS = 3
48 class LinkDuplex(IntEnum):
50 LINK_DUPLEX_API_UNKNOWN = 0
51 LINK_DUPLEX_API_HALF = 1
52 LINK_DUPLEX_API_FULL = 2
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
68 class RxMode(IntEnum):
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
77 class IfType(IntEnum):
80 IF_API_TYPE_HARDWARE = 0
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
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
106 class RdmaMode(IntEnum):
107 """RDMA interface mode."""
108 RDMA_API_MODE_AUTO = 0
109 RDMA_API_MODE_IBV = 1
113 class AfXdpMode(IntEnum):
114 """AF_XDP interface mode."""
115 AF_XDP_API_MODE_AUTO = 0
116 AF_XDP_API_MODE_COPY = 1
117 AF_XDP_API_MODE_ZERO_COPY = 2
121 """General utilities for managing interfaces"""
124 def pci_to_int(pci_str):
125 """Convert PCI address from string format (0000:18:0a.0) to
126 integer representation (169345024).
128 :param pci_str: PCI address in string representation.
130 :returns: Integer representation of PCI address.
133 pci = list(pci_str.split(u":")[0:2])
134 pci.extend(pci_str.split(u":")[2].split(u"."))
136 return (int(pci[0], 16) | int(pci[1], 16) << 16 |
137 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
140 def pci_to_eth(node, pci_str):
141 """Convert PCI address on DUT to Linux ethernet name.
143 :param node: DUT node
144 :param pci_str: PCI address.
147 :returns: Ethernet name.
150 cmd = f"basename /sys/bus/pci/devices/{pci_str}/net/*"
152 stdout, _ = exec_cmd_no_error(node, cmd)
154 raise RuntimeError(f"Cannot convert {pci_str} to ethernet name!")
156 return stdout.strip()
159 def get_interface_index(node, interface):
160 """Get interface sw_if_index from topology file.
162 :param node: Node where the interface is.
163 :param interface: Numeric index or name string of a specific interface.
165 :type interface: str or int
166 :returns: SW interface index.
170 sw_if_index = int(interface)
172 sw_if_index = Topology.get_interface_sw_index(node, interface)
173 if sw_if_index is None:
175 Topology.get_interface_sw_index_by_name(node, interface)
176 except TypeError as err:
177 raise TypeError(f"Wrong interface format {interface}") from err
182 def set_interface_state(node, interface, state, if_type=u"key"):
183 """Set interface state on a node.
185 Function can be used for DUTs as well as for TGs.
187 :param node: Node where the interface is.
188 :param interface: Interface key or sw_if_index or name.
189 :param state: One of 'up' or 'down'.
190 :param if_type: Interface type
192 :type interface: str or int
196 :raises ValueError: If the interface type is unknown.
197 :raises ValueError: If the state of interface is unexpected.
198 :raises ValueError: If the node has an unknown node type.
200 if if_type == u"key":
201 if isinstance(interface, str):
202 sw_if_index = Topology.get_interface_sw_index(node, interface)
203 iface_name = Topology.get_interface_name(node, interface)
205 sw_if_index = interface
206 elif if_type == u"name":
207 iface_key = Topology.get_interface_by_name(node, interface)
208 if iface_key is not None:
209 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
210 iface_name = interface
212 raise ValueError(f"Unknown if_type: {if_type}")
214 if node[u"type"] == NodeType.DUT:
215 if sw_if_index is None:
217 f"Interface index for {interface} not assigned by VPP."
220 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
221 elif state == u"down":
224 raise ValueError(f"Unexpected interface state: {state}")
225 cmd = u"sw_interface_set_flags"
226 err_msg = f"Failed to set interface state on host {node[u'host']}"
228 sw_if_index=int(sw_if_index),
231 with PapiSocketExecutor(node) as papi_exec:
232 papi_exec.add(cmd, **args).get_reply(err_msg)
233 elif node[u"type"] == NodeType.TG or node[u"type"] == NodeType.VM:
234 cmd = f"ip link set {iface_name} {state}"
235 exec_cmd_no_error(node, cmd, sudo=True)
238 f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
242 def set_interface_state_pci(
243 node, pf_pcis, namespace=None, state=u"up"):
244 """Set operational state for interface specified by PCI address.
246 :param node: Topology node.
247 :param pf_pcis: List of node's interfaces PCI addresses.
248 :param namespace: Exec command in namespace. (Optional, Default: none)
249 :param state: Up/Down. (Optional, default: up)
255 for pf_pci in pf_pcis:
256 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
257 InterfaceUtil.set_linux_interface_state(
258 node, pf_eth, namespace=namespace, state=state
262 def set_interface_mtu(node, pf_pcis, mtu=9200):
263 """Set Ethernet MTU for specified interfaces.
265 :param node: Topology node.
266 :param pf_pcis: List of node's interfaces PCI addresses.
267 :param mtu: MTU to set. Default: 9200.
271 :raises RuntimeError: If failed to set MTU on interface.
273 for pf_pci in pf_pcis:
274 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
275 cmd = f"ip link set {pf_eth} mtu {mtu}"
276 exec_cmd_no_error(node, cmd, sudo=True)
279 def set_interface_channels(
280 node, pf_pcis, num_queues=1, channel=u"combined"):
281 """Set interface channels for specified interfaces.
283 :param node: Topology node.
284 :param pf_pcis: List of node's interfaces PCI addresses.
285 :param num_queues: Number of channels. (Optional, Default: 1)
286 :param channel: Channel type. (Optional, Default: combined)
289 :type num_queues: int
292 for pf_pci in pf_pcis:
293 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
294 cmd = f"ethtool --set-channels {pf_eth} {channel} {num_queues}"
295 exec_cmd_no_error(node, cmd, sudo=True)
298 def set_interface_xdp_off(node, pf_pcis):
299 """Detaches any currently attached XDP/BPF program from the specified
302 :param node: Topology node.
303 :param pf_pcis: List of node's interfaces PCI addresses.
307 for pf_pci in pf_pcis:
308 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
309 cmd = f"ip link set dev {pf_eth} xdp off"
310 exec_cmd_no_error(node, cmd, sudo=True)
313 def set_interface_flow_control(node, pf_pcis, rxf=u"off", txf=u"off"):
314 """Set Ethernet flow control for specified interfaces.
316 :param node: Topology node.
317 :param pf_pcis: List of node's interfaces PCI addresses.
318 :param rxf: RX flow. (Optional, Default: off).
319 :param txf: TX flow. (Optional, Default: off).
325 for pf_pci in pf_pcis:
326 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
327 cmd = f"ethtool -A {pf_eth} rx {rxf} tx {txf}"
328 ret_code, _, _ = exec_cmd(node, cmd, sudo=True)
329 if int(ret_code) not in (0, 78):
330 raise RuntimeError("Failed to set flow control on {pf_eth}!")
333 def set_pci_parameter(node, pf_pcis, key, value):
334 """Set PCI parameter for specified interfaces.
336 :param node: Topology node.
337 :param pf_pcis: List of node's interfaces PCI addresses.
338 :param key: Key to set.
339 :param value: Value to set.
345 for pf_pci in pf_pcis:
346 cmd = f"setpci -s {pf_pci} {key}={value}"
347 exec_cmd_no_error(node, cmd, sudo=True)
350 def vpp_set_interface_mtu(node, interface, mtu):
351 """Apply new MTU value to a VPP hardware interface.
353 The interface should be down when this is called.
355 :param node: VPP node.
356 :param interface: Interface to set MTU on.
357 :param mtu: Ethernet MTU size in Bytes.
359 :type interface: str or int
362 if isinstance(interface, str):
363 sw_if_index = Topology.get_interface_sw_index(node, interface)
365 sw_if_index = interface
366 cmd = u"hw_interface_set_mtu"
367 err_msg = f"Failed to set interface MTU on host {node[u'host']}"
368 args = dict(sw_if_index=sw_if_index, mtu=int(mtu))
369 with PapiSocketExecutor(node) as papi_exec:
370 papi_exec.add(cmd, **args).get_reply(err_msg)
373 def vpp_node_interfaces_ready_wait(node, retries=15):
374 """Wait until all interfaces with admin-up are in link-up state.
376 :param node: Node to wait on.
377 :param retries: Number of retries to check interface status (optional,
382 :raises RuntimeError: If any interface is not in link-up state after
383 defined number of retries.
385 for _ in range(0, retries):
387 out = InterfaceUtil.vpp_get_interface_data(node)
388 for interface in out:
389 if interface.get(u"flags") == 1:
390 not_ready.append(interface.get(u"interface_name"))
393 f"Interfaces still not in link-up state:\n{not_ready}"
399 err = f"Timeout, interfaces not up:\n{not_ready}" \
400 if u"not_ready" in locals() else u"No check executed!"
401 raise RuntimeError(err)
404 def all_vpp_interfaces_ready_wait(nodes, retries=15):
405 """Wait until all interfaces with admin-up are in link-up state for all
406 nodes in the topology.
408 :param nodes: Nodes in the topology.
409 :param retries: Number of retries to check interface status (optional,
415 for node in nodes.values():
416 if node[u"type"] == NodeType.DUT:
417 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
420 def vpp_get_interface_data(node, interface=None):
421 """Get all interface data from a VPP node. If a name or
422 sw_interface_index is provided, return only data for the matching
425 :param node: VPP node to get interface data from.
426 :param interface: Numeric index or name string of a specific interface.
428 :type interface: int or str
429 :returns: List of dictionaries containing data for each interface, or a
430 single dictionary for the specified interface.
432 :raises TypeError: if the data type of interface is neither basestring
435 def process_if_dump(if_dump):
436 """Process interface dump.
438 :param if_dump: Interface dump.
440 :returns: Processed interface dump.
443 if_dump[u"l2_address"] = str(if_dump[u"l2_address"])
444 if_dump[u"b_dmac"] = str(if_dump[u"b_dmac"])
445 if_dump[u"b_smac"] = str(if_dump[u"b_smac"])
446 if_dump[u"flags"] = if_dump[u"flags"].value
447 if_dump[u"type"] = if_dump[u"type"].value
448 if_dump[u"link_duplex"] = if_dump[u"link_duplex"].value
449 if_dump[u"sub_if_flags"] = if_dump[u"sub_if_flags"].value \
450 if hasattr(if_dump[u"sub_if_flags"], u"value") \
451 else int(if_dump[u"sub_if_flags"])
455 if interface is not None:
456 if isinstance(interface, str):
457 param = u"interface_name"
458 elif isinstance(interface, int):
459 param = u"sw_if_index"
461 raise TypeError(f"Wrong interface format {interface}")
465 cmd = u"sw_interface_dump"
467 name_filter_valid=False,
470 err_msg = f"Failed to get interface dump on host {node[u'host']}"
472 with PapiSocketExecutor(node) as papi_exec:
473 details = papi_exec.add(cmd, **args).get_details(err_msg)
474 logger.debug(f"Received data:\n{details!r}")
476 data = list() if interface is None else dict()
478 if interface is None:
479 data.append(process_if_dump(dump))
480 elif str(dump.get(param)).rstrip(u"\x00") == str(interface):
481 data = process_if_dump(dump)
484 logger.debug(f"Interface data:\n{data}")
488 def vpp_get_interface_name(node, sw_if_index):
489 """Get interface name for the given SW interface index from actual
492 :param node: VPP node to get interface data from.
493 :param sw_if_index: SW interface index of the specific interface.
495 :type sw_if_index: int
496 :returns: Name of the given interface.
499 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
500 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
501 if_data = InterfaceUtil.vpp_get_interface_data(
502 node, if_data[u"sup_sw_if_index"]
505 return if_data.get(u"interface_name")
508 def vpp_get_interface_sw_index(node, interface_name):
509 """Get interface name for the given SW interface index from actual
512 :param node: VPP node to get interface data from.
513 :param interface_name: Interface name.
515 :type interface_name: str
516 :returns: Name of the given interface.
519 if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
521 return if_data.get(u"sw_if_index")
524 def vpp_get_interface_mac(node, interface):
525 """Get MAC address for the given interface from actual interface dump.
527 :param node: VPP node to get interface data from.
528 :param interface: Numeric index or name string of a specific interface.
530 :type interface: int or str
531 :returns: MAC address.
534 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
535 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
536 if_data = InterfaceUtil.vpp_get_interface_data(
537 node, if_data[u"sup_sw_if_index"])
539 return if_data.get(u"l2_address")
542 def vpp_set_interface_mac(node, interface, mac):
543 """Set MAC address for the given interface.
545 :param node: VPP node to set interface MAC.
546 :param interface: Numeric index or name string of a specific interface.
547 :param mac: Required MAC address.
549 :type interface: int or str
552 cmd = u"sw_interface_set_mac_address"
554 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
555 mac_address=L2Util.mac_to_bin(mac)
557 err_msg = f"Failed to set MAC address of interface {interface}" \
558 f"on host {node[u'host']}"
559 with PapiSocketExecutor(node) as papi_exec:
560 papi_exec.add(cmd, **args).get_reply(err_msg)
563 def tg_set_interface_driver(node, pci_addr, driver):
564 """Set interface driver on the TG node.
566 :param node: Node to set interface driver on (must be TG node).
567 :param pci_addr: PCI address of the interface.
568 :param driver: Driver name.
572 :raises RuntimeError: If unbinding from the current driver fails.
573 :raises RuntimeError: If binding to the new driver fails.
575 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
576 if old_driver == driver:
582 # Unbind from current driver
583 if old_driver is not None:
584 cmd = f"sh -c \"echo {pci_addr} > " \
585 f"/sys/bus/pci/drivers/{old_driver}/unbind\""
586 ret_code, _, _ = ssh.exec_command_sudo(cmd)
587 if int(ret_code) != 0:
588 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
590 # Bind to the new driver
591 cmd = f"sh -c \"echo {pci_addr} > /sys/bus/pci/drivers/{driver}/bind\""
592 ret_code, _, _ = ssh.exec_command_sudo(cmd)
593 if int(ret_code) != 0:
594 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
597 def tg_get_interface_driver(node, pci_addr):
598 """Get interface driver from the TG node.
600 :param node: Node to get interface driver on (must be TG node).
601 :param pci_addr: PCI address of the interface.
604 :returns: Interface driver or None if not found.
606 :raises RuntimeError: If PCI rescan or lspci command execution failed.
608 return DUTSetup.get_pci_dev_driver(node, pci_addr)
611 def tg_set_interfaces_default_driver(node):
612 """Set interfaces default driver specified in topology yaml file.
614 :param node: Node to setup interfaces driver on (must be TG node).
617 for interface in node[u"interfaces"].values():
618 InterfaceUtil.tg_set_interface_driver(
619 node, interface[u"pci_address"], interface[u"driver"]
623 def update_vpp_interface_data_on_node(node):
624 """Update vpp generated interface data for a given node in DICT__nodes.
626 Updates interface names, software if index numbers and any other details
627 generated specifically by vpp that are unknown before testcase run.
628 It does this by dumping interface list from all devices using python
629 api, and pairing known information from topology (mac address) to state
632 :param node: Node selected from DICT__nodes.
635 interface_list = InterfaceUtil.vpp_get_interface_data(node)
636 interface_dict = dict()
637 for ifc in interface_list:
638 interface_dict[ifc[u"l2_address"]] = ifc
640 for if_name, if_data in node[u"interfaces"].items():
641 ifc_dict = interface_dict.get(if_data[u"mac_address"])
642 if ifc_dict is not None:
643 if_data[u"name"] = ifc_dict[u"interface_name"]
644 if_data[u"vpp_sw_index"] = ifc_dict[u"sw_if_index"]
645 if_data[u"mtu"] = ifc_dict[u"mtu"][0]
647 f"Interface {if_name} found by MAC "
648 f"{if_data[u'mac_address']}"
652 f"Interface {if_name} not found by MAC "
653 f"{if_data[u'mac_address']}"
655 if_data[u"vpp_sw_index"] = None
658 def update_nic_interface_names(node):
659 """Update interface names based on nic type and PCI address.
661 This method updates interface names in the same format as VPP does.
663 :param node: Node dictionary.
666 for ifc in node[u"interfaces"].values():
667 if_pci = ifc[u"pci_address"].replace(u".", u":").split(u":")
668 loc = f"{int(if_pci[1], 16):x}/{int(if_pci[2], 16):x}/" \
669 f"{int(if_pci[3], 16):x}"
670 if ifc[u"model"] == u"Intel-XL710":
671 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
672 elif ifc[u"model"] == u"Intel-X710":
673 ifc[u"name"] = f"TenGigabitEthernet{loc}"
674 elif ifc[u"model"] == u"Intel-X520-DA2":
675 ifc[u"name"] = f"TenGigabitEthernet{loc}"
676 elif ifc[u"model"] == u"Cisco-VIC-1385":
677 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
678 elif ifc[u"model"] == u"Cisco-VIC-1227":
679 ifc[u"name"] = f"TenGigabitEthernet{loc}"
681 ifc[u"name"] = f"UnknownEthernet{loc}"
684 def update_nic_interface_names_on_all_duts(nodes):
685 """Update interface names based on nic type and PCI address on all DUTs.
687 This method updates interface names in the same format as VPP does.
689 :param nodes: Topology nodes.
692 for node in nodes.values():
693 if node[u"type"] == NodeType.DUT:
694 InterfaceUtil.update_nic_interface_names(node)
697 def update_tg_interface_data_on_node(node):
698 """Update interface name for TG/linux node in DICT__nodes.
701 # for dev in `ls /sys/class/net/`;
702 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
703 "52:54:00:9f:82:63": "eth0"
704 "52:54:00:77:ae:a9": "eth1"
705 "52:54:00:e1:8a:0f": "eth2"
706 "00:00:00:00:00:00": "lo"
708 :param node: Node selected from DICT__nodes.
710 :raises RuntimeError: If getting of interface name and MAC fails.
712 # First setup interface driver specified in yaml file
713 InterfaceUtil.tg_set_interfaces_default_driver(node)
715 # Get interface names
719 cmd = u'for dev in `ls /sys/class/net/`; do echo "\\"`cat ' \
720 u'/sys/class/net/$dev/address`\\": \\"$dev\\""; done;'
722 ret_code, stdout, _ = ssh.exec_command(cmd)
723 if int(ret_code) != 0:
724 raise RuntimeError(u"Get interface name and MAC failed")
725 tmp = u"{" + stdout.rstrip().replace(u"\n", u",") + u"}"
727 interfaces = JsonParser().parse_data(tmp)
728 for interface in node[u"interfaces"].values():
729 name = interfaces.get(interface[u"mac_address"])
732 interface[u"name"] = name
735 def iface_update_numa_node(node):
736 """For all interfaces from topology file update numa node based on
737 information from the node.
739 :param node: Node from topology.
742 :raises ValueError: If numa node ia less than 0.
743 :raises RuntimeError: If update of numa node failed.
746 for if_key in Topology.get_node_interfaces(node):
747 if_pci = Topology.get_interface_pci_addr(node, if_key)
749 cmd = f"cat /sys/bus/pci/devices/{if_pci}/numa_node"
751 ret, out, _ = ssh.exec_command(cmd)
754 numa_node = 0 if int(out) < 0 else int(out)
757 f"Reading numa location failed for: {if_pci}"
760 Topology.set_interface_numa_node(
761 node, if_key, numa_node
765 raise RuntimeError(f"Update numa node failed for: {if_pci}")
768 def update_all_interface_data_on_all_nodes(
769 nodes, skip_tg=False, skip_vpp=False):
770 """Update interface names on all nodes in DICT__nodes.
772 This method updates the topology dictionary by querying interface lists
773 of all nodes mentioned in the topology dictionary.
775 :param nodes: Nodes in the topology.
776 :param skip_tg: Skip TG node.
777 :param skip_vpp: Skip VPP node.
782 for node in nodes.values():
783 if node[u"type"] == NodeType.DUT and not skip_vpp:
784 InterfaceUtil.update_vpp_interface_data_on_node(node)
785 elif node[u"type"] == NodeType.TG and not skip_tg:
786 InterfaceUtil.update_tg_interface_data_on_node(node)
787 InterfaceUtil.iface_update_numa_node(node)
790 def create_vlan_subinterface(node, interface, vlan):
791 """Create VLAN sub-interface on node.
793 :param node: Node to add VLAN subinterface on.
794 :param interface: Interface name or index on which create VLAN
796 :param vlan: VLAN ID of the subinterface to be created.
798 :type interface: str on int
800 :returns: Name and index of created subinterface.
802 :raises RuntimeError: if it is unable to create VLAN subinterface on the
803 node or interface cannot be converted.
805 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
807 cmd = u"create_vlan_subif"
809 sw_if_index=sw_if_index,
812 err_msg = f"Failed to create VLAN sub-interface on host {node[u'host']}"
814 with PapiSocketExecutor(node) as papi_exec:
815 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
817 if_key = Topology.add_new_port(node, u"vlan_subif")
818 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
819 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
820 Topology.update_interface_name(node, if_key, ifc_name)
822 return f"{interface}.{vlan}", sw_if_index
825 def create_vxlan_interface(node, vni, source_ip, destination_ip):
826 """Create VXLAN interface and return sw if index of created interface.
828 :param node: Node where to create VXLAN interface.
829 :param vni: VXLAN Network Identifier.
830 :param source_ip: Source IP of a VXLAN Tunnel End Point.
831 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
835 :type destination_ip: str
836 :returns: SW IF INDEX of created interface.
838 :raises RuntimeError: if it is unable to create VxLAN interface on the
841 cmd = u"vxlan_add_del_tunnel_v3"
844 instance=Constants.BITWISE_NON_ZERO,
845 src_address=IPAddress.create_ip_address_object(
846 ip_address(source_ip)
848 dst_address=IPAddress.create_ip_address_object(
849 ip_address(destination_ip)
851 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
853 decap_next_index=Constants.BITWISE_NON_ZERO,
856 err_msg = f"Failed to create VXLAN tunnel interface " \
857 f"on host {node[u'host']}"
858 with PapiSocketExecutor(node) as papi_exec:
859 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
861 if_key = Topology.add_new_port(node, u"vxlan_tunnel")
862 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
863 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
864 Topology.update_interface_name(node, if_key, ifc_name)
869 def set_vxlan_bypass(node, interface=None):
870 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
872 By adding the IPv4 vxlan-bypass graph node to an interface, the node
873 checks for and validate input vxlan packet and bypass ip4-lookup,
874 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
875 This node will cause extra overhead to for non-vxlan packets which is
878 :param node: Node where to set VXLAN bypass.
879 :param interface: Numeric index or name string of a specific interface.
881 :type interface: int or str
882 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
884 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
886 cmd = u"sw_interface_set_vxlan_bypass"
889 sw_if_index=sw_if_index,
892 err_msg = f"Failed to set VXLAN bypass on interface " \
893 f"on host {node[u'host']}"
894 with PapiSocketExecutor(node) as papi_exec:
895 papi_exec.add(cmd, **args).get_reply(err_msg)
898 def vxlan_dump(node, interface=None):
899 """Get VxLAN data for the given interface.
901 :param node: VPP node to get interface data from.
902 :param interface: Numeric index or name string of a specific interface.
903 If None, information about all VxLAN interfaces is returned.
905 :type interface: int or str
906 :returns: Dictionary containing data for the given VxLAN interface or if
907 interface=None, the list of dictionaries with all VxLAN interfaces.
909 :raises TypeError: if the data type of interface is neither basestring
912 def process_vxlan_dump(vxlan_dump):
913 """Process vxlan dump.
915 :param vxlan_dump: Vxlan interface dump.
916 :type vxlan_dump: dict
917 :returns: Processed vxlan interface dump.
920 vxlan_dump[u"src_address"] = str(vxlan_dump[u"src_address"])
921 vxlan_dump[u"dst_address"] = str(vxlan_dump[u"dst_address"])
924 if interface is not None:
925 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
927 sw_if_index = int(Constants.BITWISE_NON_ZERO)
929 cmd = u"vxlan_tunnel_dump"
931 sw_if_index=sw_if_index
933 err_msg = f"Failed to get VXLAN dump on host {node[u'host']}"
935 with PapiSocketExecutor(node) as papi_exec:
936 details = papi_exec.add(cmd, **args).get_details(err_msg)
938 data = list() if interface is None else dict()
940 if interface is None:
941 data.append(process_vxlan_dump(dump))
942 elif dump[u"sw_if_index"] == sw_if_index:
943 data = process_vxlan_dump(dump)
946 logger.debug(f"VXLAN data:\n{data}")
950 def create_subinterface(
951 node, interface, sub_id, outer_vlan_id=None, inner_vlan_id=None,
953 """Create sub-interface on node. It is possible to set required
954 sub-interface type and VLAN tag(s).
956 :param node: Node to add sub-interface.
957 :param interface: Interface name on which create sub-interface.
958 :param sub_id: ID of the sub-interface to be created.
959 :param outer_vlan_id: Optional outer VLAN ID.
960 :param inner_vlan_id: Optional inner VLAN ID.
961 :param type_subif: Optional type of sub-interface. Values supported by
962 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
965 :type interface: str or int
967 :type outer_vlan_id: int
968 :type inner_vlan_id: int
969 :type type_subif: str
970 :returns: Name and index of created sub-interface.
972 :raises RuntimeError: If it is not possible to create sub-interface.
974 subif_types = type_subif.split()
977 if u"no_tags" in subif_types:
978 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
979 if u"one_tag" in subif_types:
980 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
981 if u"two_tags" in subif_types:
982 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
983 if u"dot1ad" in subif_types:
984 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
985 if u"exact_match" in subif_types:
986 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
987 if u"default_sub" in subif_types:
988 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
989 if type_subif == u"default_sub":
990 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
991 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
993 cmd = u"create_subif"
995 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
997 sub_if_flags=flags.value if hasattr(flags, u"value")
999 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
1000 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
1002 err_msg = f"Failed to create sub-interface on host {node[u'host']}"
1003 with PapiSocketExecutor(node) as papi_exec:
1004 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1006 if_key = Topology.add_new_port(node, u"subinterface")
1007 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1008 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1009 Topology.update_interface_name(node, if_key, ifc_name)
1011 return f"{interface}.{sub_id}", sw_if_index
1014 def create_gre_tunnel_interface(node, source_ip, destination_ip):
1015 """Create GRE tunnel interface on node.
1017 :param node: VPP node to add tunnel interface.
1018 :param source_ip: Source of the GRE tunnel.
1019 :param destination_ip: Destination of the GRE tunnel.
1021 :type source_ip: str
1022 :type destination_ip: str
1023 :returns: Name and index of created GRE tunnel interface.
1025 :raises RuntimeError: If unable to create GRE tunnel interface.
1027 cmd = u"gre_tunnel_add_del"
1030 instance=Constants.BITWISE_NON_ZERO,
1032 dst=str(destination_ip),
1040 err_msg = f"Failed to create GRE tunnel interface " \
1041 f"on host {node[u'host']}"
1042 with PapiSocketExecutor(node) as papi_exec:
1043 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1045 if_key = Topology.add_new_port(node, u"gre_tunnel")
1046 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1047 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1048 Topology.update_interface_name(node, if_key, ifc_name)
1050 return ifc_name, sw_if_index
1053 def create_gtpu_tunnel_interface(node, teid, source_ip, destination_ip):
1054 """Create GTPU interface and return sw if index of created interface.
1056 :param node: Node where to create GTPU interface.
1057 :param teid: GTPU Tunnel Endpoint Identifier.
1058 :param source_ip: Source IP of a GTPU Tunnel End Point.
1059 :param destination_ip: Destination IP of a GTPU Tunnel End Point.
1062 :type source_ip: str
1063 :type destination_ip: str
1064 :returns: SW IF INDEX of created interface.
1066 :raises RuntimeError: if it is unable to create GTPU interface on the
1069 cmd = u"gtpu_add_del_tunnel"
1072 src_address=IPAddress.create_ip_address_object(
1073 ip_address(source_ip)
1075 dst_address=IPAddress.create_ip_address_object(
1076 ip_address(destination_ip)
1078 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
1083 err_msg = f"Failed to create GTPU tunnel interface " \
1084 f"on host {node[u'host']}"
1085 with PapiSocketExecutor(node) as papi_exec:
1086 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1088 if_key = Topology.add_new_port(node, u"gtpu_tunnel")
1089 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1090 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1091 Topology.update_interface_name(node, if_key, ifc_name)
1096 def vpp_enable_gtpu_offload_rx(node, interface, gtpu_if_index):
1097 """Enable GTPU offload RX onto interface.
1099 :param node: Node to run command on.
1100 :param interface: Name of the specific interface.
1101 :param gtpu_if_index: Index of GTPU tunnel interface.
1104 :type interface: str
1105 :type gtpu_interface: int
1107 sw_if_index = Topology.get_interface_sw_index(node, interface)
1109 cmd = u"gtpu_offload_rx"
1111 hw_if_index=sw_if_index,
1112 sw_if_index=gtpu_if_index,
1116 err_msg = f"Failed to enable GTPU offload RX on host {node[u'host']}"
1117 with PapiSocketExecutor(node) as papi_exec:
1118 papi_exec.add(cmd, **args).get_reply(err_msg)
1121 def vpp_create_loopback(node, mac=None):
1122 """Create loopback interface on VPP node.
1124 :param node: Node to create loopback interface on.
1125 :param mac: Optional MAC address for loopback interface.
1128 :returns: SW interface index.
1130 :raises RuntimeError: If it is not possible to create loopback on the
1133 cmd = u"create_loopback_instance"
1135 mac_address=L2Util.mac_to_bin(mac) if mac else 0,
1139 err_msg = f"Failed to create loopback interface on host {node[u'host']}"
1140 with PapiSocketExecutor(node) as papi_exec:
1141 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1143 if_key = Topology.add_new_port(node, u"loopback")
1144 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1145 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1146 Topology.update_interface_name(node, if_key, ifc_name)
1148 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1149 Topology.update_interface_mac_address(node, if_key, mac)
1154 def vpp_create_bond_interface(
1155 node, mode, load_balance=None, mac=None, gso=False):
1156 """Create bond interface on VPP node.
1158 :param node: DUT node from topology.
1159 :param mode: Link bonding mode.
1160 :param load_balance: Load balance (optional, valid for xor and lacp
1161 modes, otherwise ignored). Default: None.
1162 :param mac: MAC address to assign to the bond interface (optional).
1164 :param gso: Enable GSO support (optional). Default: False.
1167 :type load_balance: str
1170 :returns: Interface key (name) in topology.
1172 :raises RuntimeError: If it is not possible to create bond interface on
1175 cmd = u"bond_create2"
1177 id=int(Constants.BITWISE_NON_ZERO),
1178 use_custom_mac=bool(mac is not None),
1179 mac_address=L2Util.mac_to_bin(mac) if mac else None,
1182 f"BOND_API_MODE_{mode.replace(u'-', u'_').upper()}"
1184 lb=0 if load_balance is None else getattr(
1185 LinkBondLoadBalanceAlgo,
1186 f"BOND_API_LB_ALGO_{load_balance.upper()}"
1191 err_msg = f"Failed to create bond interface on host {node[u'host']}"
1192 with PapiSocketExecutor(node) as papi_exec:
1193 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1195 InterfaceUtil.add_eth_interface(
1196 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_bond"
1198 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1203 def add_eth_interface(
1204 node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
1206 """Add ethernet interface to current topology.
1208 :param node: DUT node from topology.
1209 :param ifc_name: Name of the interface.
1210 :param sw_if_index: SW interface index.
1211 :param ifc_pfx: Interface key prefix.
1212 :param host_if_key: Host interface key from topology file.
1215 :type sw_if_index: int
1217 :type host_if_key: str
1219 if_key = Topology.add_new_port(node, ifc_pfx)
1221 if ifc_name and sw_if_index is None:
1222 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1224 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1225 if sw_if_index and ifc_name is None:
1226 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1227 Topology.update_interface_name(node, if_key, ifc_name)
1228 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1229 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1230 if host_if_key is not None:
1231 Topology.set_interface_numa_node(
1232 node, if_key, Topology.get_interface_numa_node(
1236 Topology.update_interface_pci_address(
1237 node, if_key, Topology.get_interface_pci_addr(node, host_if_key)
1241 def vpp_create_avf_interface(
1242 node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0):
1243 """Create AVF interface on VPP node.
1245 :param node: DUT node from topology.
1246 :param if_key: Interface key from topology file of interface
1247 to be bound to i40evf driver.
1248 :param num_rx_queues: Number of RX queues.
1249 :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1250 :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1253 :type num_rx_queues: int
1256 :returns: AVF interface key (name) in topology.
1258 :raises RuntimeError: If it is not possible to create AVF interface on
1261 PapiSocketExecutor.run_cli_cmd(
1262 node, u"set logging class avf level debug"
1266 vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
1268 pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1270 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1274 err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1276 # FIXME: Remove once the fw/driver is upgraded.
1278 with PapiSocketExecutor(node) as papi_exec:
1280 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(
1284 except AssertionError:
1285 logger.error(err_msg)
1287 raise AssertionError(err_msg)
1289 InterfaceUtil.add_eth_interface(
1290 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
1294 return Topology.get_interface_by_sw_index(node, sw_if_index)
1297 def vpp_create_af_xdp_interface(
1298 node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
1300 """Create AF_XDP interface on VPP node.
1302 :param node: DUT node from topology.
1303 :param if_key: Physical interface key from topology file of interface
1304 to be bound to compatible driver.
1305 :param num_rx_queues: Number of RX queues. (Optional, Default: none)
1306 :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1307 :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1308 :param mode: AF_XDP interface mode. (Optional, Default: auto).
1311 :type num_rx_queues: int
1315 :returns: Interface key (name) in topology file.
1317 :raises RuntimeError: If it is not possible to create AF_XDP interface
1320 PapiSocketExecutor.run_cli_cmd(
1321 node, u"set logging class af_xdp level debug"
1324 cmd = u"af_xdp_create_v2"
1325 pci_addr = Topology.get_interface_pci_addr(node, if_key)
1327 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1328 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1329 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1332 mode=getattr(AfXdpMode, f"AF_XDP_API_MODE_{mode.upper()}").value
1334 err_msg = f"Failed to create AF_XDP interface on host {node[u'host']}"
1335 with PapiSocketExecutor(node) as papi_exec:
1336 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1338 InterfaceUtil.vpp_set_interface_mac(
1339 node, sw_if_index, Topology.get_interface_mac(node, if_key)
1341 InterfaceUtil.add_eth_interface(
1342 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_af_xdp",
1346 return Topology.get_interface_by_sw_index(node, sw_if_index)
1349 def vpp_create_rdma_interface(
1350 node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
1352 """Create RDMA interface on VPP node.
1354 :param node: DUT node from topology.
1355 :param if_key: Physical interface key from topology file of interface
1356 to be bound to rdma-core driver.
1357 :param num_rx_queues: Number of RX queues.
1358 :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1359 :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1360 :param mode: RDMA interface mode - auto/ibv/dv.
1363 :type num_rx_queues: int
1367 :returns: Interface key (name) in topology file.
1369 :raises RuntimeError: If it is not possible to create RDMA interface on
1372 PapiSocketExecutor.run_cli_cmd(
1373 node, u"set logging class rdma level debug"
1376 cmd = u"rdma_create_v3"
1377 pci_addr = Topology.get_interface_pci_addr(node, if_key)
1379 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1380 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1381 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1384 mode=getattr(RdmaMode, f"RDMA_API_MODE_{mode.upper()}").value,
1385 # Note: Set True for non-jumbo packets.
1388 # TODO: Apply desired RSS flags.
1390 err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1391 with PapiSocketExecutor(node) as papi_exec:
1392 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1394 InterfaceUtil.vpp_set_interface_mac(
1395 node, sw_if_index, Topology.get_interface_mac(node, if_key)
1397 InterfaceUtil.add_eth_interface(
1398 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
1402 return Topology.get_interface_by_sw_index(node, sw_if_index)
1405 def vpp_add_bond_member(node, interface, bond_if):
1406 """Add member interface to bond interface on VPP node.
1408 :param node: DUT node from topology.
1409 :param interface: Physical interface key from topology file.
1410 :param bond_if: Load balance
1412 :type interface: str
1414 :raises RuntimeError: If it is not possible to add member to bond
1415 interface on the node.
1417 cmd = u"bond_add_member"
1419 sw_if_index=Topology.get_interface_sw_index(node, interface),
1420 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1422 is_long_timeout=False
1424 err_msg = f"Failed to add member {interface} to bond interface " \
1425 f"{bond_if} on host {node[u'host']}"
1426 with PapiSocketExecutor(node) as papi_exec:
1427 papi_exec.add(cmd, **args).get_reply(err_msg)
1430 def vpp_show_bond_data_on_node(node, verbose=False):
1431 """Show (detailed) bond information on VPP node.
1433 :param node: DUT node from topology.
1434 :param verbose: If detailed information is required or not.
1438 cmd = u"sw_bond_interface_dump"
1439 err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1441 data = f"Bond data on node {node[u'host']}:\n"
1442 with PapiSocketExecutor(node) as papi_exec:
1443 details = papi_exec.add(cmd).get_details(err_msg)
1445 for bond in details:
1446 data += f"{bond[u'interface_name']}\n"
1447 data += u" mode: {m}\n".format(
1448 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1450 data += u" load balance: {lb}\n".format(
1451 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1453 data += f" number of active members: {bond[u'active_members']}\n"
1455 member_data = InterfaceUtil.vpp_bond_member_dump(
1456 node, Topology.get_interface_by_sw_index(
1457 node, bond[u"sw_if_index"]
1460 for member in member_data:
1461 if not member[u"is_passive"]:
1462 data += f" {member[u'interface_name']}\n"
1463 data += f" number of members: {bond[u'members']}\n"
1465 for member in member_data:
1466 data += f" {member[u'interface_name']}\n"
1467 data += f" interface id: {bond[u'id']}\n"
1468 data += f" sw_if_index: {bond[u'sw_if_index']}\n"
1472 def vpp_bond_member_dump(node, interface):
1473 """Get bond interface slave(s) data on VPP node.
1475 :param node: DUT node from topology.
1476 :param interface: Physical interface key from topology file.
1478 :type interface: str
1479 :returns: Bond slave interface data.
1482 cmd = u"sw_member_interface_dump"
1484 sw_if_index=Topology.get_interface_sw_index(node, interface)
1486 err_msg = f"Failed to get slave dump on host {node[u'host']}"
1488 with PapiSocketExecutor(node) as papi_exec:
1489 details = papi_exec.add(cmd, **args).get_details(err_msg)
1491 logger.debug(f"Member data:\n{details}")
1495 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1496 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1498 :param nodes: Nodes in the topology.
1499 :param verbose: If detailed information is required or not.
1503 for node_data in nodes.values():
1504 if node_data[u"type"] == NodeType.DUT:
1505 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1508 def vpp_enable_input_acl_interface(
1509 node, interface, ip_version, table_index):
1510 """Enable input acl on interface.
1512 :param node: VPP node to setup interface for input acl.
1513 :param interface: Interface to setup input acl.
1514 :param ip_version: Version of IP protocol.
1515 :param table_index: Classify table index.
1517 :type interface: str or int
1518 :type ip_version: str
1519 :type table_index: int
1521 cmd = u"input_acl_set_interface"
1523 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1524 ip4_table_index=table_index if ip_version == u"ip4"
1525 else Constants.BITWISE_NON_ZERO,
1526 ip6_table_index=table_index if ip_version == u"ip6"
1527 else Constants.BITWISE_NON_ZERO,
1528 l2_table_index=table_index if ip_version == u"l2"
1529 else Constants.BITWISE_NON_ZERO,
1531 err_msg = f"Failed to enable input acl on interface {interface}"
1532 with PapiSocketExecutor(node) as papi_exec:
1533 papi_exec.add(cmd, **args).get_reply(err_msg)
1536 def get_interface_classify_table(node, interface):
1537 """Get name of classify table for the given interface.
1539 TODO: Move to Classify.py.
1541 :param node: VPP node to get data from.
1542 :param interface: Name or sw_if_index of a specific interface.
1544 :type interface: str or int
1545 :returns: Classify table name.
1548 if isinstance(interface, str):
1549 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1551 sw_if_index = interface
1553 cmd = u"classify_table_by_interface"
1555 sw_if_index=sw_if_index
1557 err_msg = f"Failed to get classify table name by interface {interface}"
1558 with PapiSocketExecutor(node) as papi_exec:
1559 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1564 def get_sw_if_index(node, interface_name):
1565 """Get sw_if_index for the given interface from actual interface dump.
1567 FIXME: Delete and redirect callers to vpp_get_interface_sw_index.
1569 :param node: VPP node to get interface data from.
1570 :param interface_name: Name of the specific interface.
1572 :type interface_name: str
1573 :returns: sw_if_index of the given interface.
1576 interface_data = InterfaceUtil.vpp_get_interface_data(
1577 node, interface=interface_name
1579 return interface_data.get(u"sw_if_index")
1582 def vxlan_gpe_dump(node, interface_name=None):
1583 """Get VxLAN GPE data for the given interface.
1585 :param node: VPP node to get interface data from.
1586 :param interface_name: Name of the specific interface. If None,
1587 information about all VxLAN GPE interfaces is returned.
1589 :type interface_name: str
1590 :returns: Dictionary containing data for the given VxLAN GPE interface
1591 or if interface=None, the list of dictionaries with all VxLAN GPE
1593 :rtype: dict or list
1595 def process_vxlan_gpe_dump(vxlan_dump):
1596 """Process vxlan_gpe dump.
1598 :param vxlan_dump: Vxlan_gpe nterface dump.
1599 :type vxlan_dump: dict
1600 :returns: Processed vxlan_gpe interface dump.
1603 if vxlan_dump[u"is_ipv6"]:
1604 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1605 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1607 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1608 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1611 if interface_name is not None:
1612 sw_if_index = InterfaceUtil.get_interface_index(
1613 node, interface_name
1616 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1618 cmd = u"vxlan_gpe_tunnel_dump"
1620 sw_if_index=sw_if_index
1622 err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1623 with PapiSocketExecutor(node) as papi_exec:
1624 details = papi_exec.add(cmd, **args).get_details(err_msg)
1626 data = list() if interface_name is None else dict()
1627 for dump in details:
1628 if interface_name is None:
1629 data.append(process_vxlan_gpe_dump(dump))
1630 elif dump[u"sw_if_index"] == sw_if_index:
1631 data = process_vxlan_gpe_dump(dump)
1634 logger.debug(f"VXLAN-GPE data:\n{data}")
1638 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1639 """Assign VPP interface to specific VRF/FIB table.
1641 :param node: VPP node where the FIB and interface are located.
1642 :param interface: Interface to be assigned to FIB.
1643 :param table_id: VRF table ID.
1644 :param ipv6: Assign to IPv6 table. Default False.
1646 :type interface: str or int
1650 cmd = u"sw_interface_set_table"
1652 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1654 vrf_id=int(table_id)
1656 err_msg = f"Failed to assign interface {interface} to FIB table"
1657 with PapiSocketExecutor(node) as papi_exec:
1658 papi_exec.add(cmd, **args).get_reply(err_msg)
1661 def set_linux_interface_mac(
1662 node, interface, mac, namespace=None, vf_id=None):
1663 """Set MAC address for interface in linux.
1665 :param node: Node where to execute command.
1666 :param interface: Interface in namespace.
1667 :param mac: MAC to be assigned to interface.
1668 :param namespace: Execute command in namespace. Optional
1669 :param vf_id: Virtual Function id. Optional
1671 :type interface: str
1673 :type namespace: str
1676 mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1677 else f"address {mac}"
1678 ns_str = f"ip netns exec {namespace}" if namespace else u""
1680 cmd = f"{ns_str} ip link set {interface} {mac_str}"
1681 exec_cmd_no_error(node, cmd, sudo=True)
1684 def set_linux_interface_promisc(
1685 node, interface, namespace=None, vf_id=None, state=u"on"):
1686 """Set promisc state for interface in linux.
1688 :param node: Node where to execute command.
1689 :param interface: Interface in namespace.
1690 :param namespace: Exec command in namespace. (Optional, Default: None)
1691 :param vf_id: Virtual Function id. (Optional, Default: None)
1692 :param state: State of feature. (Optional, Default: on)
1694 :type interface: str
1695 :type namespace: str
1699 promisc_str = f"vf {vf_id} promisc {state}" if vf_id is not None \
1700 else f"promisc {state}"
1701 ns_str = f"ip netns exec {namespace}" if namespace else u""
1703 cmd = f"{ns_str} ip link set dev {interface} {promisc_str}"
1704 exec_cmd_no_error(node, cmd, sudo=True)
1707 def set_linux_interface_trust_on(
1708 node, interface, namespace=None, vf_id=None):
1709 """Set trust on (promisc) for interface in linux.
1711 :param node: Node where to execute command.
1712 :param interface: Interface in namespace.
1713 :param namespace: Execute command in namespace. Optional
1714 :param vf_id: Virtual Function id. Optional
1716 :type interface: str
1717 :type namespace: str
1720 trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1721 ns_str = f"ip netns exec {namespace}" if namespace else u""
1723 cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1724 exec_cmd_no_error(node, cmd, sudo=True)
1727 def set_linux_interface_spoof_off(
1728 node, interface, namespace=None, vf_id=None):
1729 """Set spoof off for interface in linux.
1731 :param node: Node where to execute command.
1732 :param interface: Interface in namespace.
1733 :param namespace: Execute command in namespace. Optional
1734 :param vf_id: Virtual Function id. Optional
1736 :type interface: str
1737 :type namespace: str
1740 spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1742 ns_str = f"ip netns exec {namespace}" if namespace else u""
1744 cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1745 exec_cmd_no_error(node, cmd, sudo=True)
1748 def set_linux_interface_state(
1749 node, interface, namespace=None, state=u"up"):
1750 """Set operational state for interface in linux.
1752 :param node: Node where to execute command.
1753 :param interface: Interface in namespace.
1754 :param namespace: Execute command in namespace. Optional
1755 :param state: Up/Down.
1757 :type interface: str
1758 :type namespace: str
1761 ns_str = f"ip netns exec {namespace}" if namespace else u""
1763 cmd = f"{ns_str} ip link set dev {interface} {state}"
1764 exec_cmd_no_error(node, cmd, sudo=True)
1767 def init_interface(node, ifc_key, driver, numvfs=0, osi_layer=u"L2"):
1768 """Init PCI device. Check driver compatibility and bind to proper
1769 drivers. Optionally create NIC VFs.
1771 :param node: DUT node.
1772 :param ifc_key: Interface key from topology file.
1773 :param driver: Base driver to use.
1774 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1775 :param osi_layer: OSI Layer type to initialize TG with.
1776 Default value "L2" sets linux interface spoof off.
1781 :type osi_layer: str
1782 :returns: Virtual Function topology interface keys.
1784 :raises RuntimeError: If a reason preventing initialization is found.
1786 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1788 if driver == u"avf":
1789 if kernel_driver not in (
1790 u"ice", u"iavf", u"i40e", u"i40evf"):
1792 f"AVF needs ice or i40e compatible driver, not "
1793 f"{kernel_driver} at node {node[u'host']} ifc {ifc_key}"
1795 vf_keys = InterfaceUtil.init_generic_interface(
1796 node, ifc_key, numvfs=numvfs, osi_layer=osi_layer
1798 elif driver == u"af_xdp":
1799 if kernel_driver not in (
1800 u"ice", u"iavf", u"i40e", u"i40evf", u"mlx5_core",
1803 f"AF_XDP needs ice/i40e/rdma/ixgbe compatible driver, not "
1804 f"{kernel_driver} at node {node[u'host']} ifc {ifc_key}"
1806 vf_keys = InterfaceUtil.init_generic_interface(
1807 node, ifc_key, numvfs=numvfs, osi_layer=osi_layer
1809 elif driver == u"rdma-core":
1810 vf_keys = InterfaceUtil.init_generic_interface(
1811 node, ifc_key, numvfs=numvfs, osi_layer=osi_layer
1816 def init_generic_interface(node, ifc_key, numvfs=0, osi_layer=u"L2"):
1817 """Init PCI device. Bind to proper drivers. Optionally create NIC VFs.
1819 :param node: DUT node.
1820 :param ifc_key: Interface key from topology file.
1821 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1822 :param osi_layer: OSI Layer type to initialize TG with.
1823 Default value "L2" sets linux interface spoof off.
1827 :type osi_layer: str
1828 :returns: Virtual Function topology interface keys.
1830 :raises RuntimeError: If a reason preventing initialization is found.
1832 # Read PCI address and driver.
1833 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1834 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1835 uio_driver = Topology.get_uio_driver(node)
1836 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1837 current_driver = DUTSetup.get_pci_dev_driver(
1838 node, pf_pci_addr.replace(u":", r"\:"))
1839 pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1841 VPPUtil.stop_vpp_service(node)
1842 if current_driver != kernel_driver:
1843 # PCI device must be re-bound to kernel driver before creating VFs.
1844 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1845 # Stop VPP to prevent deadlock.
1846 # Unbind from current driver if bound.
1848 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1849 # Bind to kernel driver.
1850 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1852 # Initialize PCI VFs.
1853 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1856 if osi_layer == u"L2":
1857 InterfaceUtil.set_linux_interface_promisc(node, pf_dev)
1860 # Set MAC address and bind each virtual function to uio driver.
1861 for vf_id in range(numvfs):
1862 vf_mac_addr = u":".join(
1863 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1864 pf_mac_addr[5], f"{vf_id:02x}"
1868 InterfaceUtil.set_linux_interface_trust_on(
1869 node, pf_dev, vf_id=vf_id
1871 if osi_layer == u"L2":
1872 InterfaceUtil.set_linux_interface_spoof_off(
1873 node, pf_dev, vf_id=vf_id
1875 InterfaceUtil.set_linux_interface_mac(
1876 node, pf_dev, vf_mac_addr, vf_id=vf_id
1878 InterfaceUtil.set_linux_interface_state(
1879 node, pf_dev, state=u"up"
1882 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1883 current_driver = DUTSetup.get_pci_dev_driver(
1884 node, vf_pci_addr.replace(":", r"\:")
1887 DUTSetup.pci_vf_driver_unbind(
1888 node, pf_pci_addr, vf_id
1890 DUTSetup.pci_vf_driver_bind(
1891 node, pf_pci_addr, vf_id, uio_driver
1894 # Add newly created ports into topology file
1895 vf_ifc_name = f"{ifc_key}_vif"
1896 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1897 Topology.update_interface_name(
1898 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1900 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1901 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1902 Topology.set_interface_numa_node(
1903 node, vf_ifc_key, Topology.get_interface_numa_node(
1907 vf_ifc_keys.append(vf_ifc_key)
1912 def vpp_sw_interface_rx_placement_dump(node):
1913 """Dump VPP interface RX placement on node.
1915 :param node: Node to run command on.
1917 :returns: Thread mapping information as a list of dictionaries.
1920 cmd = u"sw_interface_rx_placement_dump"
1921 err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1922 with PapiSocketExecutor(node) as papi_exec:
1923 for ifc in node[u"interfaces"].values():
1924 if ifc[u"vpp_sw_index"] is not None:
1925 papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1926 details = papi_exec.get_details(err_msg)
1927 return sorted(details, key=lambda k: k[u"sw_if_index"])
1930 def vpp_sw_interface_rx_placement_dump_on_all_duts(nodes):
1931 """Dump VPP interface RX placement on all given nodes.
1933 :param nodes: Nodes to run command on.
1935 :returns: Thread mapping information as a list of dictionaries.
1938 for node in nodes.values():
1939 if node[u"type"] == NodeType.DUT:
1940 InterfaceUtil.vpp_sw_interface_rx_placement_dump(node)
1943 def vpp_sw_interface_set_rx_placement(
1944 node, sw_if_index, queue_id, worker_id):
1945 """Set interface RX placement to worker on node.
1947 :param node: Node to run command on.
1948 :param sw_if_index: VPP SW interface index.
1949 :param queue_id: VPP interface queue ID.
1950 :param worker_id: VPP worker ID (indexing from 0).
1952 :type sw_if_index: int
1954 :type worker_id: int
1955 :raises RuntimeError: If failed to run command on host or if no API
1958 cmd = u"sw_interface_set_rx_placement"
1959 err_msg = f"Failed to set interface RX placement to worker " \
1960 f"on host {node[u'host']}!"
1962 sw_if_index=sw_if_index,
1964 worker_id=worker_id,
1967 with PapiSocketExecutor(node) as papi_exec:
1968 papi_exec.add(cmd, **args).get_reply(err_msg)
1971 def vpp_round_robin_rx_placement(
1972 node, prefix, workers=None):
1973 """Set Round Robin interface RX placement on all worker threads
1976 If specified, workers limits the number of physical cores used
1977 for data plane I/O work. Other cores are presumed to do something else,
1978 e.g. asynchronous crypto processing.
1979 None means all workers are used for data plane work.
1981 :param node: Topology nodes.
1982 :param prefix: Interface name prefix.
1983 :param workers: Comma separated worker index numbers intended for
1989 thread_data = VPPUtil.vpp_show_threads(node)
1990 worker_cnt = len(thread_data) - 1
1995 for item in thread_data:
1996 if str(item.cpu_id) in workers.split(u","):
1997 worker_ids.append(item.id)
1999 for item in thread_data:
2000 if u"vpp_main" not in item.name:
2001 worker_ids.append(item.id)
2004 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
2005 for interface in node[u"interfaces"].values():
2006 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
2007 and prefix in interface[u"name"]:
2008 InterfaceUtil.vpp_sw_interface_set_rx_placement(
2009 node, placement[u"sw_if_index"], placement[u"queue_id"],
2010 worker_ids[worker_idx % len(worker_ids)] - 1
2015 def vpp_round_robin_rx_placement_on_all_duts(
2016 nodes, prefix, workers=None):
2017 """Set Round Robin interface RX placement on worker threads
2020 If specified, workers limits the number of physical cores used
2021 for data plane I/O work. Other cores are presumed to do something else,
2022 e.g. asynchronous crypto processing.
2023 None means all cores are used for data plane work.
2025 :param nodes: Topology nodes.
2026 :param prefix: Interface name prefix.
2027 :param workers: Comma separated worker index numbers intended for
2033 for node in nodes.values():
2034 if node[u"type"] == NodeType.DUT:
2035 InterfaceUtil.vpp_round_robin_rx_placement(
2036 node, prefix, workers