1 # Copyright (c) 2021 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.CpuUtils import CpuUtils
24 from resources.libraries.python.DUTSetup import DUTSetup
25 from resources.libraries.python.IPAddress import IPAddress
26 from resources.libraries.python.L2Util import L2Util
27 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
28 from resources.libraries.python.parsers.JsonParser import JsonParser
29 from resources.libraries.python.ssh import SSH, exec_cmd, exec_cmd_no_error
30 from resources.libraries.python.topology import NodeType, Topology
31 from resources.libraries.python.VPPUtil import VPPUtil
34 class InterfaceStatusFlags(IntEnum):
35 """Interface status flags."""
36 IF_STATUS_API_FLAG_ADMIN_UP = 1
37 IF_STATUS_API_FLAG_LINK_UP = 2
40 class MtuProto(IntEnum):
45 MTU_PROTO_API_MPLS = 3
49 class LinkDuplex(IntEnum):
51 LINK_DUPLEX_API_UNKNOWN = 0
52 LINK_DUPLEX_API_HALF = 1
53 LINK_DUPLEX_API_FULL = 2
56 class SubInterfaceFlags(IntEnum):
57 """Sub-interface flags."""
58 SUB_IF_API_FLAG_NO_TAGS = 1
59 SUB_IF_API_FLAG_ONE_TAG = 2
60 SUB_IF_API_FLAG_TWO_TAGS = 4
61 SUB_IF_API_FLAG_DOT1AD = 8
62 SUB_IF_API_FLAG_EXACT_MATCH = 16
63 SUB_IF_API_FLAG_DEFAULT = 32
64 SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY = 64
65 SUB_IF_API_FLAG_INNER_VLAN_ID_ANY = 128
66 SUB_IF_API_FLAG_DOT1AH = 256
69 class RxMode(IntEnum):
71 RX_MODE_API_UNKNOWN = 0
72 RX_MODE_API_POLLING = 1
73 RX_MODE_API_INTERRUPT = 2
74 RX_MODE_API_ADAPTIVE = 3
75 RX_MODE_API_DEFAULT = 4
78 class IfType(IntEnum):
81 IF_API_TYPE_HARDWARE = 0
88 class LinkBondLoadBalanceAlgo(IntEnum):
89 """Link bonding load balance algorithm."""
90 BOND_API_LB_ALGO_L2 = 0
91 BOND_API_LB_ALGO_L34 = 1
92 BOND_API_LB_ALGO_L23 = 2
93 BOND_API_LB_ALGO_RR = 3
94 BOND_API_LB_ALGO_BC = 4
95 BOND_API_LB_ALGO_AB = 5
98 class LinkBondMode(IntEnum):
99 """Link bonding mode."""
100 BOND_API_MODE_ROUND_ROBIN = 1
101 BOND_API_MODE_ACTIVE_BACKUP = 2
102 BOND_API_MODE_XOR = 3
103 BOND_API_MODE_BROADCAST = 4
104 BOND_API_MODE_LACP = 5
107 class RdmaMode(IntEnum):
108 """RDMA interface mode."""
109 RDMA_API_MODE_AUTO = 0
110 RDMA_API_MODE_IBV = 1
115 """General utilities for managing interfaces"""
118 def pci_to_int(pci_str):
119 """Convert PCI address from string format (0000:18:0a.0) to
120 integer representation (169345024).
122 :param pci_str: PCI address in string representation.
124 :returns: Integer representation of PCI address.
127 pci = list(pci_str.split(u":")[0:2])
128 pci.extend(pci_str.split(u":")[2].split(u"."))
130 return (int(pci[0], 16) | int(pci[1], 16) << 16 |
131 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
134 def pci_to_eth(node, pci_str):
135 """Convert PCI address on DUT to Linux ethernet name.
137 :param node: DUT node
138 :param pci_str: PCI address.
141 :returns: Ethernet name.
144 cmd = f"basename /sys/bus/pci/devices/{pci_str}/net/*"
146 stdout, _ = exec_cmd_no_error(node, cmd)
148 raise RuntimeError(f"Cannot convert {pci_str} to ethernet name!")
150 return stdout.strip()
153 def get_interface_index(node, interface):
154 """Get interface sw_if_index from topology file.
156 :param node: Node where the interface is.
157 :param interface: Numeric index or name string of a specific interface.
159 :type interface: str or int
160 :returns: SW interface index.
164 sw_if_index = int(interface)
166 sw_if_index = Topology.get_interface_sw_index(node, interface)
167 if sw_if_index is None:
169 Topology.get_interface_sw_index_by_name(node, interface)
170 except TypeError as err:
171 raise TypeError(f"Wrong interface format {interface}") from err
176 def set_interface_state(node, interface, state, if_type=u"key"):
177 """Set interface state on a node.
179 Function can be used for DUTs as well as for TGs.
181 :param node: Node where the interface is.
182 :param interface: Interface key or sw_if_index or name.
183 :param state: One of 'up' or 'down'.
184 :param if_type: Interface type
186 :type interface: str or int
190 :raises ValueError: If the interface type is unknown.
191 :raises ValueError: If the state of interface is unexpected.
192 :raises ValueError: If the node has an unknown node type.
194 if if_type == u"key":
195 if isinstance(interface, str):
196 sw_if_index = Topology.get_interface_sw_index(node, interface)
197 iface_name = Topology.get_interface_name(node, interface)
199 sw_if_index = interface
200 elif if_type == u"name":
201 iface_key = Topology.get_interface_by_name(node, interface)
202 if iface_key is not None:
203 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
204 iface_name = interface
206 raise ValueError(f"Unknown if_type: {if_type}")
208 if node[u"type"] == NodeType.DUT:
210 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
211 elif state == u"down":
214 raise ValueError(f"Unexpected interface state: {state}")
215 cmd = u"sw_interface_set_flags"
216 err_msg = f"Failed to set interface state on host {node[u'host']}"
218 sw_if_index=int(sw_if_index),
221 with PapiSocketExecutor(node) as papi_exec:
222 papi_exec.add(cmd, **args).get_reply(err_msg)
223 elif node[u"type"] == NodeType.TG or node[u"type"] == NodeType.VM:
224 cmd = f"ip link set {iface_name} {state}"
225 exec_cmd_no_error(node, cmd, sudo=True)
228 f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
232 def set_interface_mtu(node, pf_pcis, mtu=9200):
233 """Set Ethernet MTU for specified interfaces.
235 :param node: Topology node.
236 :param pf_pcis: List of node's interfaces PCI addresses.
237 :param mtu: MTU to set. Default: 9200.
241 :raises RuntimeError: If failed to set MTU on interface.
243 for pf_pci in pf_pcis:
244 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
245 cmd = f"ip link set {pf_eth} mtu {mtu}"
246 exec_cmd_no_error(node, cmd, sudo=True)
249 def set_interface_flow_control(node, pf_pcis, rx=u"off", tx=u"off"):
250 """Set Ethernet flow control for specified interfaces.
252 :param node: Topology node.
253 :param pf_pcis: List of node's interfaces PCI addresses.
254 :param rx: RX flow. Default: off.
255 :param tx: TX flow. Default: off.
261 for pf_pci in pf_pcis:
262 pf_eth = InterfaceUtil.pci_to_eth(node, pf_pci)
263 cmd = f"ethtool -A {pf_eth} rx off tx off"
264 ret_code, _, _ = exec_cmd(node, cmd, sudo=True)
265 if int(ret_code) not in (0, 78):
266 raise RuntimeError("Failed to set MTU on {pf_eth}!")
270 def set_pci_parameter(node, pf_pcis, key, value):
271 """Set PCI parameter for specified interfaces.
273 :param node: Topology node.
274 :param pf_pcis: List of node's interfaces PCI addresses.
275 :param key: Key to set.
276 :param value: Value to set.
282 for pf_pci in pf_pcis:
283 cmd = f"setpci -s {pf_pci} {key}={value}"
284 exec_cmd_no_error(node, cmd, sudo=True)
287 def vpp_set_interface_mtu(node, interface, mtu=9200):
288 """Set Ethernet MTU on interface.
290 :param node: VPP node.
291 :param interface: Interface to setup MTU. Default: 9200.
292 :param mtu: Ethernet MTU size in Bytes.
294 :type interface: str or int
297 if isinstance(interface, str):
298 sw_if_index = Topology.get_interface_sw_index(node, interface)
300 sw_if_index = interface
302 cmd = u"hw_interface_set_mtu"
303 err_msg = f"Failed to set interface MTU on host {node[u'host']}"
305 sw_if_index=sw_if_index,
309 with PapiSocketExecutor(node) as papi_exec:
310 papi_exec.add(cmd, **args).get_reply(err_msg)
311 except AssertionError as err:
312 # TODO: Make failure tolerance optional.
313 logger.debug(f"Setting MTU failed. Expected?\n{err}")
316 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
317 """Set Ethernet MTU on all interfaces.
319 :param node: VPP node.
320 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
324 for interface in node[u"interfaces"]:
325 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
328 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
329 """Set Ethernet MTU on all interfaces on all DUTs.
331 :param nodes: VPP nodes.
332 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
336 for node in nodes.values():
337 if node[u"type"] == NodeType.DUT:
338 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
341 def vpp_node_interfaces_ready_wait(node, retries=15):
342 """Wait until all interfaces with admin-up are in link-up state.
344 :param node: Node to wait on.
345 :param retries: Number of retries to check interface status (optional,
350 :raises RuntimeError: If any interface is not in link-up state after
351 defined number of retries.
353 for _ in range(0, retries):
355 out = InterfaceUtil.vpp_get_interface_data(node)
356 for interface in out:
357 if interface.get(u"flags") == 1:
358 not_ready.append(interface.get(u"interface_name"))
361 f"Interfaces still not in link-up state:\n{not_ready}"
367 err = f"Timeout, interfaces not up:\n{not_ready}" \
368 if u"not_ready" in locals() else u"No check executed!"
369 raise RuntimeError(err)
372 def all_vpp_interfaces_ready_wait(nodes, retries=15):
373 """Wait until all interfaces with admin-up are in link-up state for all
374 nodes in the topology.
376 :param nodes: Nodes in the topology.
377 :param retries: Number of retries to check interface status (optional,
383 for node in nodes.values():
384 if node[u"type"] == NodeType.DUT:
385 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
388 def vpp_get_interface_data(node, interface=None):
389 """Get all interface data from a VPP node. If a name or
390 sw_interface_index is provided, return only data for the matching
393 :param node: VPP node to get interface data from.
394 :param interface: Numeric index or name string of a specific interface.
396 :type interface: int or str
397 :returns: List of dictionaries containing data for each interface, or a
398 single dictionary for the specified interface.
400 :raises TypeError: if the data type of interface is neither basestring
403 def process_if_dump(if_dump):
404 """Process interface dump.
406 :param if_dump: Interface dump.
408 :returns: Processed interface dump.
411 if_dump[u"l2_address"] = str(if_dump[u"l2_address"])
412 if_dump[u"b_dmac"] = str(if_dump[u"b_dmac"])
413 if_dump[u"b_smac"] = str(if_dump[u"b_smac"])
414 if_dump[u"flags"] = if_dump[u"flags"].value
415 if_dump[u"type"] = if_dump[u"type"].value
416 if_dump[u"link_duplex"] = if_dump[u"link_duplex"].value
417 if_dump[u"sub_if_flags"] = if_dump[u"sub_if_flags"].value \
418 if hasattr(if_dump[u"sub_if_flags"], u"value") \
419 else int(if_dump[u"sub_if_flags"])
423 if interface is not None:
424 if isinstance(interface, str):
425 param = u"interface_name"
426 elif isinstance(interface, int):
427 param = u"sw_if_index"
429 raise TypeError(f"Wrong interface format {interface}")
433 cmd = u"sw_interface_dump"
435 name_filter_valid=False,
438 err_msg = f"Failed to get interface dump on host {node[u'host']}"
440 with PapiSocketExecutor(node) as papi_exec:
441 details = papi_exec.add(cmd, **args).get_details(err_msg)
442 logger.debug(f"Received data:\n{details!r}")
444 data = list() if interface is None else dict()
446 if interface is None:
447 data.append(process_if_dump(dump))
448 elif str(dump.get(param)).rstrip(u"\x00") == str(interface):
449 data = process_if_dump(dump)
452 logger.debug(f"Interface data:\n{data}")
456 def vpp_get_interface_name(node, sw_if_index):
457 """Get interface name for the given SW interface index from actual
460 :param node: VPP node to get interface data from.
461 :param sw_if_index: SW interface index of the specific interface.
463 :type sw_if_index: int
464 :returns: Name of the given interface.
467 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
468 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
469 if_data = InterfaceUtil.vpp_get_interface_data(
470 node, if_data[u"sup_sw_if_index"]
473 return if_data.get(u"interface_name")
476 def vpp_get_interface_sw_index(node, interface_name):
477 """Get interface name for the given SW interface index from actual
480 :param node: VPP node to get interface data from.
481 :param interface_name: Interface name.
483 :type interface_name: str
484 :returns: Name of the given interface.
487 if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
489 return if_data.get(u"sw_if_index")
492 def vpp_get_interface_mac(node, interface):
493 """Get MAC address for the given interface from actual interface dump.
495 :param node: VPP node to get interface data from.
496 :param interface: Numeric index or name string of a specific interface.
498 :type interface: int or str
499 :returns: MAC address.
502 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
503 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
504 if_data = InterfaceUtil.vpp_get_interface_data(
505 node, if_data[u"sup_sw_if_index"])
507 return if_data.get(u"l2_address")
510 def vpp_set_interface_mac(node, interface, mac):
511 """Set MAC address for the given interface.
513 :param node: VPP node to set interface MAC.
514 :param interface: Numeric index or name string of a specific interface.
515 :param mac: Required MAC address.
517 :type interface: int or str
520 cmd = u"sw_interface_set_mac_address"
522 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
523 mac_address=L2Util.mac_to_bin(mac)
525 err_msg = f"Failed to set MAC address of interface {interface}" \
526 f"on host {node[u'host']}"
527 with PapiSocketExecutor(node) as papi_exec:
528 papi_exec.add(cmd, **args).get_reply(err_msg)
531 def tg_set_interface_driver(node, pci_addr, driver):
532 """Set interface driver on the TG node.
534 :param node: Node to set interface driver on (must be TG node).
535 :param pci_addr: PCI address of the interface.
536 :param driver: Driver name.
540 :raises RuntimeError: If unbinding from the current driver fails.
541 :raises RuntimeError: If binding to the new driver fails.
543 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
544 if old_driver == driver:
550 # Unbind from current driver
551 if old_driver is not None:
552 cmd = f"sh -c \"echo {pci_addr} > " \
553 f"/sys/bus/pci/drivers/{old_driver}/unbind\""
554 ret_code, _, _ = ssh.exec_command_sudo(cmd)
555 if int(ret_code) != 0:
556 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
558 # Bind to the new driver
559 cmd = f"sh -c \"echo {pci_addr} > /sys/bus/pci/drivers/{driver}/bind\""
560 ret_code, _, _ = ssh.exec_command_sudo(cmd)
561 if int(ret_code) != 0:
562 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
565 def tg_get_interface_driver(node, pci_addr):
566 """Get interface driver from the TG node.
568 :param node: Node to get interface driver on (must be TG node).
569 :param pci_addr: PCI address of the interface.
572 :returns: Interface driver or None if not found.
574 :raises RuntimeError: If PCI rescan or lspci command execution failed.
576 return DUTSetup.get_pci_dev_driver(node, pci_addr)
579 def tg_set_interfaces_default_driver(node):
580 """Set interfaces default driver specified in topology yaml file.
582 :param node: Node to setup interfaces driver on (must be TG node).
585 for interface in node[u"interfaces"].values():
586 InterfaceUtil.tg_set_interface_driver(
587 node, interface[u"pci_address"], interface[u"driver"]
591 def update_vpp_interface_data_on_node(node):
592 """Update vpp generated interface data for a given node in DICT__nodes.
594 Updates interface names, software if index numbers and any other details
595 generated specifically by vpp that are unknown before testcase run.
596 It does this by dumping interface list from all devices using python
597 api, and pairing known information from topology (mac address) to state
600 :param node: Node selected from DICT__nodes.
603 interface_list = InterfaceUtil.vpp_get_interface_data(node)
604 interface_dict = dict()
605 for ifc in interface_list:
606 interface_dict[ifc[u"l2_address"]] = ifc
608 for if_name, if_data in node[u"interfaces"].items():
609 ifc_dict = interface_dict.get(if_data[u"mac_address"])
610 if ifc_dict is not None:
611 if_data[u"name"] = ifc_dict[u"interface_name"]
612 if_data[u"vpp_sw_index"] = ifc_dict[u"sw_if_index"]
613 if_data[u"mtu"] = ifc_dict[u"mtu"][0]
615 f"Interface {if_name} found by MAC "
616 f"{if_data[u'mac_address']}"
620 f"Interface {if_name} not found by MAC "
621 f"{if_data[u'mac_address']}"
623 if_data[u"vpp_sw_index"] = None
626 def update_nic_interface_names(node):
627 """Update interface names based on nic type and PCI address.
629 This method updates interface names in the same format as VPP does.
631 :param node: Node dictionary.
634 for ifc in node[u"interfaces"].values():
635 if_pci = ifc[u"pci_address"].replace(u".", u":").split(u":")
636 loc = f"{int(if_pci[1], 16):x}/{int(if_pci[2], 16):x}/" \
637 f"{int(if_pci[3], 16):x}"
638 if ifc[u"model"] == u"Intel-XL710":
639 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
640 elif ifc[u"model"] == u"Intel-X710":
641 ifc[u"name"] = f"TenGigabitEthernet{loc}"
642 elif ifc[u"model"] == u"Intel-X520-DA2":
643 ifc[u"name"] = f"TenGigabitEthernet{loc}"
644 elif ifc[u"model"] == u"Cisco-VIC-1385":
645 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
646 elif ifc[u"model"] == u"Cisco-VIC-1227":
647 ifc[u"name"] = f"TenGigabitEthernet{loc}"
649 ifc[u"name"] = f"UnknownEthernet{loc}"
652 def update_nic_interface_names_on_all_duts(nodes):
653 """Update interface names based on nic type and PCI address on all DUTs.
655 This method updates interface names in the same format as VPP does.
657 :param nodes: Topology nodes.
660 for node in nodes.values():
661 if node[u"type"] == NodeType.DUT:
662 InterfaceUtil.update_nic_interface_names(node)
665 def update_tg_interface_data_on_node(node):
666 """Update interface name for TG/linux node in DICT__nodes.
669 # for dev in `ls /sys/class/net/`;
670 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
671 "52:54:00:9f:82:63": "eth0"
672 "52:54:00:77:ae:a9": "eth1"
673 "52:54:00:e1:8a:0f": "eth2"
674 "00:00:00:00:00:00": "lo"
676 :param node: Node selected from DICT__nodes.
678 :raises RuntimeError: If getting of interface name and MAC fails.
680 # First setup interface driver specified in yaml file
681 InterfaceUtil.tg_set_interfaces_default_driver(node)
683 # Get interface names
687 cmd = u'for dev in `ls /sys/class/net/`; do echo "\\"`cat ' \
688 u'/sys/class/net/$dev/address`\\": \\"$dev\\""; done;'
690 ret_code, stdout, _ = ssh.exec_command(cmd)
691 if int(ret_code) != 0:
692 raise RuntimeError(u"Get interface name and MAC failed")
693 tmp = u"{" + stdout.rstrip().replace(u"\n", u",") + u"}"
695 interfaces = JsonParser().parse_data(tmp)
696 for interface in node[u"interfaces"].values():
697 name = interfaces.get(interface[u"mac_address"])
700 interface[u"name"] = name
703 def iface_update_numa_node(node):
704 """For all interfaces from topology file update numa node based on
705 information from the node.
707 :param node: Node from topology.
710 :raises ValueError: If numa node ia less than 0.
711 :raises RuntimeError: If update of numa node failed.
714 for if_key in Topology.get_node_interfaces(node):
715 if_pci = Topology.get_interface_pci_addr(node, if_key)
717 cmd = f"cat /sys/bus/pci/devices/{if_pci}/numa_node"
719 ret, out, _ = ssh.exec_command(cmd)
722 numa_node = 0 if int(out) < 0 else int(out)
725 f"Reading numa location failed for: {if_pci}"
728 Topology.set_interface_numa_node(
729 node, if_key, numa_node
733 raise RuntimeError(f"Update numa node failed for: {if_pci}")
736 def update_all_interface_data_on_all_nodes(
737 nodes, skip_tg=False, skip_vpp=False):
738 """Update interface names on all nodes in DICT__nodes.
740 This method updates the topology dictionary by querying interface lists
741 of all nodes mentioned in the topology dictionary.
743 :param nodes: Nodes in the topology.
744 :param skip_tg: Skip TG node.
745 :param skip_vpp: Skip VPP node.
750 for node in nodes.values():
751 if node[u"type"] == NodeType.DUT and not skip_vpp:
752 InterfaceUtil.update_vpp_interface_data_on_node(node)
753 elif node[u"type"] == NodeType.TG and not skip_tg:
754 InterfaceUtil.update_tg_interface_data_on_node(node)
755 InterfaceUtil.iface_update_numa_node(node)
758 def create_vlan_subinterface(node, interface, vlan):
759 """Create VLAN sub-interface on node.
761 :param node: Node to add VLAN subinterface on.
762 :param interface: Interface name or index on which create VLAN
764 :param vlan: VLAN ID of the subinterface to be created.
766 :type interface: str on int
768 :returns: Name and index of created subinterface.
770 :raises RuntimeError: if it is unable to create VLAN subinterface on the
771 node or interface cannot be converted.
773 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
775 cmd = u"create_vlan_subif"
777 sw_if_index=sw_if_index,
780 err_msg = f"Failed to create VLAN sub-interface on host {node[u'host']}"
782 with PapiSocketExecutor(node) as papi_exec:
783 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
785 if_key = Topology.add_new_port(node, u"vlan_subif")
786 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
787 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
788 Topology.update_interface_name(node, if_key, ifc_name)
790 return f"{interface}.{vlan}", sw_if_index
793 def create_vxlan_interface(node, vni, source_ip, destination_ip):
794 """Create VXLAN interface and return sw if index of created interface.
796 :param node: Node where to create VXLAN interface.
797 :param vni: VXLAN Network Identifier.
798 :param source_ip: Source IP of a VXLAN Tunnel End Point.
799 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
803 :type destination_ip: str
804 :returns: SW IF INDEX of created interface.
806 :raises RuntimeError: if it is unable to create VxLAN interface on the
809 cmd = u"vxlan_add_del_tunnel"
812 instance=Constants.BITWISE_NON_ZERO,
813 src_address=IPAddress.create_ip_address_object(
814 ip_address(source_ip)
816 dst_address=IPAddress.create_ip_address_object(
817 ip_address(destination_ip)
819 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
821 decap_next_index=Constants.BITWISE_NON_ZERO,
824 err_msg = f"Failed to create VXLAN tunnel interface " \
825 f"on host {node[u'host']}"
826 with PapiSocketExecutor(node) as papi_exec:
827 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
829 if_key = Topology.add_new_port(node, u"vxlan_tunnel")
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)
837 def set_vxlan_bypass(node, interface=None):
838 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
840 By adding the IPv4 vxlan-bypass graph node to an interface, the node
841 checks for and validate input vxlan packet and bypass ip4-lookup,
842 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
843 This node will cause extra overhead to for non-vxlan packets which is
846 :param node: Node where to set VXLAN bypass.
847 :param interface: Numeric index or name string of a specific interface.
849 :type interface: int or str
850 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
852 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
854 cmd = u"sw_interface_set_vxlan_bypass"
857 sw_if_index=sw_if_index,
860 err_msg = f"Failed to set VXLAN bypass on interface " \
861 f"on host {node[u'host']}"
862 with PapiSocketExecutor(node) as papi_exec:
863 papi_exec.add(cmd, **args).get_replies(err_msg)
866 def vxlan_dump(node, interface=None):
867 """Get VxLAN data for the given interface.
869 :param node: VPP node to get interface data from.
870 :param interface: Numeric index or name string of a specific interface.
871 If None, information about all VxLAN interfaces is returned.
873 :type interface: int or str
874 :returns: Dictionary containing data for the given VxLAN interface or if
875 interface=None, the list of dictionaries with all VxLAN interfaces.
877 :raises TypeError: if the data type of interface is neither basestring
880 def process_vxlan_dump(vxlan_dump):
881 """Process vxlan dump.
883 :param vxlan_dump: Vxlan interface dump.
884 :type vxlan_dump: dict
885 :returns: Processed vxlan interface dump.
888 vxlan_dump[u"src_address"] = str(vxlan_dump[u"src_address"])
889 vxlan_dump[u"dst_address"] = str(vxlan_dump[u"dst_address"])
892 if interface is not None:
893 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
895 sw_if_index = int(Constants.BITWISE_NON_ZERO)
897 cmd = u"vxlan_tunnel_dump"
899 sw_if_index=sw_if_index
901 err_msg = f"Failed to get VXLAN dump on host {node[u'host']}"
903 with PapiSocketExecutor(node) as papi_exec:
904 details = papi_exec.add(cmd, **args).get_details(err_msg)
906 data = list() if interface is None else dict()
908 if interface is None:
909 data.append(process_vxlan_dump(dump))
910 elif dump[u"sw_if_index"] == sw_if_index:
911 data = process_vxlan_dump(dump)
914 logger.debug(f"VXLAN data:\n{data}")
918 def create_subinterface(
919 node, interface, sub_id, outer_vlan_id=None, inner_vlan_id=None,
921 """Create sub-interface on node. It is possible to set required
922 sub-interface type and VLAN tag(s).
924 :param node: Node to add sub-interface.
925 :param interface: Interface name on which create sub-interface.
926 :param sub_id: ID of the sub-interface to be created.
927 :param outer_vlan_id: Optional outer VLAN ID.
928 :param inner_vlan_id: Optional inner VLAN ID.
929 :param type_subif: Optional type of sub-interface. Values supported by
930 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
933 :type interface: str or int
935 :type outer_vlan_id: int
936 :type inner_vlan_id: int
937 :type type_subif: str
938 :returns: Name and index of created sub-interface.
940 :raises RuntimeError: If it is not possible to create sub-interface.
942 subif_types = type_subif.split()
945 if u"no_tags" in subif_types:
946 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
947 if u"one_tag" in subif_types:
948 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
949 if u"two_tags" in subif_types:
950 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
951 if u"dot1ad" in subif_types:
952 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
953 if u"exact_match" in subif_types:
954 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
955 if u"default_sub" in subif_types:
956 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
957 if type_subif == u"default_sub":
958 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
959 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
961 cmd = u"create_subif"
963 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
965 sub_if_flags=flags.value if hasattr(flags, u"value")
967 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
968 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
970 err_msg = f"Failed to create sub-interface on host {node[u'host']}"
971 with PapiSocketExecutor(node) as papi_exec:
972 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
974 if_key = Topology.add_new_port(node, u"subinterface")
975 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
976 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
977 Topology.update_interface_name(node, if_key, ifc_name)
979 return f"{interface}.{sub_id}", sw_if_index
982 def create_gre_tunnel_interface(node, source_ip, destination_ip):
983 """Create GRE tunnel interface on node.
985 :param node: VPP node to add tunnel interface.
986 :param source_ip: Source of the GRE tunnel.
987 :param destination_ip: Destination of the GRE tunnel.
990 :type destination_ip: str
991 :returns: Name and index of created GRE tunnel interface.
993 :raises RuntimeError: If unable to create GRE tunnel interface.
995 cmd = u"gre_tunnel_add_del"
998 instance=Constants.BITWISE_NON_ZERO,
1000 dst=str(destination_ip),
1008 err_msg = f"Failed to create GRE tunnel interface " \
1009 f"on host {node[u'host']}"
1010 with PapiSocketExecutor(node) as papi_exec:
1011 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1013 if_key = Topology.add_new_port(node, u"gre_tunnel")
1014 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1015 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1016 Topology.update_interface_name(node, if_key, ifc_name)
1018 return ifc_name, sw_if_index
1021 def vpp_create_loopback(node, mac=None):
1022 """Create loopback interface on VPP node.
1024 :param node: Node to create loopback interface on.
1025 :param mac: Optional MAC address for loopback interface.
1028 :returns: SW interface index.
1030 :raises RuntimeError: If it is not possible to create loopback on the
1033 cmd = u"create_loopback_instance"
1035 mac_address=L2Util.mac_to_bin(mac) if mac else 0,
1039 err_msg = f"Failed to create loopback interface on host {node[u'host']}"
1040 with PapiSocketExecutor(node) as papi_exec:
1041 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1043 if_key = Topology.add_new_port(node, u"loopback")
1044 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1045 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1046 Topology.update_interface_name(node, if_key, ifc_name)
1048 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1049 Topology.update_interface_mac_address(node, if_key, mac)
1054 def vpp_create_bond_interface(
1055 node, mode, load_balance=None, mac=None, gso=False):
1056 """Create bond interface on VPP node.
1058 :param node: DUT node from topology.
1059 :param mode: Link bonding mode.
1060 :param load_balance: Load balance (optional, valid for xor and lacp
1061 modes, otherwise ignored). Default: None.
1062 :param mac: MAC address to assign to the bond interface (optional).
1064 :param gso: Enable GSO support (optional). Default: False.
1067 :type load_balance: str
1070 :returns: Interface key (name) in topology.
1072 :raises RuntimeError: If it is not possible to create bond interface on
1075 cmd = u"bond_create2"
1077 id=int(Constants.BITWISE_NON_ZERO),
1078 use_custom_mac=bool(mac is not None),
1079 mac_address=L2Util.mac_to_bin(mac) if mac else None,
1082 f"BOND_API_MODE_{mode.replace(u'-', u'_').upper()}"
1084 lb=0 if load_balance is None else getattr(
1085 LinkBondLoadBalanceAlgo,
1086 f"BOND_API_LB_ALGO_{load_balance.upper()}"
1091 err_msg = f"Failed to create bond interface on host {node[u'host']}"
1092 with PapiSocketExecutor(node) as papi_exec:
1093 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1095 InterfaceUtil.add_eth_interface(
1096 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_bond"
1098 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1103 def add_eth_interface(
1104 node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
1106 """Add ethernet interface to current topology.
1108 :param node: DUT node from topology.
1109 :param ifc_name: Name of the interface.
1110 :param sw_if_index: SW interface index.
1111 :param ifc_pfx: Interface key prefix.
1112 :param host_if_key: Host interface key from topology file.
1115 :type sw_if_index: int
1117 :type host_if_key: str
1119 if_key = Topology.add_new_port(node, ifc_pfx)
1121 if ifc_name and sw_if_index is None:
1122 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1124 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1125 if sw_if_index and ifc_name is None:
1126 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1127 Topology.update_interface_name(node, if_key, ifc_name)
1128 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1129 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1130 if host_if_key is not None:
1131 Topology.set_interface_numa_node(
1132 node, if_key, Topology.get_interface_numa_node(
1136 Topology.update_interface_pci_address(
1137 node, if_key, Topology.get_interface_pci_addr(node, host_if_key)
1141 def vpp_create_avf_interface(
1142 node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0):
1143 """Create AVF interface on VPP node.
1145 :param node: DUT node from topology.
1146 :param if_key: Interface key from topology file of interface
1147 to be bound to i40evf driver.
1148 :param num_rx_queues: Number of RX queues.
1149 :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1150 :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1153 :type num_rx_queues: int
1156 :returns: AVF interface key (name) in topology.
1158 :raises RuntimeError: If it is not possible to create AVF interface on
1161 PapiSocketExecutor.run_cli_cmd(
1162 node, u"set logging class avf level debug"
1166 vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
1168 pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1170 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1174 err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1175 with PapiSocketExecutor(node) as papi_exec:
1176 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1178 InterfaceUtil.add_eth_interface(
1179 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
1183 return Topology.get_interface_by_sw_index(node, sw_if_index)
1186 def vpp_create_rdma_interface(
1187 node, if_key, num_rx_queues=None, rxq_size=0, txq_size=0,
1189 """Create RDMA interface on VPP node.
1191 :param node: DUT node from topology.
1192 :param if_key: Physical interface key from topology file of interface
1193 to be bound to rdma-core driver.
1194 :param num_rx_queues: Number of RX queues.
1195 :param rxq_size: Size of RXQ (0 = Default API; 512 = Default VPP).
1196 :param txq_size: Size of TXQ (0 = Default API; 512 = Default VPP).
1197 :param mode: RDMA interface mode - auto/ibv/dv.
1200 :type num_rx_queues: int
1204 :returns: Interface key (name) in topology file.
1206 :raises RuntimeError: If it is not possible to create RDMA interface on
1209 PapiSocketExecutor.run_cli_cmd(
1210 node, u"set logging class rdma level debug"
1213 cmd = u"rdma_create_v2"
1214 pci_addr = Topology.get_interface_pci_addr(node, if_key)
1216 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1217 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1218 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1221 mode=getattr(RdmaMode, f"RDMA_API_MODE_{mode.upper()}").value,
1222 # TODO: Set True for non-jumbo packets.
1226 err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1227 with PapiSocketExecutor(node) as papi_exec:
1228 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1230 InterfaceUtil.vpp_set_interface_mac(
1231 node, sw_if_index, Topology.get_interface_mac(node, if_key)
1233 InterfaceUtil.add_eth_interface(
1234 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
1238 return Topology.get_interface_by_sw_index(node, sw_if_index)
1241 def vpp_add_bond_member(node, interface, bond_if):
1242 """Add member interface to bond interface on VPP node.
1244 :param node: DUT node from topology.
1245 :param interface: Physical interface key from topology file.
1246 :param bond_if: Load balance
1248 :type interface: str
1250 :raises RuntimeError: If it is not possible to add member to bond
1251 interface on the node.
1253 cmd = u"bond_add_member"
1255 sw_if_index=Topology.get_interface_sw_index(node, interface),
1256 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1258 is_long_timeout=False
1260 err_msg = f"Failed to add member {interface} to bond interface " \
1261 f"{bond_if} on host {node[u'host']}"
1262 with PapiSocketExecutor(node) as papi_exec:
1263 papi_exec.add(cmd, **args).get_reply(err_msg)
1266 def vpp_show_bond_data_on_node(node, verbose=False):
1267 """Show (detailed) bond information on VPP node.
1269 :param node: DUT node from topology.
1270 :param verbose: If detailed information is required or not.
1274 cmd = u"sw_bond_interface_dump"
1275 err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1277 data = f"Bond data on node {node[u'host']}:\n"
1278 with PapiSocketExecutor(node) as papi_exec:
1279 details = papi_exec.add(cmd).get_details(err_msg)
1281 for bond in details:
1282 data += f"{bond[u'interface_name']}\n"
1283 data += u" mode: {m}\n".format(
1284 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1286 data += u" load balance: {lb}\n".format(
1287 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1289 data += f" number of active members: {bond[u'active_members']}\n"
1291 member_data = InterfaceUtil.vpp_bond_member_dump(
1292 node, Topology.get_interface_by_sw_index(
1293 node, bond[u"sw_if_index"]
1296 for member in member_data:
1297 if not member[u"is_passive"]:
1298 data += f" {member[u'interface_name']}\n"
1299 data += f" number of members: {bond[u'members']}\n"
1301 for member in member_data:
1302 data += f" {member[u'interface_name']}\n"
1303 data += f" interface id: {bond[u'id']}\n"
1304 data += f" sw_if_index: {bond[u'sw_if_index']}\n"
1308 def vpp_bond_member_dump(node, interface):
1309 """Get bond interface slave(s) data on VPP node.
1311 :param node: DUT node from topology.
1312 :param interface: Physical interface key from topology file.
1314 :type interface: str
1315 :returns: Bond slave interface data.
1318 cmd = u"sw_member_interface_dump"
1320 sw_if_index=Topology.get_interface_sw_index(node, interface)
1322 err_msg = f"Failed to get slave dump on host {node[u'host']}"
1324 with PapiSocketExecutor(node) as papi_exec:
1325 details = papi_exec.add(cmd, **args).get_details(err_msg)
1327 logger.debug(f"Member data:\n{details}")
1331 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1332 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1334 :param nodes: Nodes in the topology.
1335 :param verbose: If detailed information is required or not.
1339 for node_data in nodes.values():
1340 if node_data[u"type"] == NodeType.DUT:
1341 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1344 def vpp_enable_input_acl_interface(
1345 node, interface, ip_version, table_index):
1346 """Enable input acl on interface.
1348 :param node: VPP node to setup interface for input acl.
1349 :param interface: Interface to setup input acl.
1350 :param ip_version: Version of IP protocol.
1351 :param table_index: Classify table index.
1353 :type interface: str or int
1354 :type ip_version: str
1355 :type table_index: int
1357 cmd = u"input_acl_set_interface"
1359 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1360 ip4_table_index=table_index if ip_version == u"ip4"
1361 else Constants.BITWISE_NON_ZERO,
1362 ip6_table_index=table_index if ip_version == u"ip6"
1363 else Constants.BITWISE_NON_ZERO,
1364 l2_table_index=table_index if ip_version == u"l2"
1365 else Constants.BITWISE_NON_ZERO,
1367 err_msg = f"Failed to enable input acl on interface {interface}"
1368 with PapiSocketExecutor(node) as papi_exec:
1369 papi_exec.add(cmd, **args).get_reply(err_msg)
1372 def get_interface_classify_table(node, interface):
1373 """Get name of classify table for the given interface.
1375 TODO: Move to Classify.py.
1377 :param node: VPP node to get data from.
1378 :param interface: Name or sw_if_index of a specific interface.
1380 :type interface: str or int
1381 :returns: Classify table name.
1384 if isinstance(interface, str):
1385 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1387 sw_if_index = interface
1389 cmd = u"classify_table_by_interface"
1391 sw_if_index=sw_if_index
1393 err_msg = f"Failed to get classify table name by interface {interface}"
1394 with PapiSocketExecutor(node) as papi_exec:
1395 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1400 def get_sw_if_index(node, interface_name):
1401 """Get sw_if_index for the given interface from actual interface dump.
1403 FIXME: Delete and redirect callers to vpp_get_interface_sw_index.
1405 :param node: VPP node to get interface data from.
1406 :param interface_name: Name of the specific interface.
1408 :type interface_name: str
1409 :returns: sw_if_index of the given interface.
1412 interface_data = InterfaceUtil.vpp_get_interface_data(
1413 node, interface=interface_name
1415 return interface_data.get(u"sw_if_index")
1418 def vxlan_gpe_dump(node, interface_name=None):
1419 """Get VxLAN GPE data for the given interface.
1421 :param node: VPP node to get interface data from.
1422 :param interface_name: Name of the specific interface. If None,
1423 information about all VxLAN GPE interfaces is returned.
1425 :type interface_name: str
1426 :returns: Dictionary containing data for the given VxLAN GPE interface
1427 or if interface=None, the list of dictionaries with all VxLAN GPE
1429 :rtype: dict or list
1431 def process_vxlan_gpe_dump(vxlan_dump):
1432 """Process vxlan_gpe dump.
1434 :param vxlan_dump: Vxlan_gpe nterface dump.
1435 :type vxlan_dump: dict
1436 :returns: Processed vxlan_gpe interface dump.
1439 if vxlan_dump[u"is_ipv6"]:
1440 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1441 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1443 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1444 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1447 if interface_name is not None:
1448 sw_if_index = InterfaceUtil.get_interface_index(
1449 node, interface_name
1452 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1454 cmd = u"vxlan_gpe_tunnel_dump"
1456 sw_if_index=sw_if_index
1458 err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1459 with PapiSocketExecutor(node) as papi_exec:
1460 details = papi_exec.add(cmd, **args).get_details(err_msg)
1462 data = list() if interface_name is None else dict()
1463 for dump in details:
1464 if interface_name is None:
1465 data.append(process_vxlan_gpe_dump(dump))
1466 elif dump[u"sw_if_index"] == sw_if_index:
1467 data = process_vxlan_gpe_dump(dump)
1470 logger.debug(f"VXLAN-GPE data:\n{data}")
1474 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1475 """Assign VPP interface to specific VRF/FIB table.
1477 :param node: VPP node where the FIB and interface are located.
1478 :param interface: Interface to be assigned to FIB.
1479 :param table_id: VRF table ID.
1480 :param ipv6: Assign to IPv6 table. Default False.
1482 :type interface: str or int
1486 cmd = u"sw_interface_set_table"
1488 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1490 vrf_id=int(table_id)
1492 err_msg = f"Failed to assign interface {interface} to FIB table"
1493 with PapiSocketExecutor(node) as papi_exec:
1494 papi_exec.add(cmd, **args).get_reply(err_msg)
1497 def set_linux_interface_mac(
1498 node, interface, mac, namespace=None, vf_id=None):
1499 """Set MAC address for interface in linux.
1501 :param node: Node where to execute command.
1502 :param interface: Interface in namespace.
1503 :param mac: MAC to be assigned to interface.
1504 :param namespace: Execute command in namespace. Optional
1505 :param vf_id: Virtual Function id. Optional
1507 :type interface: str
1509 :type namespace: str
1512 mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1513 else f"address {mac}"
1514 ns_str = f"ip netns exec {namespace}" if namespace else u""
1516 cmd = f"{ns_str} ip link set {interface} {mac_str}"
1517 exec_cmd_no_error(node, cmd, sudo=True)
1520 def set_linux_interface_trust_on(
1521 node, interface, namespace=None, vf_id=None):
1522 """Set trust on (promisc) for interface in linux.
1524 :param node: Node where to execute command.
1525 :param interface: Interface in namespace.
1526 :param namespace: Execute command in namespace. Optional
1527 :param vf_id: Virtual Function id. Optional
1529 :type interface: str
1530 :type namespace: str
1533 trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1534 ns_str = f"ip netns exec {namespace}" if namespace else u""
1536 cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1537 exec_cmd_no_error(node, cmd, sudo=True)
1540 def set_linux_interface_spoof_off(
1541 node, interface, namespace=None, vf_id=None):
1542 """Set spoof off for interface in linux.
1544 :param node: Node where to execute command.
1545 :param interface: Interface in namespace.
1546 :param namespace: Execute command in namespace. Optional
1547 :param vf_id: Virtual Function id. Optional
1549 :type interface: str
1550 :type namespace: str
1553 spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1555 ns_str = f"ip netns exec {namespace}" if namespace else u""
1557 cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1558 exec_cmd_no_error(node, cmd, sudo=True)
1561 def set_linux_interface_state(
1562 node, interface, namespace=None, state=u"up"):
1563 """Set operational state for interface in linux.
1565 :param node: Node where to execute command.
1566 :param interface: Interface in namespace.
1567 :param namespace: Execute command in namespace. Optional
1568 :param state: Up/Down.
1570 :type interface: str
1571 :type namespace: str
1574 ns_str = f"ip netns exec {namespace}" if namespace else u""
1576 cmd = f"{ns_str} ip link set dev {interface} {state}"
1577 exec_cmd_no_error(node, cmd, sudo=True)
1580 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer=u"L2"):
1581 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1582 driver testing on DUT.
1584 :param node: DUT node.
1585 :param ifc_key: Interface key from topology file.
1586 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1587 :param osi_layer: OSI Layer type to initialize TG with.
1588 Default value "L2" sets linux interface spoof off.
1592 :type osi_layer: str
1593 :returns: Virtual Function topology interface keys.
1595 :raises RuntimeError: If a reason preventing initialization is found.
1597 # Read PCI address and driver.
1598 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1599 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1600 uio_driver = Topology.get_uio_driver(node)
1601 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1602 if kernel_driver not in (u"ice", u"iavf", u"i40e", u"i40evf"):
1604 f"AVF needs ice or i40e compatible driver, not {kernel_driver}"
1605 f"at node {node[u'host']} ifc {ifc_key}"
1607 current_driver = DUTSetup.get_pci_dev_driver(
1608 node, pf_pci_addr.replace(u":", r"\:"))
1610 VPPUtil.stop_vpp_service(node)
1611 if current_driver != kernel_driver:
1612 # PCI device must be re-bound to kernel driver before creating VFs.
1613 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1614 # Stop VPP to prevent deadlock.
1615 # Unbind from current driver.
1616 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1617 # Bind to kernel driver.
1618 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1620 # Initialize PCI VFs.
1621 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1624 # Set MAC address and bind each virtual function to uio driver.
1625 for vf_id in range(numvfs):
1626 vf_mac_addr = u":".join(
1627 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1628 pf_mac_addr[5], f"{vf_id:02x}"
1632 pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1633 InterfaceUtil.set_linux_interface_trust_on(
1634 node, pf_dev, vf_id=vf_id
1636 if osi_layer == u"L2":
1637 InterfaceUtil.set_linux_interface_spoof_off(
1638 node, pf_dev, vf_id=vf_id
1640 InterfaceUtil.set_linux_interface_mac(
1641 node, pf_dev, vf_mac_addr, vf_id=vf_id
1643 InterfaceUtil.set_linux_interface_state(
1644 node, pf_dev, state=u"up"
1647 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1648 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1650 # Add newly created ports into topology file
1651 vf_ifc_name = f"{ifc_key}_vif"
1652 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1653 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1654 Topology.update_interface_name(
1655 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1657 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1658 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1659 Topology.set_interface_numa_node(
1660 node, vf_ifc_key, Topology.get_interface_numa_node(
1664 vf_ifc_keys.append(vf_ifc_key)
1669 def vpp_sw_interface_rx_placement_dump(node):
1670 """Dump VPP interface RX placement on node.
1672 :param node: Node to run command on.
1674 :returns: Thread mapping information as a list of dictionaries.
1677 cmd = u"sw_interface_rx_placement_dump"
1678 err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1679 with PapiSocketExecutor(node) as papi_exec:
1680 for ifc in node[u"interfaces"].values():
1681 if ifc[u"vpp_sw_index"] is not None:
1682 papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1683 details = papi_exec.get_details(err_msg)
1684 return sorted(details, key=lambda k: k[u"sw_if_index"])
1687 def vpp_sw_interface_set_rx_placement(
1688 node, sw_if_index, queue_id, worker_id):
1689 """Set interface RX placement to worker on node.
1691 :param node: Node to run command on.
1692 :param sw_if_index: VPP SW interface index.
1693 :param queue_id: VPP interface queue ID.
1694 :param worker_id: VPP worker ID (indexing from 0).
1696 :type sw_if_index: int
1698 :type worker_id: int
1699 :raises RuntimeError: If failed to run command on host or if no API
1702 cmd = u"sw_interface_set_rx_placement"
1703 err_msg = f"Failed to set interface RX placement to worker " \
1704 f"on host {node[u'host']}!"
1706 sw_if_index=sw_if_index,
1708 worker_id=worker_id,
1711 with PapiSocketExecutor(node) as papi_exec:
1712 papi_exec.add(cmd, **args).get_reply(err_msg)
1715 def vpp_round_robin_rx_placement(
1716 node, prefix, dp_worker_limit=None
1718 """Set Round Robin interface RX placement on all worker threads
1721 If specified, dp_core_limit limits the number of physical cores used
1722 for data plane I/O work. Other cores are presumed to do something else,
1723 e.g. asynchronous crypto processing.
1724 None means all workers are used for data plane work.
1725 Note this keyword specifies workers, not cores.
1727 :param node: Topology nodes.
1728 :param prefix: Interface name prefix.
1729 :param dp_worker_limit: How many cores for data plane work.
1732 :type dp_worker_limit: Optional[int]
1735 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1736 if dp_worker_limit is not None:
1737 worker_cnt = min(worker_cnt, dp_worker_limit)
1740 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1741 for interface in node[u"interfaces"].values():
1742 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
1743 and prefix in interface[u"name"]:
1744 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1745 node, placement[u"sw_if_index"], placement[u"queue_id"],
1746 worker_id % worker_cnt
1751 def vpp_round_robin_rx_placement_on_all_duts(
1752 nodes, prefix, dp_core_limit=None
1754 """Set Round Robin interface RX placement on all worker threads
1757 If specified, dp_core_limit limits the number of physical cores used
1758 for data plane I/O work. Other cores are presumed to do something else,
1759 e.g. asynchronous crypto processing.
1760 None means all cores are used for data plane work.
1761 Note this keyword specifies cores, not workers.
1763 :param nodes: Topology nodes.
1764 :param prefix: Interface name prefix.
1765 :param dp_worker_limit: How many cores for data plane work.
1768 :type dp_worker_limit: Optional[int]
1770 for node in nodes.values():
1771 if node[u"type"] == NodeType.DUT:
1772 dp_worker_limit = CpuUtils.worker_count_from_cores_and_smt(
1773 phy_cores=dp_core_limit,
1774 smt_used=CpuUtils.is_smt_enabled(node[u"cpuinfo"]),
1776 InterfaceUtil.vpp_round_robin_rx_placement(
1777 node, prefix, dp_worker_limit