1 # Copyright (c) 2020 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
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.L2Util import L2Util
26 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
27 from resources.libraries.python.parsers.JsonParser import JsonParser
28 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
29 from resources.libraries.python.topology import NodeType, Topology
30 from resources.libraries.python.VPPUtil import VPPUtil
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
114 """General utilities for managing interfaces"""
117 def pci_to_int(pci_str):
118 """Convert PCI address from string format (0000:18:0a.0) to
119 integer representation (169345024).
121 :param pci_str: PCI address in string representation.
123 :returns: Integer representation of PCI address.
126 pci = list(pci_str.split(u":")[0:2])
127 pci.extend(pci_str.split(u":")[2].split(u"."))
129 return (int(pci[0], 16) | int(pci[1], 16) << 16 |
130 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
133 def pci_to_eth(node, pci_str):
134 """Convert PCI address on DUT to Linux ethernet name.
136 :param node: DUT node
137 :param pci_str: PCI address.
140 :returns: Ethernet name.
143 cmd = f"basename /sys/bus/pci/devices/{pci_str}/net/*"
145 stdout, _ = exec_cmd_no_error(node, cmd)
147 raise RuntimeError(f"Cannot convert {pci_str} to ethernet name!")
149 return stdout.strip()
152 def get_interface_index(node, interface):
153 """Get interface sw_if_index from topology file.
155 :param node: Node where the interface is.
156 :param interface: Numeric index or name string of a specific interface.
158 :type interface: str or int
159 :returns: SW interface index.
163 sw_if_index = int(interface)
165 sw_if_index = Topology.get_interface_sw_index(node, interface)
166 if sw_if_index is None:
168 Topology.get_interface_sw_index_by_name(node, interface)
169 except TypeError as err:
170 raise TypeError(f"Wrong interface format {interface}") from err
175 def set_interface_state(node, interface, state, if_type=u"key"):
176 """Set interface state on a node.
178 Function can be used for DUTs as well as for TGs.
180 :param node: Node where the interface is.
181 :param interface: Interface key or sw_if_index or name.
182 :param state: One of 'up' or 'down'.
183 :param if_type: Interface type
185 :type interface: str or int
189 :raises ValueError: If the interface type is unknown.
190 :raises ValueError: If the state of interface is unexpected.
191 :raises ValueError: If the node has an unknown node type.
193 if if_type == u"key":
194 if isinstance(interface, str):
195 sw_if_index = Topology.get_interface_sw_index(node, interface)
196 iface_name = Topology.get_interface_name(node, interface)
198 sw_if_index = interface
199 elif if_type == u"name":
200 iface_key = Topology.get_interface_by_name(node, interface)
201 if iface_key is not None:
202 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
203 iface_name = interface
205 raise ValueError(f"Unknown if_type: {if_type}")
207 if node[u"type"] == NodeType.DUT:
209 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
210 elif state == u"down":
213 raise ValueError(f"Unexpected interface state: {state}")
214 cmd = u"sw_interface_set_flags"
215 err_msg = f"Failed to set interface state on host {node[u'host']}"
217 sw_if_index=int(sw_if_index),
220 with PapiSocketExecutor(node) as papi_exec:
221 papi_exec.add(cmd, **args).get_reply(err_msg)
222 elif node[u"type"] == NodeType.TG or node[u"type"] == NodeType.VM:
223 cmd = f"ip link set {iface_name} {state}"
224 exec_cmd_no_error(node, cmd, sudo=True)
227 f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
231 def set_interface_ethernet_mtu(node, iface_key, mtu):
232 """Set Ethernet MTU for specified interface.
234 Function can be used only for TGs.
236 :param node: Node where the interface is.
237 :param iface_key: Interface key from topology file.
238 :param mtu: MTU to set.
243 :raises ValueError: If the node type is "DUT".
244 :raises ValueError: If the node has an unknown node type.
246 if node[u"type"] == NodeType.DUT:
247 msg = f"Node {node[u'host']}: Setting Ethernet MTU for interface " \
248 f"on DUT nodes not supported"
249 elif node[u"type"] != NodeType.TG:
250 msg = f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
252 iface_name = Topology.get_interface_name(node, iface_key)
253 cmd = f"ip link set {iface_name} mtu {mtu}"
254 exec_cmd_no_error(node, cmd, sudo=True)
256 raise ValueError(msg)
259 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
260 """Set default Ethernet MTU on all interfaces on node.
262 Function can be used only for TGs.
264 :param node: Node where to set default MTU.
268 for ifc in node[u"interfaces"]:
269 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
272 def vpp_set_interface_mtu(node, interface, mtu=9200):
273 """Set Ethernet MTU on interface.
275 :param node: VPP node.
276 :param interface: Interface to setup MTU. Default: 9200.
277 :param mtu: Ethernet MTU size in Bytes.
279 :type interface: str or int
282 if isinstance(interface, str):
283 sw_if_index = Topology.get_interface_sw_index(node, interface)
285 sw_if_index = interface
287 cmd = u"hw_interface_set_mtu"
288 err_msg = f"Failed to set interface MTU on host {node[u'host']}"
290 sw_if_index=sw_if_index,
294 with PapiSocketExecutor(node) as papi_exec:
295 papi_exec.add(cmd, **args).get_reply(err_msg)
296 except AssertionError as err:
297 # TODO: Make failure tolerance optional.
298 logger.debug(f"Setting MTU failed. Expected?\n{err}")
301 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
302 """Set Ethernet MTU on all interfaces.
304 :param node: VPP node.
305 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
309 for interface in node[u"interfaces"]:
310 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
313 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
314 """Set Ethernet MTU on all interfaces on all DUTs.
316 :param nodes: VPP nodes.
317 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
321 for node in nodes.values():
322 if node[u"type"] == NodeType.DUT:
323 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
326 def vpp_node_interfaces_ready_wait(node, retries=15):
327 """Wait until all interfaces with admin-up are in link-up state.
329 :param node: Node to wait on.
330 :param retries: Number of retries to check interface status (optional,
335 :raises RuntimeError: If any interface is not in link-up state after
336 defined number of retries.
338 for _ in range(0, retries):
340 out = InterfaceUtil.vpp_get_interface_data(node)
341 for interface in out:
342 if interface.get(u"flags") == 1:
343 not_ready.append(interface.get(u"interface_name"))
346 f"Interfaces still not in link-up state:\n{not_ready}"
352 err = f"Timeout, interfaces not up:\n{not_ready}" \
353 if u"not_ready" in locals() else u"No check executed!"
354 raise RuntimeError(err)
357 def all_vpp_interfaces_ready_wait(nodes, retries=15):
358 """Wait until all interfaces with admin-up are in link-up state for all
359 nodes in the topology.
361 :param nodes: Nodes in the topology.
362 :param retries: Number of retries to check interface status (optional,
368 for node in nodes.values():
369 if node[u"type"] == NodeType.DUT:
370 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
373 def vpp_get_interface_data(node, interface=None):
374 """Get all interface data from a VPP node. If a name or
375 sw_interface_index is provided, return only data for the matching
378 :param node: VPP node to get interface data from.
379 :param interface: Numeric index or name string of a specific interface.
381 :type interface: int or str
382 :returns: List of dictionaries containing data for each interface, or a
383 single dictionary for the specified interface.
385 :raises TypeError: if the data type of interface is neither basestring
388 def process_if_dump(if_dump):
389 """Process interface dump.
391 :param if_dump: Interface dump.
393 :returns: Processed interface dump.
396 if_dump[u"l2_address"] = str(if_dump[u"l2_address"])
397 if_dump[u"b_dmac"] = str(if_dump[u"b_dmac"])
398 if_dump[u"b_smac"] = str(if_dump[u"b_smac"])
399 if_dump[u"flags"] = if_dump[u"flags"].value
400 if_dump[u"type"] = if_dump[u"type"].value
401 if_dump[u"link_duplex"] = if_dump[u"link_duplex"].value
402 if_dump[u"sub_if_flags"] = if_dump[u"sub_if_flags"].value \
403 if hasattr(if_dump[u"sub_if_flags"], u"value") \
404 else int(if_dump[u"sub_if_flags"])
408 if interface is not None:
409 if isinstance(interface, str):
410 param = u"interface_name"
411 elif isinstance(interface, int):
412 param = u"sw_if_index"
414 raise TypeError(f"Wrong interface format {interface}")
418 cmd = u"sw_interface_dump"
420 name_filter_valid=False,
423 err_msg = f"Failed to get interface dump on host {node[u'host']}"
425 with PapiSocketExecutor(node) as papi_exec:
426 details = papi_exec.add(cmd, **args).get_details(err_msg)
427 logger.debug(f"Received data:\n{details!r}")
429 data = list() if interface is None else dict()
431 if interface is None:
432 data.append(process_if_dump(dump))
433 elif str(dump.get(param)).rstrip(u"\x00") == str(interface):
434 data = process_if_dump(dump)
437 logger.debug(f"Interface data:\n{data}")
441 def vpp_get_interface_name(node, sw_if_index):
442 """Get interface name for the given SW interface index from actual
445 :param node: VPP node to get interface data from.
446 :param sw_if_index: SW interface index of the specific interface.
448 :type sw_if_index: int
449 :returns: Name of the given interface.
452 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
453 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
454 if_data = InterfaceUtil.vpp_get_interface_data(
455 node, if_data[u"sup_sw_if_index"]
458 return if_data.get(u"interface_name")
461 def vpp_get_interface_sw_index(node, interface_name):
462 """Get interface name for the given SW interface index from actual
465 :param node: VPP node to get interface data from.
466 :param interface_name: Interface name.
468 :type interface_name: str
469 :returns: Name of the given interface.
472 if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
474 return if_data.get(u"sw_if_index")
477 def vpp_get_interface_mac(node, interface):
478 """Get MAC address for the given interface from actual interface dump.
480 :param node: VPP node to get interface data from.
481 :param interface: Numeric index or name string of a specific interface.
483 :type interface: int or str
484 :returns: MAC address.
487 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
488 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
489 if_data = InterfaceUtil.vpp_get_interface_data(
490 node, if_data[u"sup_sw_if_index"])
492 return if_data.get(u"l2_address")
495 def tg_set_interface_driver(node, pci_addr, driver):
496 """Set interface driver on the TG node.
498 :param node: Node to set interface driver on (must be TG node).
499 :param pci_addr: PCI address of the interface.
500 :param driver: Driver name.
504 :raises RuntimeError: If unbinding from the current driver fails.
505 :raises RuntimeError: If binding to the new driver fails.
507 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
508 if old_driver == driver:
514 # Unbind from current driver
515 if old_driver is not None:
516 cmd = f"sh -c \"echo {pci_addr} > " \
517 f"/sys/bus/pci/drivers/{old_driver}/unbind\""
518 ret_code, _, _ = ssh.exec_command_sudo(cmd)
519 if int(ret_code) != 0:
520 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
522 # Bind to the new driver
523 cmd = f"sh -c \"echo {pci_addr} > /sys/bus/pci/drivers/{driver}/bind\""
524 ret_code, _, _ = ssh.exec_command_sudo(cmd)
525 if int(ret_code) != 0:
526 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
529 def tg_get_interface_driver(node, pci_addr):
530 """Get interface driver from the TG node.
532 :param node: Node to get interface driver on (must be TG node).
533 :param pci_addr: PCI address of the interface.
536 :returns: Interface driver or None if not found.
538 :raises RuntimeError: If PCI rescan or lspci command execution failed.
540 return DUTSetup.get_pci_dev_driver(node, pci_addr)
543 def tg_set_interfaces_default_driver(node):
544 """Set interfaces default driver specified in topology yaml file.
546 :param node: Node to setup interfaces driver on (must be TG node).
549 for interface in node[u"interfaces"].values():
550 InterfaceUtil.tg_set_interface_driver(
551 node, interface[u"pci_address"], interface[u"driver"]
555 def update_vpp_interface_data_on_node(node):
556 """Update vpp generated interface data for a given node in DICT__nodes.
558 Updates interface names, software if index numbers and any other details
559 generated specifically by vpp that are unknown before testcase run.
560 It does this by dumping interface list from all devices using python
561 api, and pairing known information from topology (mac address) to state
564 :param node: Node selected from DICT__nodes.
567 interface_list = InterfaceUtil.vpp_get_interface_data(node)
568 interface_dict = dict()
569 for ifc in interface_list:
570 interface_dict[ifc[u"l2_address"]] = ifc
572 for if_name, if_data in node[u"interfaces"].items():
573 ifc_dict = interface_dict.get(if_data[u"mac_address"])
574 if ifc_dict is not None:
575 if_data[u"name"] = ifc_dict[u"interface_name"]
576 if_data[u"vpp_sw_index"] = ifc_dict[u"sw_if_index"]
577 if_data[u"mtu"] = ifc_dict[u"mtu"][0]
579 f"Interface {if_name} found by MAC "
580 f"{if_data[u'mac_address']}"
584 f"Interface {if_name} not found by MAC "
585 f"{if_data[u'mac_address']}"
587 if_data[u"vpp_sw_index"] = None
590 def update_nic_interface_names(node):
591 """Update interface names based on nic type and PCI address.
593 This method updates interface names in the same format as VPP does.
595 :param node: Node dictionary.
598 for ifc in node[u"interfaces"].values():
599 if_pci = ifc[u"pci_address"].replace(u".", u":").split(u":")
600 loc = f"{int(if_pci[1], 16):x}/{int(if_pci[2], 16):x}/" \
601 f"{int(if_pci[3], 16):x}"
602 if ifc[u"model"] == u"Intel-XL710":
603 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
604 elif ifc[u"model"] == u"Intel-X710":
605 ifc[u"name"] = f"TenGigabitEthernet{loc}"
606 elif ifc[u"model"] == u"Intel-X520-DA2":
607 ifc[u"name"] = f"TenGigabitEthernet{loc}"
608 elif ifc[u"model"] == u"Cisco-VIC-1385":
609 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
610 elif ifc[u"model"] == u"Cisco-VIC-1227":
611 ifc[u"name"] = f"TenGigabitEthernet{loc}"
613 ifc[u"name"] = f"UnknownEthernet{loc}"
616 def update_nic_interface_names_on_all_duts(nodes):
617 """Update interface names based on nic type and PCI address on all DUTs.
619 This method updates interface names in the same format as VPP does.
621 :param nodes: Topology nodes.
624 for node in nodes.values():
625 if node[u"type"] == NodeType.DUT:
626 InterfaceUtil.update_nic_interface_names(node)
629 def update_tg_interface_data_on_node(node):
630 """Update interface name for TG/linux node in DICT__nodes.
633 # for dev in `ls /sys/class/net/`;
634 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
635 "52:54:00:9f:82:63": "eth0"
636 "52:54:00:77:ae:a9": "eth1"
637 "52:54:00:e1:8a:0f": "eth2"
638 "00:00:00:00:00:00": "lo"
640 :param node: Node selected from DICT__nodes.
642 :raises RuntimeError: If getting of interface name and MAC fails.
644 # First setup interface driver specified in yaml file
645 InterfaceUtil.tg_set_interfaces_default_driver(node)
647 # Get interface names
651 cmd = u'for dev in `ls /sys/class/net/`; do echo "\\"`cat ' \
652 u'/sys/class/net/$dev/address`\\": \\"$dev\\""; done;'
654 ret_code, stdout, _ = ssh.exec_command(cmd)
655 if int(ret_code) != 0:
656 raise RuntimeError(u"Get interface name and MAC failed")
657 tmp = u"{" + stdout.rstrip().replace(u"\n", u",") + u"}"
659 interfaces = JsonParser().parse_data(tmp)
660 for interface in node[u"interfaces"].values():
661 name = interfaces.get(interface[u"mac_address"])
664 interface[u"name"] = name
667 def iface_update_numa_node(node):
668 """For all interfaces from topology file update numa node based on
669 information from the node.
671 :param node: Node from topology.
674 :raises ValueError: If numa node ia less than 0.
675 :raises RuntimeError: If update of numa node failed.
677 def check_cpu_node_count(node_n, val):
680 if CpuUtils.cpu_node_count(node_n) == 1:
686 for if_key in Topology.get_node_interfaces(node):
687 if_pci = Topology.get_interface_pci_addr(node, if_key)
689 cmd = f"cat /sys/bus/pci/devices/{if_pci}/numa_node"
691 ret, out, _ = ssh.exec_command(cmd)
694 numa_node = check_cpu_node_count(node, out)
697 f"Reading numa location failed for: {if_pci}"
700 Topology.set_interface_numa_node(
701 node, if_key, numa_node
705 raise RuntimeError(f"Update numa node failed for: {if_pci}")
708 def update_all_interface_data_on_all_nodes(
709 nodes, skip_tg=False, skip_vpp=False):
710 """Update interface names on all nodes in DICT__nodes.
712 This method updates the topology dictionary by querying interface lists
713 of all nodes mentioned in the topology dictionary.
715 :param nodes: Nodes in the topology.
716 :param skip_tg: Skip TG node.
717 :param skip_vpp: Skip VPP node.
722 for node in nodes.values():
723 if node[u"type"] == NodeType.DUT and not skip_vpp:
724 InterfaceUtil.update_vpp_interface_data_on_node(node)
725 elif node[u"type"] == NodeType.TG and not skip_tg:
726 InterfaceUtil.update_tg_interface_data_on_node(node)
727 InterfaceUtil.iface_update_numa_node(node)
730 def create_vlan_subinterface(node, interface, vlan):
731 """Create VLAN sub-interface on node.
733 :param node: Node to add VLAN subinterface on.
734 :param interface: Interface name or index on which create VLAN
736 :param vlan: VLAN ID of the subinterface to be created.
738 :type interface: str on int
740 :returns: Name and index of created subinterface.
742 :raises RuntimeError: if it is unable to create VLAN subinterface on the
743 node or interface cannot be converted.
745 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
747 cmd = u"create_vlan_subif"
749 sw_if_index=sw_if_index,
752 err_msg = f"Failed to create VLAN sub-interface on host {node[u'host']}"
754 with PapiSocketExecutor(node) as papi_exec:
755 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
757 if_key = Topology.add_new_port(node, u"vlan_subif")
758 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
759 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
760 Topology.update_interface_name(node, if_key, ifc_name)
762 return f"{interface}.{vlan}", sw_if_index
765 def create_vxlan_interface(node, vni, source_ip, destination_ip):
766 """Create VXLAN interface and return sw if index of created interface.
768 :param node: Node where to create VXLAN interface.
769 :param vni: VXLAN Network Identifier.
770 :param source_ip: Source IP of a VXLAN Tunnel End Point.
771 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
775 :type destination_ip: str
776 :returns: SW IF INDEX of created interface.
778 :raises RuntimeError: if it is unable to create VxLAN interface on the
781 src_address = ip_address(source_ip)
782 dst_address = ip_address(destination_ip)
784 cmd = u"vxlan_add_del_tunnel"
787 is_ipv6=1 if src_address.version == 6 else 0,
788 instance=Constants.BITWISE_NON_ZERO,
789 src_address=src_address.packed,
790 dst_address=dst_address.packed,
791 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
793 decap_next_index=Constants.BITWISE_NON_ZERO,
796 err_msg = f"Failed to create VXLAN tunnel interface " \
797 f"on host {node[u'host']}"
798 with PapiSocketExecutor(node) as papi_exec:
799 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
801 if_key = Topology.add_new_port(node, u"vxlan_tunnel")
802 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
803 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
804 Topology.update_interface_name(node, if_key, ifc_name)
809 def set_vxlan_bypass(node, interface=None):
810 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
812 By adding the IPv4 vxlan-bypass graph node to an interface, the node
813 checks for and validate input vxlan packet and bypass ip4-lookup,
814 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
815 This node will cause extra overhead to for non-vxlan packets which is
818 :param node: Node where to set VXLAN bypass.
819 :param interface: Numeric index or name string of a specific interface.
821 :type interface: int or str
822 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
824 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
826 cmd = u"sw_interface_set_vxlan_bypass"
829 sw_if_index=sw_if_index,
832 err_msg = f"Failed to set VXLAN bypass on interface " \
833 f"on host {node[u'host']}"
834 with PapiSocketExecutor(node) as papi_exec:
835 papi_exec.add(cmd, **args).get_replies(err_msg)
838 def vxlan_dump(node, interface=None):
839 """Get VxLAN data for the given interface.
841 :param node: VPP node to get interface data from.
842 :param interface: Numeric index or name string of a specific interface.
843 If None, information about all VxLAN interfaces is returned.
845 :type interface: int or str
846 :returns: Dictionary containing data for the given VxLAN interface or if
847 interface=None, the list of dictionaries with all VxLAN interfaces.
849 :raises TypeError: if the data type of interface is neither basestring
852 def process_vxlan_dump(vxlan_dump):
853 """Process vxlan dump.
855 :param vxlan_dump: Vxlan interface dump.
856 :type vxlan_dump: dict
857 :returns: Processed vxlan interface dump.
860 if vxlan_dump[u"is_ipv6"]:
861 vxlan_dump[u"src_address"] = \
862 ip_address(vxlan_dump[u"src_address"])
863 vxlan_dump[u"dst_address"] = \
864 ip_address(vxlan_dump[u"dst_address"])
866 vxlan_dump[u"src_address"] = \
867 ip_address(vxlan_dump[u"src_address"][0:4])
868 vxlan_dump[u"dst_address"] = \
869 ip_address(vxlan_dump[u"dst_address"][0:4])
872 if interface is not None:
873 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
875 sw_if_index = int(Constants.BITWISE_NON_ZERO)
877 cmd = u"vxlan_tunnel_dump"
879 sw_if_index=sw_if_index
881 err_msg = f"Failed to get VXLAN dump on host {node[u'host']}"
883 with PapiSocketExecutor(node) as papi_exec:
884 details = papi_exec.add(cmd, **args).get_details(err_msg)
886 data = list() if interface is None else dict()
888 if interface is None:
889 data.append(process_vxlan_dump(dump))
890 elif dump[u"sw_if_index"] == sw_if_index:
891 data = process_vxlan_dump(dump)
894 logger.debug(f"VXLAN data:\n{data}")
898 def create_subinterface(
899 node, interface, sub_id, outer_vlan_id=None, inner_vlan_id=None,
901 """Create sub-interface on node. It is possible to set required
902 sub-interface type and VLAN tag(s).
904 :param node: Node to add sub-interface.
905 :param interface: Interface name on which create sub-interface.
906 :param sub_id: ID of the sub-interface to be created.
907 :param outer_vlan_id: Optional outer VLAN ID.
908 :param inner_vlan_id: Optional inner VLAN ID.
909 :param type_subif: Optional type of sub-interface. Values supported by
910 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
913 :type interface: str or int
915 :type outer_vlan_id: int
916 :type inner_vlan_id: int
917 :type type_subif: str
918 :returns: Name and index of created sub-interface.
920 :raises RuntimeError: If it is not possible to create sub-interface.
922 subif_types = type_subif.split()
925 if u"no_tags" in subif_types:
926 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
927 if u"one_tag" in subif_types:
928 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
929 if u"two_tags" in subif_types:
930 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
931 if u"dot1ad" in subif_types:
932 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
933 if u"exact_match" in subif_types:
934 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
935 if u"default_sub" in subif_types:
936 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
937 if type_subif == u"default_sub":
938 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
939 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
941 cmd = u"create_subif"
943 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
945 sub_if_flags=flags.value if hasattr(flags, u"value")
947 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
948 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
950 err_msg = f"Failed to create sub-interface on host {node[u'host']}"
951 with PapiSocketExecutor(node) as papi_exec:
952 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
954 if_key = Topology.add_new_port(node, u"subinterface")
955 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
956 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
957 Topology.update_interface_name(node, if_key, ifc_name)
959 return f"{interface}.{sub_id}", sw_if_index
962 def create_gre_tunnel_interface(node, source_ip, destination_ip):
963 """Create GRE tunnel interface on node.
965 :param node: VPP node to add tunnel interface.
966 :param source_ip: Source of the GRE tunnel.
967 :param destination_ip: Destination of the GRE tunnel.
970 :type destination_ip: str
971 :returns: Name and index of created GRE tunnel interface.
973 :raises RuntimeError: If unable to create GRE tunnel interface.
975 cmd = u"gre_tunnel_add_del"
978 instance=Constants.BITWISE_NON_ZERO,
980 dst=str(destination_ip),
988 err_msg = f"Failed to create GRE tunnel interface " \
989 f"on host {node[u'host']}"
990 with PapiSocketExecutor(node) as papi_exec:
991 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
993 if_key = Topology.add_new_port(node, u"gre_tunnel")
994 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
995 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
996 Topology.update_interface_name(node, if_key, ifc_name)
998 return ifc_name, sw_if_index
1001 def vpp_create_loopback(node, mac=None):
1002 """Create loopback interface on VPP node.
1004 :param node: Node to create loopback interface on.
1005 :param mac: Optional MAC address for loopback interface.
1008 :returns: SW interface index.
1010 :raises RuntimeError: If it is not possible to create loopback on the
1013 cmd = u"create_loopback"
1015 mac_address=L2Util.mac_to_bin(mac) if mac else 0
1017 err_msg = f"Failed to create loopback interface on host {node[u'host']}"
1018 with PapiSocketExecutor(node) as papi_exec:
1019 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1021 if_key = Topology.add_new_port(node, u"loopback")
1022 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1023 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1024 Topology.update_interface_name(node, if_key, ifc_name)
1026 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1027 Topology.update_interface_mac_address(node, if_key, mac)
1032 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1033 """Create bond interface on VPP node.
1035 :param node: DUT node from topology.
1036 :param mode: Link bonding mode.
1037 :param load_balance: Load balance (optional, valid for xor and lacp
1038 modes, otherwise ignored).
1039 :param mac: MAC address to assign to the bond interface (optional).
1042 :type load_balance: str
1044 :returns: Interface key (name) in topology.
1046 :raises RuntimeError: If it is not possible to create bond interface on
1049 cmd = u"bond_create"
1051 id=int(Constants.BITWISE_NON_ZERO),
1052 use_custom_mac=bool(mac is not None),
1053 mac_address=L2Util.mac_to_bin(mac) if mac else None,
1056 f"BOND_API_MODE_{mode.replace(u'-', u'_').upper()}"
1058 lb=0 if load_balance is None else getattr(
1059 LinkBondLoadBalanceAlgo,
1060 f"BOND_API_LB_ALGO_{load_balance.upper()}"
1064 err_msg = f"Failed to create bond interface on host {node[u'host']}"
1065 with PapiSocketExecutor(node) as papi_exec:
1066 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1068 InterfaceUtil.add_eth_interface(
1069 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_bond"
1071 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1076 def add_eth_interface(
1077 node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
1079 """Add ethernet interface to current topology.
1081 :param node: DUT node from topology.
1082 :param ifc_name: Name of the interface.
1083 :param sw_if_index: SW interface index.
1084 :param ifc_pfx: Interface key prefix.
1085 :param host_if_key: Host interface key from topology file.
1088 :type sw_if_index: int
1090 :type host_if_key: str
1092 if_key = Topology.add_new_port(node, ifc_pfx)
1094 if ifc_name and sw_if_index is None:
1095 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1097 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1098 if sw_if_index and ifc_name is None:
1099 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1100 Topology.update_interface_name(node, if_key, ifc_name)
1101 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1102 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1103 if host_if_key is not None:
1104 Topology.set_interface_numa_node(
1105 node, if_key, Topology.get_interface_numa_node(
1109 Topology.update_interface_pci_address(
1110 node, if_key, Topology.get_interface_pci_addr(node, host_if_key)
1114 def vpp_create_avf_interface(node, if_key, num_rx_queues=None):
1115 """Create AVF interface on VPP node.
1117 :param node: DUT node from topology.
1118 :param if_key: Interface key from topology file of interface
1119 to be bound to i40evf driver.
1120 :param num_rx_queues: Number of RX queues.
1123 :type num_rx_queues: int
1124 :returns: AVF interface key (name) in topology.
1126 :raises RuntimeError: If it is not possible to create AVF interface on
1129 PapiSocketExecutor.run_cli_cmd(
1130 node, u"set logging class avf level debug"
1134 vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
1136 pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1138 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1142 err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1143 with PapiSocketExecutor(node) as papi_exec:
1144 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1146 InterfaceUtil.add_eth_interface(
1147 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
1151 return Topology.get_interface_by_sw_index(node, sw_if_index)
1154 def vpp_create_rdma_interface(
1155 node, if_key, num_rx_queues=None, mode=u"auto"):
1156 """Create RDMA interface on VPP node.
1158 :param node: DUT node from topology.
1159 :param if_key: Physical interface key from topology file of interface
1160 to be bound to rdma-core driver.
1161 :param num_rx_queues: Number of RX queues.
1162 :param mode: RDMA interface mode - auto/ibv/dv.
1165 :type num_rx_queues: int
1167 :returns: Interface key (name) in topology file.
1169 :raises RuntimeError: If it is not possible to create RDMA interface on
1172 cmd = u"rdma_create"
1173 pci_addr = Topology.get_interface_pci_addr(node, if_key)
1175 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1176 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1177 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1180 mode=getattr(RdmaMode,f"RDMA_API_MODE_{mode.upper()}").value,
1182 err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1183 with PapiSocketExecutor(node) as papi_exec:
1184 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1186 InterfaceUtil.add_eth_interface(
1187 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
1191 return Topology.get_interface_by_sw_index(node, sw_if_index)
1194 def vpp_enslave_physical_interface(node, interface, bond_if):
1195 """Enslave physical interface to bond interface on VPP node.
1197 :param node: DUT node from topology.
1198 :param interface: Physical interface key from topology file.
1199 :param bond_if: Load balance
1201 :type interface: str
1203 :raises RuntimeError: If it is not possible to enslave physical
1204 interface to bond interface on the node.
1206 cmd = u"bond_enslave"
1208 sw_if_index=Topology.get_interface_sw_index(node, interface),
1209 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1211 is_long_timeout=False
1213 err_msg = f"Failed to enslave physical interface {interface} to bond " \
1214 f"interface {bond_if} on host {node[u'host']}"
1215 with PapiSocketExecutor(node) as papi_exec:
1216 papi_exec.add(cmd, **args).get_reply(err_msg)
1219 def vpp_show_bond_data_on_node(node, verbose=False):
1220 """Show (detailed) bond information on VPP node.
1222 :param node: DUT node from topology.
1223 :param verbose: If detailed information is required or not.
1227 cmd = u"sw_interface_bond_dump"
1228 err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1230 data = f"Bond data on node {node[u'host']}:\n"
1231 with PapiSocketExecutor(node) as papi_exec:
1232 details = papi_exec.add(cmd).get_details(err_msg)
1234 for bond in details:
1235 data += f"{bond[u'interface_name']}\n"
1236 data += u" mode: {m}\n".format(
1237 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1239 data += u" load balance: {lb}\n".format(
1240 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1242 data += f" number of active slaves: {bond[u'active_slaves']}\n"
1244 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1245 node, Topology.get_interface_by_sw_index(
1246 node, bond[u"sw_if_index"]
1249 for slave in slave_data:
1250 if not slave[u"is_passive"]:
1251 data += f" {slave[u'interface_name']}\n"
1252 data += f" number of slaves: {bond[u'slaves']}\n"
1254 for slave in slave_data:
1255 data += f" {slave[u'interface_name']}\n"
1256 data += f" interface id: {bond[u'id']}\n"
1257 data += f" sw_if_index: {bond[u'sw_if_index']}\n"
1261 def vpp_bond_slave_dump(node, interface):
1262 """Get bond interface slave(s) data on VPP node.
1264 :param node: DUT node from topology.
1265 :param interface: Physical interface key from topology file.
1267 :type interface: str
1268 :returns: Bond slave interface data.
1271 cmd = u"sw_interface_slave_dump"
1273 sw_if_index=Topology.get_interface_sw_index(node, interface)
1275 err_msg = f"Failed to get slave dump on host {node[u'host']}"
1277 with PapiSocketExecutor(node) as papi_exec:
1278 details = papi_exec.add(cmd, **args).get_details(err_msg)
1280 logger.debug(f"Slave data:\n{details}")
1284 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1285 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1287 :param nodes: Nodes in the topology.
1288 :param verbose: If detailed information is required or not.
1292 for node_data in nodes.values():
1293 if node_data[u"type"] == NodeType.DUT:
1294 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1297 def vpp_enable_input_acl_interface(
1298 node, interface, ip_version, table_index):
1299 """Enable input acl on interface.
1301 :param node: VPP node to setup interface for input acl.
1302 :param interface: Interface to setup input acl.
1303 :param ip_version: Version of IP protocol.
1304 :param table_index: Classify table index.
1306 :type interface: str or int
1307 :type ip_version: str
1308 :type table_index: int
1310 cmd = u"input_acl_set_interface"
1312 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1313 ip4_table_index=table_index if ip_version == u"ip4"
1314 else Constants.BITWISE_NON_ZERO,
1315 ip6_table_index=table_index if ip_version == u"ip6"
1316 else Constants.BITWISE_NON_ZERO,
1317 l2_table_index=table_index if ip_version == u"l2"
1318 else Constants.BITWISE_NON_ZERO,
1320 err_msg = f"Failed to enable input acl on interface {interface}"
1321 with PapiSocketExecutor(node) as papi_exec:
1322 papi_exec.add(cmd, **args).get_reply(err_msg)
1325 def get_interface_classify_table(node, interface):
1326 """Get name of classify table for the given interface.
1328 TODO: Move to Classify.py.
1330 :param node: VPP node to get data from.
1331 :param interface: Name or sw_if_index of a specific interface.
1333 :type interface: str or int
1334 :returns: Classify table name.
1337 if isinstance(interface, str):
1338 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1340 sw_if_index = interface
1342 cmd = u"classify_table_by_interface"
1344 sw_if_index=sw_if_index
1346 err_msg = f"Failed to get classify table name by interface {interface}"
1347 with PapiSocketExecutor(node) as papi_exec:
1348 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1353 def get_sw_if_index(node, interface_name):
1354 """Get sw_if_index for the given interface from actual interface dump.
1356 FIXME: Delete and redirect callers to vpp_get_interface_sw_index.
1358 :param node: VPP node to get interface data from.
1359 :param interface_name: Name of the specific interface.
1361 :type interface_name: str
1362 :returns: sw_if_index of the given interface.
1365 interface_data = InterfaceUtil.vpp_get_interface_data(
1366 node, interface=interface_name
1368 return interface_data.get(u"sw_if_index")
1371 def vxlan_gpe_dump(node, interface_name=None):
1372 """Get VxLAN GPE data for the given interface.
1374 :param node: VPP node to get interface data from.
1375 :param interface_name: Name of the specific interface. If None,
1376 information about all VxLAN GPE interfaces is returned.
1378 :type interface_name: str
1379 :returns: Dictionary containing data for the given VxLAN GPE interface
1380 or if interface=None, the list of dictionaries with all VxLAN GPE
1382 :rtype: dict or list
1384 def process_vxlan_gpe_dump(vxlan_dump):
1385 """Process vxlan_gpe dump.
1387 :param vxlan_dump: Vxlan_gpe nterface dump.
1388 :type vxlan_dump: dict
1389 :returns: Processed vxlan_gpe interface dump.
1392 if vxlan_dump[u"is_ipv6"]:
1393 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1394 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1396 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1397 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1400 if interface_name is not None:
1401 sw_if_index = InterfaceUtil.get_interface_index(
1402 node, interface_name
1405 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1407 cmd = u"vxlan_gpe_tunnel_dump"
1409 sw_if_index=sw_if_index
1411 err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1412 with PapiSocketExecutor(node) as papi_exec:
1413 details = papi_exec.add(cmd, **args).get_details(err_msg)
1415 data = list() if interface_name is None else dict()
1416 for dump in details:
1417 if interface_name is None:
1418 data.append(process_vxlan_gpe_dump(dump))
1419 elif dump[u"sw_if_index"] == sw_if_index:
1420 data = process_vxlan_gpe_dump(dump)
1423 logger.debug(f"VXLAN-GPE data:\n{data}")
1427 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1428 """Assign VPP interface to specific VRF/FIB table.
1430 :param node: VPP node where the FIB and interface are located.
1431 :param interface: Interface to be assigned to FIB.
1432 :param table_id: VRF table ID.
1433 :param ipv6: Assign to IPv6 table. Default False.
1435 :type interface: str or int
1439 cmd = u"sw_interface_set_table"
1441 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1443 vrf_id=int(table_id)
1445 err_msg = f"Failed to assign interface {interface} to FIB table"
1446 with PapiSocketExecutor(node) as papi_exec:
1447 papi_exec.add(cmd, **args).get_reply(err_msg)
1450 def set_linux_interface_mac(
1451 node, interface, mac, namespace=None, vf_id=None):
1452 """Set MAC address for interface in linux.
1454 :param node: Node where to execute command.
1455 :param interface: Interface in namespace.
1456 :param mac: MAC to be assigned to interface.
1457 :param namespace: Execute command in namespace. Optional
1458 :param vf_id: Virtual Function id. Optional
1460 :type interface: str
1462 :type namespace: str
1465 mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1466 else f"address {mac}"
1467 ns_str = f"ip netns exec {namespace}" if namespace else u""
1469 cmd = f"{ns_str} ip link set {interface} {mac_str}"
1470 exec_cmd_no_error(node, cmd, sudo=True)
1473 def set_linux_interface_trust_on(
1474 node, interface, namespace=None, vf_id=None):
1475 """Set trust on (promisc) for interface in linux.
1477 :param node: Node where to execute command.
1478 :param interface: Interface in namespace.
1479 :param namespace: Execute command in namespace. Optional
1480 :param vf_id: Virtual Function id. Optional
1482 :type interface: str
1483 :type namespace: str
1486 trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1487 ns_str = f"ip netns exec {namespace}" if namespace else u""
1489 cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1490 exec_cmd_no_error(node, cmd, sudo=True)
1493 def set_linux_interface_spoof_off(
1494 node, interface, namespace=None, vf_id=None):
1495 """Set spoof off for interface in linux.
1497 :param node: Node where to execute command.
1498 :param interface: Interface in namespace.
1499 :param namespace: Execute command in namespace. Optional
1500 :param vf_id: Virtual Function id. Optional
1502 :type interface: str
1503 :type namespace: str
1506 spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1508 ns_str = f"ip netns exec {namespace}" if namespace else u""
1510 cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1511 exec_cmd_no_error(node, cmd, sudo=True)
1514 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer=u"L2"):
1515 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1516 driver testing on DUT.
1518 :param node: DUT node.
1519 :param ifc_key: Interface key from topology file.
1520 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1521 :param osi_layer: OSI Layer type to initialize TG with.
1522 Default value "L2" sets linux interface spoof off.
1526 :type osi_layer: str
1527 :returns: Virtual Function topology interface keys.
1529 :raises RuntimeError: If a reason preventing initialization is found.
1531 # Read PCI address and driver.
1532 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1533 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1534 uio_driver = Topology.get_uio_driver(node)
1535 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1536 if kernel_driver not in (u"i40e", u"i40evf"):
1538 f"AVF needs i40e-compatible driver, not {kernel_driver} "
1539 f"at node {node[u'host']} ifc {ifc_key}"
1541 current_driver = DUTSetup.get_pci_dev_driver(
1542 node, pf_pci_addr.replace(u":", r"\:"))
1544 VPPUtil.stop_vpp_service(node)
1545 if current_driver != kernel_driver:
1546 # PCI device must be re-bound to kernel driver before creating VFs.
1547 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1548 # Stop VPP to prevent deadlock.
1549 # Unbind from current driver.
1550 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1551 # Bind to kernel driver.
1552 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1554 # Initialize PCI VFs.
1555 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1558 # Set MAC address and bind each virtual function to uio driver.
1559 for vf_id in range(numvfs):
1560 vf_mac_addr = u":".join(
1561 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1562 pf_mac_addr[5], f"{vf_id:02x}"
1566 pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1567 InterfaceUtil.set_linux_interface_trust_on(
1568 node, pf_dev, vf_id=vf_id
1570 if osi_layer == u"L2":
1571 InterfaceUtil.set_linux_interface_spoof_off(
1572 node, pf_dev, vf_id=vf_id
1574 InterfaceUtil.set_linux_interface_mac(
1575 node, pf_dev, vf_mac_addr, vf_id=vf_id
1578 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1579 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1581 # Add newly created ports into topology file
1582 vf_ifc_name = f"{ifc_key}_vif"
1583 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1584 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1585 Topology.update_interface_name(
1586 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1588 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1589 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1590 Topology.set_interface_numa_node(
1591 node, vf_ifc_key, Topology.get_interface_numa_node(
1595 vf_ifc_keys.append(vf_ifc_key)
1600 def vpp_sw_interface_rx_placement_dump(node):
1601 """Dump VPP interface RX placement on node.
1603 :param node: Node to run command on.
1605 :returns: Thread mapping information as a list of dictionaries.
1608 cmd = u"sw_interface_rx_placement_dump"
1609 err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1610 with PapiSocketExecutor(node) as papi_exec:
1611 for ifc in node[u"interfaces"].values():
1612 if ifc[u"vpp_sw_index"] is not None:
1613 papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1614 details = papi_exec.get_details(err_msg)
1615 return sorted(details, key=lambda k: k[u"sw_if_index"])
1618 def vpp_sw_interface_set_rx_placement(
1619 node, sw_if_index, queue_id, worker_id):
1620 """Set interface RX placement to worker on node.
1622 :param node: Node to run command on.
1623 :param sw_if_index: VPP SW interface index.
1624 :param queue_id: VPP interface queue ID.
1625 :param worker_id: VPP worker ID (indexing from 0).
1627 :type sw_if_index: int
1629 :type worker_id: int
1630 :raises RuntimeError: If failed to run command on host or if no API
1633 cmd = u"sw_interface_set_rx_placement"
1634 err_msg = f"Failed to set interface RX placement to worker " \
1635 f"on host {node[u'host']}!"
1637 sw_if_index=sw_if_index,
1639 worker_id=worker_id,
1642 with PapiSocketExecutor(node) as papi_exec:
1643 papi_exec.add(cmd, **args).get_reply(err_msg)
1646 def vpp_round_robin_rx_placement(node, prefix):
1647 """Set Round Robin interface RX placement on all worker threads
1650 :param node: Topology nodes.
1651 :param prefix: Interface name prefix.
1656 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1659 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1660 for interface in node[u"interfaces"].values():
1661 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
1662 and prefix in interface[u"name"]:
1663 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1664 node, placement[u"sw_if_index"], placement[u"queue_id"],
1665 worker_id % worker_cnt
1670 def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1671 """Set Round Robin interface RX placement on all worker threads
1674 :param nodes: Topology nodes.
1675 :param prefix: Interface name prefix.
1679 for node in nodes.values():
1680 if node[u"type"] == NodeType.DUT:
1681 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)