1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
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
107 """General utilities for managing interfaces"""
109 __UDEV_IF_RULES_FILE = u"/etc/udev/rules.d/10-network.rules"
112 def pci_to_int(pci_str):
113 """Convert PCI address from string format (0000:18:0a.0) to
114 integer representation (169345024).
116 :param pci_str: PCI address in string representation.
118 :returns: Integer representation of PCI address.
121 pci = list(pci_str.split(u":")[0:2])
122 pci.extend(pci_str.split(u":")[2].split(u"."))
124 return (int(pci[0], 16) | int(pci[1], 16) << 16 |
125 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
128 def pci_to_eth(node, pci_str):
129 """Convert PCI address on DUT to Linux ethernet name.
131 :param node: DUT node
132 :param pci_str: PCI address.
135 :returns: Ethernet name.
138 cmd = f"basename /sys/bus/pci/devices/{pci_str}/net/*"
140 stdout, _ = exec_cmd_no_error(node, cmd)
142 raise RuntimeError(f"Cannot convert {pci_str} to ethernet name!")
144 return stdout.strip()
147 def get_interface_index(node, interface):
148 """Get interface sw_if_index from topology file.
150 :param node: Node where the interface is.
151 :param interface: Numeric index or name string of a specific interface.
153 :type interface: str or int
154 :returns: SW interface index.
158 sw_if_index = int(interface)
160 sw_if_index = Topology.get_interface_sw_index(node, interface)
161 if sw_if_index is None:
163 Topology.get_interface_sw_index_by_name(node, interface)
164 except TypeError as err:
165 raise TypeError(f"Wrong interface format {interface}") from err
170 def set_interface_state(node, interface, state, if_type=u"key"):
171 """Set interface state on a node.
173 Function can be used for DUTs as well as for TGs.
175 :param node: Node where the interface is.
176 :param interface: Interface key or sw_if_index or name.
177 :param state: One of 'up' or 'down'.
178 :param if_type: Interface type
180 :type interface: str or int
184 :raises ValueError: If the interface type is unknown.
185 :raises ValueError: If the state of interface is unexpected.
186 :raises ValueError: If the node has an unknown node type.
188 if if_type == u"key":
189 if isinstance(interface, str):
190 sw_if_index = Topology.get_interface_sw_index(node, interface)
191 iface_name = Topology.get_interface_name(node, interface)
193 sw_if_index = interface
194 elif if_type == u"name":
195 iface_key = Topology.get_interface_by_name(node, interface)
196 if iface_key is not None:
197 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
198 iface_name = interface
200 raise ValueError(f"Unknown if_type: {if_type}")
202 if node[u"type"] == NodeType.DUT:
204 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
205 elif state == u"down":
208 raise ValueError(f"Unexpected interface state: {state}")
209 cmd = u"sw_interface_set_flags"
210 err_msg = f"Failed to set interface state on host {node[u'host']}"
212 sw_if_index=int(sw_if_index),
215 with PapiSocketExecutor(node) as papi_exec:
216 papi_exec.add(cmd, **args).get_reply(err_msg)
217 elif node[u"type"] == NodeType.TG or node[u"type"] == NodeType.VM:
218 cmd = f"ip link set {iface_name} {state}"
219 exec_cmd_no_error(node, cmd, sudo=True)
222 f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
226 def set_interface_ethernet_mtu(node, iface_key, mtu):
227 """Set Ethernet MTU for specified interface.
229 Function can be used only for TGs.
231 :param node: Node where the interface is.
232 :param iface_key: Interface key from topology file.
233 :param mtu: MTU to set.
238 :raises ValueError: If the node type is "DUT".
239 :raises ValueError: If the node has an unknown node type.
241 if node[u"type"] == NodeType.DUT:
242 msg = f"Node {node[u'host']}: Setting Ethernet MTU for interface " \
243 f"on DUT nodes not supported"
244 elif node[u"type"] != NodeType.TG:
245 msg = f"Node {node[u'host']} has unknown NodeType: {node[u'type']}"
247 iface_name = Topology.get_interface_name(node, iface_key)
248 cmd = f"ip link set {iface_name} mtu {mtu}"
249 exec_cmd_no_error(node, cmd, sudo=True)
251 raise ValueError(msg)
254 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
255 """Set default Ethernet MTU on all interfaces on node.
257 Function can be used only for TGs.
259 :param node: Node where to set default MTU.
263 for ifc in node[u"interfaces"]:
264 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
267 def vpp_set_interface_mtu(node, interface, mtu=9200):
268 """Set Ethernet MTU on interface.
270 :param node: VPP node.
271 :param interface: Interface to setup MTU. Default: 9200.
272 :param mtu: Ethernet MTU size in Bytes.
274 :type interface: str or int
277 if isinstance(interface, str):
278 sw_if_index = Topology.get_interface_sw_index(node, interface)
280 sw_if_index = interface
282 cmd = u"hw_interface_set_mtu"
283 err_msg = f"Failed to set interface MTU on host {node[u'host']}"
285 sw_if_index=sw_if_index,
289 with PapiSocketExecutor(node) as papi_exec:
290 papi_exec.add(cmd, **args).get_reply(err_msg)
291 except AssertionError as err:
292 # TODO: Make failure tolerance optional.
293 logger.debug(f"Setting MTU failed. Expected?\n{err}")
296 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
297 """Set Ethernet MTU on all interfaces.
299 :param node: VPP node.
300 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
304 for interface in node[u"interfaces"]:
305 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
308 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
309 """Set Ethernet MTU on all interfaces on all DUTs.
311 :param nodes: VPP nodes.
312 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
316 for node in nodes.values():
317 if node[u"type"] == NodeType.DUT:
318 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
321 def vpp_node_interfaces_ready_wait(node, retries=15):
322 """Wait until all interfaces with admin-up are in link-up state.
324 :param node: Node to wait on.
325 :param retries: Number of retries to check interface status (optional,
330 :raises RuntimeError: If any interface is not in link-up state after
331 defined number of retries.
333 for _ in range(0, retries):
335 out = InterfaceUtil.vpp_get_interface_data(node)
336 for interface in out:
337 if interface.get(u"flags") == 1:
338 not_ready.append(interface.get(u"interface_name"))
341 f"Interfaces still not in link-up state:\n{not_ready}"
347 err = f"Timeout, interfaces not up:\n{not_ready}" \
348 if u"not_ready" in locals() else u"No check executed!"
349 raise RuntimeError(err)
352 def all_vpp_interfaces_ready_wait(nodes, retries=15):
353 """Wait until all interfaces with admin-up are in link-up state for all
354 nodes in the topology.
356 :param nodes: Nodes in the topology.
357 :param retries: Number of retries to check interface status (optional,
363 for node in nodes.values():
364 if node[u"type"] == NodeType.DUT:
365 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
368 def vpp_get_interface_data(node, interface=None):
369 """Get all interface data from a VPP node. If a name or
370 sw_interface_index is provided, return only data for the matching
373 :param node: VPP node to get interface data from.
374 :param interface: Numeric index or name string of a specific interface.
376 :type interface: int or str
377 :returns: List of dictionaries containing data for each interface, or a
378 single dictionary for the specified interface.
380 :raises TypeError: if the data type of interface is neither basestring
383 def process_if_dump(if_dump):
384 """Process interface dump.
386 :param if_dump: Interface dump.
388 :returns: Processed interface dump.
391 if_dump[u"l2_address"] = str(if_dump[u"l2_address"])
392 if_dump[u"b_dmac"] = str(if_dump[u"b_dmac"])
393 if_dump[u"b_smac"] = str(if_dump[u"b_smac"])
394 if_dump[u"flags"] = if_dump[u"flags"].value
395 if_dump[u"type"] = if_dump[u"type"].value
396 if_dump[u"link_duplex"] = if_dump[u"link_duplex"].value
397 if_dump[u"sub_if_flags"] = if_dump[u"sub_if_flags"].value \
398 if hasattr(if_dump[u"sub_if_flags"], u"value") \
399 else int(if_dump[u"sub_if_flags"])
403 if interface is not None:
404 if isinstance(interface, str):
405 param = u"interface_name"
406 elif isinstance(interface, int):
407 param = u"sw_if_index"
409 raise TypeError(f"Wrong interface format {interface}")
413 cmd = u"sw_interface_dump"
415 name_filter_valid=False,
418 err_msg = f"Failed to get interface dump on host {node[u'host']}"
420 with PapiSocketExecutor(node) as papi_exec:
421 details = papi_exec.add(cmd, **args).get_details(err_msg)
422 logger.debug(f"Received data:\n{details!r}")
424 data = list() if interface is None else dict()
426 if interface is None:
427 data.append(process_if_dump(dump))
428 elif str(dump.get(param)).rstrip(u"\x00") == str(interface):
429 data = process_if_dump(dump)
432 logger.debug(f"Interface data:\n{data}")
436 def vpp_get_interface_name(node, sw_if_index):
437 """Get interface name for the given SW interface index from actual
440 :param node: VPP node to get interface data from.
441 :param sw_if_index: SW interface index of the specific interface.
443 :type sw_if_index: int
444 :returns: Name of the given interface.
447 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
448 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
449 if_data = InterfaceUtil.vpp_get_interface_data(
450 node, if_data[u"sup_sw_if_index"]
453 return if_data.get(u"interface_name")
456 def vpp_get_interface_sw_index(node, interface_name):
457 """Get interface name for the given SW interface index from actual
460 :param node: VPP node to get interface data from.
461 :param interface_name: Interface name.
463 :type interface_name: str
464 :returns: Name of the given interface.
467 if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
469 return if_data.get(u"sw_if_index")
472 def vpp_get_interface_mac(node, interface):
473 """Get MAC address for the given interface from actual interface dump.
475 :param node: VPP node to get interface data from.
476 :param interface: Numeric index or name string of a specific interface.
478 :type interface: int or str
479 :returns: MAC address.
482 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
483 if if_data[u"sup_sw_if_index"] != if_data[u"sw_if_index"]:
484 if_data = InterfaceUtil.vpp_get_interface_data(
485 node, if_data[u"sup_sw_if_index"])
487 return if_data.get(u"l2_address")
490 def tg_set_interface_driver(node, pci_addr, driver):
491 """Set interface driver on the TG node.
493 :param node: Node to set interface driver on (must be TG node).
494 :param pci_addr: PCI address of the interface.
495 :param driver: Driver name.
499 :raises RuntimeError: If unbinding from the current driver fails.
500 :raises RuntimeError: If binding to the new driver fails.
502 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
503 if old_driver == driver:
509 # Unbind from current driver
510 if old_driver is not None:
511 cmd = f"sh -c \"echo {pci_addr} > " \
512 f"/sys/bus/pci/drivers/{old_driver}/unbind\""
513 ret_code, _, _ = ssh.exec_command_sudo(cmd)
514 if int(ret_code) != 0:
515 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
517 # Bind to the new driver
518 cmd = f"sh -c \"echo {pci_addr} > /sys/bus/pci/drivers/{driver}/bind\""
519 ret_code, _, _ = ssh.exec_command_sudo(cmd)
520 if int(ret_code) != 0:
521 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
524 def tg_get_interface_driver(node, pci_addr):
525 """Get interface driver from the TG node.
527 :param node: Node to get interface driver on (must be TG node).
528 :param pci_addr: PCI address of the interface.
531 :returns: Interface driver or None if not found.
533 :raises RuntimeError: If PCI rescan or lspci command execution failed.
535 return DUTSetup.get_pci_dev_driver(node, pci_addr)
538 def tg_set_interfaces_udev_rules(node):
539 """Set udev rules for interfaces.
541 Create udev rules file in /etc/udev/rules.d where are rules for each
542 interface used by TG node, based on MAC interface has specific name.
543 So after unbind and bind again to kernel driver interface has same
544 name as before. This must be called after TG has set name for each
545 port in topology dictionary.
547 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
550 :param node: Node to set udev rules on (must be TG node).
552 :raises RuntimeError: If setting of udev rules fails.
557 cmd = f"rm -f {InterfaceUtil.__UDEV_IF_RULES_FILE}"
558 ret_code, _, _ = ssh.exec_command_sudo(cmd)
559 if int(ret_code) != 0:
560 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
562 for interface in node[u"interfaces"].values():
563 rule = u'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
564 u'==\\"' + interface[u"mac_address"] + u'\\", NAME=\\"' + \
565 interface[u"name"] + u'\\"'
566 cmd = f"sh -c \"echo '{rule}'\" >> " \
567 f"{InterfaceUtil.__UDEV_IF_RULES_FILE}'"
569 ret_code, _, _ = ssh.exec_command_sudo(cmd)
570 if int(ret_code) != 0:
571 raise RuntimeError(f"'{cmd}' failed on '{node[u'host']}'")
573 cmd = u"/etc/init.d/udev restart"
574 ssh.exec_command_sudo(cmd)
577 def tg_set_interfaces_default_driver(node):
578 """Set interfaces default driver specified in topology yaml file.
580 :param node: Node to setup interfaces driver on (must be TG node).
583 for interface in node[u"interfaces"].values():
584 InterfaceUtil.tg_set_interface_driver(
585 node, interface[u"pci_address"], interface[u"driver"]
589 def update_vpp_interface_data_on_node(node):
590 """Update vpp generated interface data for a given node in DICT__nodes.
592 Updates interface names, software if index numbers and any other details
593 generated specifically by vpp that are unknown before testcase run.
594 It does this by dumping interface list from all devices using python
595 api, and pairing known information from topology (mac address) to state
598 :param node: Node selected from DICT__nodes.
601 interface_list = InterfaceUtil.vpp_get_interface_data(node)
602 interface_dict = dict()
603 for ifc in interface_list:
604 interface_dict[ifc[u"l2_address"]] = ifc
606 for if_name, if_data in node[u"interfaces"].items():
607 ifc_dict = interface_dict.get(if_data[u"mac_address"])
608 if ifc_dict is not None:
609 if_data[u"name"] = ifc_dict[u"interface_name"]
610 if_data[u"vpp_sw_index"] = ifc_dict[u"sw_if_index"]
611 if_data[u"mtu"] = ifc_dict[u"mtu"][0]
613 f"Interface {if_name} found by MAC "
614 f"{if_data[u'mac_address']}"
618 f"Interface {if_name} not found by MAC "
619 f"{if_data[u'mac_address']}"
621 if_data[u"vpp_sw_index"] = None
624 def update_nic_interface_names(node):
625 """Update interface names based on nic type and PCI address.
627 This method updates interface names in the same format as VPP does.
629 :param node: Node dictionary.
632 for ifc in node[u"interfaces"].values():
633 if_pci = ifc[u"pci_address"].replace(u".", u":").split(u":")
634 loc = f"{int(if_pci[1], 16):x}/{int(if_pci[2], 16):x}/" \
635 f"{int(if_pci[3], 16):x}"
636 if ifc[u"model"] == u"Intel-XL710":
637 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
638 elif ifc[u"model"] == u"Intel-X710":
639 ifc[u"name"] = f"TenGigabitEthernet{loc}"
640 elif ifc[u"model"] == u"Intel-X520-DA2":
641 ifc[u"name"] = f"TenGigabitEthernet{loc}"
642 elif ifc[u"model"] == u"Cisco-VIC-1385":
643 ifc[u"name"] = f"FortyGigabitEthernet{loc}"
644 elif ifc[u"model"] == u"Cisco-VIC-1227":
645 ifc[u"name"] = f"TenGigabitEthernet{loc}"
647 ifc[u"name"] = f"UnknownEthernet{loc}"
650 def update_nic_interface_names_on_all_duts(nodes):
651 """Update interface names based on nic type and PCI address on all DUTs.
653 This method updates interface names in the same format as VPP does.
655 :param nodes: Topology nodes.
658 for node in nodes.values():
659 if node[u"type"] == NodeType.DUT:
660 InterfaceUtil.update_nic_interface_names(node)
663 def update_tg_interface_data_on_node(node, skip_tg_udev=False):
664 """Update interface name for TG/linux node in DICT__nodes.
667 # for dev in `ls /sys/class/net/`;
668 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
669 "52:54:00:9f:82:63": "eth0"
670 "52:54:00:77:ae:a9": "eth1"
671 "52:54:00:e1:8a:0f": "eth2"
672 "00:00:00:00:00:00": "lo"
674 :param node: Node selected from DICT__nodes.
675 :param skip_tg_udev: Skip udev rename on TG node.
677 :type skip_tg_udev: bool
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
702 # Set udev rules for interfaces
704 InterfaceUtil.tg_set_interfaces_udev_rules(node)
707 def iface_update_numa_node(node):
708 """For all interfaces from topology file update numa node based on
709 information from the node.
711 :param node: Node from topology.
714 :raises ValueError: If numa node ia less than 0.
715 :raises RuntimeError: If update of numa node failed.
717 def check_cpu_node_count(node_n, val):
720 if CpuUtils.cpu_node_count(node_n) == 1:
726 for if_key in Topology.get_node_interfaces(node):
727 if_pci = Topology.get_interface_pci_addr(node, if_key)
729 cmd = f"cat /sys/bus/pci/devices/{if_pci}/numa_node"
731 ret, out, _ = ssh.exec_command(cmd)
734 numa_node = check_cpu_node_count(node, out)
737 f"Reading numa location failed for: {if_pci}"
740 Topology.set_interface_numa_node(
741 node, if_key, numa_node
745 raise RuntimeError(f"Update numa node failed for: {if_pci}")
748 def update_all_numa_nodes(nodes, skip_tg=False):
749 """For all nodes and all their interfaces from topology file update numa
750 node information based on information from the node.
752 :param nodes: Nodes in the topology.
753 :param skip_tg: Skip TG node
758 for node in nodes.values():
759 if node[u"type"] == NodeType.DUT:
760 InterfaceUtil.iface_update_numa_node(node)
761 elif node[u"type"] == NodeType.TG and not skip_tg:
762 InterfaceUtil.iface_update_numa_node(node)
765 def update_all_interface_data_on_all_nodes(
766 nodes, skip_tg=False, skip_tg_udev=False, numa_node=False):
767 """Update interface names on all nodes in DICT__nodes.
769 This method updates the topology dictionary by querying interface lists
770 of all nodes mentioned in the topology dictionary.
772 :param nodes: Nodes in the topology.
773 :param skip_tg: Skip TG node.
774 :param skip_tg_udev: Skip udev rename on TG node.
775 :param numa_node: Retrieve numa_node location.
778 :type skip_tg_udev: bool
779 :type numa_node: bool
781 for node_data in nodes.values():
782 if node_data[u"type"] == NodeType.DUT:
783 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
784 elif node_data[u"type"] == NodeType.TG and not skip_tg:
785 InterfaceUtil.update_tg_interface_data_on_node(
786 node_data, skip_tg_udev)
789 if node_data[u"type"] == NodeType.DUT:
790 InterfaceUtil.iface_update_numa_node(node_data)
791 elif node_data[u"type"] == NodeType.TG and not skip_tg:
792 InterfaceUtil.iface_update_numa_node(node_data)
795 def create_vlan_subinterface(node, interface, vlan):
796 """Create VLAN sub-interface on node.
798 :param node: Node to add VLAN subinterface on.
799 :param interface: Interface name or index on which create VLAN
801 :param vlan: VLAN ID of the subinterface to be created.
803 :type interface: str on int
805 :returns: Name and index of created subinterface.
807 :raises RuntimeError: if it is unable to create VLAN subinterface on the
808 node or interface cannot be converted.
810 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
812 cmd = u"create_vlan_subif"
814 sw_if_index=sw_if_index,
817 err_msg = f"Failed to create VLAN sub-interface on host {node[u'host']}"
819 with PapiSocketExecutor(node) as papi_exec:
820 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
822 if_key = Topology.add_new_port(node, u"vlan_subif")
823 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
824 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
825 Topology.update_interface_name(node, if_key, ifc_name)
827 return f"{interface}.{vlan}", sw_if_index
830 def create_vxlan_interface(node, vni, source_ip, destination_ip):
831 """Create VXLAN interface and return sw if index of created interface.
833 :param node: Node where to create VXLAN interface.
834 :param vni: VXLAN Network Identifier.
835 :param source_ip: Source IP of a VXLAN Tunnel End Point.
836 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
840 :type destination_ip: str
841 :returns: SW IF INDEX of created interface.
843 :raises RuntimeError: if it is unable to create VxLAN interface on the
846 src_address = ip_address(source_ip)
847 dst_address = ip_address(destination_ip)
849 cmd = u"vxlan_add_del_tunnel"
852 is_ipv6=1 if src_address.version == 6 else 0,
853 instance=Constants.BITWISE_NON_ZERO,
854 src_address=src_address.packed,
855 dst_address=dst_address.packed,
856 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
858 decap_next_index=Constants.BITWISE_NON_ZERO,
861 err_msg = f"Failed to create VXLAN tunnel interface " \
862 f"on host {node[u'host']}"
863 with PapiSocketExecutor(node) as papi_exec:
864 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
866 if_key = Topology.add_new_port(node, u"vxlan_tunnel")
867 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
868 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
869 Topology.update_interface_name(node, if_key, ifc_name)
874 def set_vxlan_bypass(node, interface=None):
875 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
877 By adding the IPv4 vxlan-bypass graph node to an interface, the node
878 checks for and validate input vxlan packet and bypass ip4-lookup,
879 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
880 This node will cause extra overhead to for non-vxlan packets which is
883 :param node: Node where to set VXLAN bypass.
884 :param interface: Numeric index or name string of a specific interface.
886 :type interface: int or str
887 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
889 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
891 cmd = u"sw_interface_set_vxlan_bypass"
894 sw_if_index=sw_if_index,
897 err_msg = f"Failed to set VXLAN bypass on interface " \
898 f"on host {node[u'host']}"
899 with PapiSocketExecutor(node) as papi_exec:
900 papi_exec.add(cmd, **args).get_replies(err_msg)
903 def vxlan_dump(node, interface=None):
904 """Get VxLAN data for the given interface.
906 :param node: VPP node to get interface data from.
907 :param interface: Numeric index or name string of a specific interface.
908 If None, information about all VxLAN interfaces is returned.
910 :type interface: int or str
911 :returns: Dictionary containing data for the given VxLAN interface or if
912 interface=None, the list of dictionaries with all VxLAN interfaces.
914 :raises TypeError: if the data type of interface is neither basestring
917 def process_vxlan_dump(vxlan_dump):
918 """Process vxlan dump.
920 :param vxlan_dump: Vxlan interface dump.
921 :type vxlan_dump: dict
922 :returns: Processed vxlan interface dump.
925 if vxlan_dump[u"is_ipv6"]:
926 vxlan_dump[u"src_address"] = \
927 ip_address(vxlan_dump[u"src_address"])
928 vxlan_dump[u"dst_address"] = \
929 ip_address(vxlan_dump[u"dst_address"])
931 vxlan_dump[u"src_address"] = \
932 ip_address(vxlan_dump[u"src_address"][0:4])
933 vxlan_dump[u"dst_address"] = \
934 ip_address(vxlan_dump[u"dst_address"][0:4])
937 if interface is not None:
938 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
940 sw_if_index = int(Constants.BITWISE_NON_ZERO)
942 cmd = u"vxlan_tunnel_dump"
944 sw_if_index=sw_if_index
946 err_msg = f"Failed to get VXLAN dump on host {node[u'host']}"
948 with PapiSocketExecutor(node) as papi_exec:
949 details = papi_exec.add(cmd, **args).get_details(err_msg)
951 data = list() if interface is None else dict()
953 if interface is None:
954 data.append(process_vxlan_dump(dump))
955 elif dump[u"sw_if_index"] == sw_if_index:
956 data = process_vxlan_dump(dump)
959 logger.debug(f"VXLAN data:\n{data}")
963 def create_subinterface(
964 node, interface, sub_id, outer_vlan_id=None, inner_vlan_id=None,
966 """Create sub-interface on node. It is possible to set required
967 sub-interface type and VLAN tag(s).
969 :param node: Node to add sub-interface.
970 :param interface: Interface name on which create sub-interface.
971 :param sub_id: ID of the sub-interface to be created.
972 :param outer_vlan_id: Optional outer VLAN ID.
973 :param inner_vlan_id: Optional inner VLAN ID.
974 :param type_subif: Optional type of sub-interface. Values supported by
975 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
978 :type interface: str or int
980 :type outer_vlan_id: int
981 :type inner_vlan_id: int
982 :type type_subif: str
983 :returns: Name and index of created sub-interface.
985 :raises RuntimeError: If it is not possible to create sub-interface.
987 subif_types = type_subif.split()
990 if u"no_tags" in subif_types:
991 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
992 if u"one_tag" in subif_types:
993 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
994 if u"two_tags" in subif_types:
995 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
996 if u"dot1ad" in subif_types:
997 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
998 if u"exact_match" in subif_types:
999 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
1000 if u"default_sub" in subif_types:
1001 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
1002 if type_subif == u"default_sub":
1003 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
1004 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
1006 cmd = u"create_subif"
1008 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1010 sub_if_flags=flags.value if hasattr(flags, u"value")
1012 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
1013 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
1015 err_msg = f"Failed to create sub-interface on host {node[u'host']}"
1016 with PapiSocketExecutor(node) as papi_exec:
1017 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1019 if_key = Topology.add_new_port(node, u"subinterface")
1020 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1021 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1022 Topology.update_interface_name(node, if_key, ifc_name)
1024 return f"{interface}.{sub_id}", sw_if_index
1027 def create_gre_tunnel_interface(node, source_ip, destination_ip):
1028 """Create GRE tunnel interface on node.
1030 :param node: VPP node to add tunnel interface.
1031 :param source_ip: Source of the GRE tunnel.
1032 :param destination_ip: Destination of the GRE tunnel.
1034 :type source_ip: str
1035 :type destination_ip: str
1036 :returns: Name and index of created GRE tunnel interface.
1038 :raises RuntimeError: If unable to create GRE tunnel interface.
1040 cmd = u"gre_tunnel_add_del"
1043 instance=Constants.BITWISE_NON_ZERO,
1045 dst=str(destination_ip),
1053 err_msg = f"Failed to create GRE tunnel interface " \
1054 f"on host {node[u'host']}"
1055 with PapiSocketExecutor(node) as papi_exec:
1056 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1058 if_key = Topology.add_new_port(node, u"gre_tunnel")
1059 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1060 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1061 Topology.update_interface_name(node, if_key, ifc_name)
1063 return ifc_name, sw_if_index
1066 def vpp_create_loopback(node, mac=None):
1067 """Create loopback interface on VPP node.
1069 :param node: Node to create loopback interface on.
1070 :param mac: Optional MAC address for loopback interface.
1073 :returns: SW interface index.
1075 :raises RuntimeError: If it is not possible to create loopback on the
1078 cmd = u"create_loopback"
1080 mac_address=L2Util.mac_to_bin(mac) if mac else 0
1082 err_msg = f"Failed to create loopback interface on host {node[u'host']}"
1083 with PapiSocketExecutor(node) as papi_exec:
1084 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1086 if_key = Topology.add_new_port(node, u"loopback")
1087 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1088 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1089 Topology.update_interface_name(node, if_key, ifc_name)
1091 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1092 Topology.update_interface_mac_address(node, if_key, mac)
1097 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1098 """Create bond interface on VPP node.
1100 :param node: DUT node from topology.
1101 :param mode: Link bonding mode.
1102 :param load_balance: Load balance (optional, valid for xor and lacp
1103 modes, otherwise ignored).
1104 :param mac: MAC address to assign to the bond interface (optional).
1107 :type load_balance: str
1109 :returns: Interface key (name) in topology.
1111 :raises RuntimeError: If it is not possible to create bond interface on
1114 cmd = u"bond_create"
1116 id=int(Constants.BITWISE_NON_ZERO),
1117 use_custom_mac=bool(mac is not None),
1118 mac_address=L2Util.mac_to_bin(mac) if mac else None,
1121 f"BOND_API_MODE_{mode.replace(u'-', u'_').upper()}"
1123 lb=0 if load_balance is None else getattr(
1124 LinkBondLoadBalanceAlgo,
1125 f"BOND_API_LB_ALGO_{load_balance.upper()}"
1129 err_msg = f"Failed to create bond interface on host {node[u'host']}"
1130 with PapiSocketExecutor(node) as papi_exec:
1131 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1133 InterfaceUtil.add_eth_interface(
1134 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_bond"
1136 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1141 def add_eth_interface(
1142 node, ifc_name=None, sw_if_index=None, ifc_pfx=None,
1144 """Add ethernet interface to current topology.
1146 :param node: DUT node from topology.
1147 :param ifc_name: Name of the interface.
1148 :param sw_if_index: SW interface index.
1149 :param ifc_pfx: Interface key prefix.
1150 :param host_if_key: Host interface key from topology file.
1153 :type sw_if_index: int
1155 :type ifc_pfx: host_if_key
1157 if_key = Topology.add_new_port(node, ifc_pfx)
1159 if ifc_name and sw_if_index is None:
1160 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1162 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1163 if sw_if_index and ifc_name is None:
1164 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1165 Topology.update_interface_name(node, if_key, ifc_name)
1166 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1167 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1168 if host_if_key is not None:
1169 Topology.set_interface_numa_node(
1170 node, if_key, Topology.get_interface_numa_node(
1176 def vpp_create_avf_interface(node, if_key, num_rx_queues=None):
1177 """Create AVF interface on VPP node.
1179 :param node: DUT node from topology.
1180 :param if_key: Interface key from topology file of interface
1181 to be bound to i40evf driver.
1182 :param num_rx_queues: Number of RX queues.
1185 :type num_rx_queues: int
1186 :returns: Interface key (name) in topology.
1188 :raises RuntimeError: If it is not possible to create AVF interface on
1191 PapiSocketExecutor.run_cli_cmd(
1192 node, u"set logging class avf level debug"
1196 vf_pci_addr = Topology.get_interface_pci_addr(node, if_key)
1198 pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1200 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1204 err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1205 with PapiSocketExecutor(node) as papi_exec:
1206 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1208 InterfaceUtil.add_eth_interface(
1209 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf",
1212 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1217 def vpp_create_rdma_interface(node, if_key, num_rx_queues=None):
1218 """Create RDMA interface on VPP node.
1220 :param node: DUT node from topology.
1221 :param if_key: Physical interface key from topology file of interface
1222 to be bound to rdma-core driver.
1223 :param num_rx_queues: Number of RX queues.
1226 :type num_rx_queues: int
1227 :returns: Interface key (name) in topology file.
1229 :raises RuntimeError: If it is not possible to create RDMA interface on
1232 cmd = u"rdma_create"
1233 pci_addr = Topology.get_interface_pci_addr(node, if_key)
1235 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1236 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1237 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1241 err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1242 with PapiSocketExecutor(node) as papi_exec:
1243 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1245 InterfaceUtil.add_eth_interface(
1246 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma",
1250 return Topology.get_interface_by_sw_index(node, sw_if_index)
1253 def vpp_enslave_physical_interface(node, interface, bond_if):
1254 """Enslave physical interface to bond interface on VPP node.
1256 :param node: DUT node from topology.
1257 :param interface: Physical interface key from topology file.
1258 :param bond_if: Load balance
1260 :type interface: str
1262 :raises RuntimeError: If it is not possible to enslave physical
1263 interface to bond interface on the node.
1265 cmd = u"bond_enslave"
1267 sw_if_index=Topology.get_interface_sw_index(node, interface),
1268 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1270 is_long_timeout=False
1272 err_msg = f"Failed to enslave physical interface {interface} to bond " \
1273 f"interface {bond_if} on host {node[u'host']}"
1274 with PapiSocketExecutor(node) as papi_exec:
1275 papi_exec.add(cmd, **args).get_reply(err_msg)
1278 def vpp_show_bond_data_on_node(node, verbose=False):
1279 """Show (detailed) bond information on VPP node.
1281 :param node: DUT node from topology.
1282 :param verbose: If detailed information is required or not.
1286 cmd = u"sw_interface_bond_dump"
1287 err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1289 data = f"Bond data on node {node[u'host']}:\n"
1290 with PapiSocketExecutor(node) as papi_exec:
1291 details = papi_exec.add(cmd).get_details(err_msg)
1293 for bond in details:
1294 data += f"{bond[u'interface_name']}\n"
1295 data += u" mode: {m}\n".format(
1296 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1298 data += u" load balance: {lb}\n".format(
1299 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1301 data += f" number of active slaves: {bond[u'active_slaves']}\n"
1303 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1304 node, Topology.get_interface_by_sw_index(
1305 node, bond[u"sw_if_index"]
1308 for slave in slave_data:
1309 if not slave[u"is_passive"]:
1310 data += f" {slave[u'interface_name']}\n"
1311 data += f" number of slaves: {bond[u'slaves']}\n"
1313 for slave in slave_data:
1314 data += f" {slave[u'interface_name']}\n"
1315 data += f" interface id: {bond[u'id']}\n"
1316 data += f" sw_if_index: {bond[u'sw_if_index']}\n"
1320 def vpp_bond_slave_dump(node, interface):
1321 """Get bond interface slave(s) data on VPP node.
1323 :param node: DUT node from topology.
1324 :param interface: Physical interface key from topology file.
1326 :type interface: str
1327 :returns: Bond slave interface data.
1330 cmd = u"sw_interface_slave_dump"
1332 sw_if_index=Topology.get_interface_sw_index(node, interface)
1334 err_msg = f"Failed to get slave dump on host {node[u'host']}"
1336 with PapiSocketExecutor(node) as papi_exec:
1337 details = papi_exec.add(cmd, **args).get_details(err_msg)
1339 logger.debug(f"Slave data:\n{details}")
1343 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1344 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1346 :param nodes: Nodes in the topology.
1347 :param verbose: If detailed information is required or not.
1351 for node_data in nodes.values():
1352 if node_data[u"type"] == NodeType.DUT:
1353 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1356 def vpp_enable_input_acl_interface(
1357 node, interface, ip_version, table_index):
1358 """Enable input acl on interface.
1360 :param node: VPP node to setup interface for input acl.
1361 :param interface: Interface to setup input acl.
1362 :param ip_version: Version of IP protocol.
1363 :param table_index: Classify table index.
1365 :type interface: str or int
1366 :type ip_version: str
1367 :type table_index: int
1369 cmd = u"input_acl_set_interface"
1371 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1372 ip4_table_index=table_index if ip_version == u"ip4"
1373 else Constants.BITWISE_NON_ZERO,
1374 ip6_table_index=table_index if ip_version == u"ip6"
1375 else Constants.BITWISE_NON_ZERO,
1376 l2_table_index=table_index if ip_version == u"l2"
1377 else Constants.BITWISE_NON_ZERO,
1379 err_msg = f"Failed to enable input acl on interface {interface}"
1380 with PapiSocketExecutor(node) as papi_exec:
1381 papi_exec.add(cmd, **args).get_reply(err_msg)
1384 def get_interface_classify_table(node, interface):
1385 """Get name of classify table for the given interface.
1387 TODO: Move to Classify.py.
1389 :param node: VPP node to get data from.
1390 :param interface: Name or sw_if_index of a specific interface.
1392 :type interface: str or int
1393 :returns: Classify table name.
1396 if isinstance(interface, str):
1397 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1399 sw_if_index = interface
1401 cmd = u"classify_table_by_interface"
1403 sw_if_index=sw_if_index
1405 err_msg = f"Failed to get classify table name by interface {interface}"
1406 with PapiSocketExecutor(node) as papi_exec:
1407 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1412 def get_sw_if_index(node, interface_name):
1413 """Get sw_if_index for the given interface from actual interface dump.
1415 :param node: VPP node to get interface data from.
1416 :param interface_name: Name of the specific interface.
1418 :type interface_name: str
1419 :returns: sw_if_index of the given interface.
1422 interface_data = InterfaceUtil.vpp_get_interface_data(
1423 node, interface=interface_name
1425 return interface_data.get(u"sw_if_index")
1428 def vxlan_gpe_dump(node, interface_name=None):
1429 """Get VxLAN GPE data for the given interface.
1431 :param node: VPP node to get interface data from.
1432 :param interface_name: Name of the specific interface. If None,
1433 information about all VxLAN GPE interfaces is returned.
1435 :type interface_name: str
1436 :returns: Dictionary containing data for the given VxLAN GPE interface
1437 or if interface=None, the list of dictionaries with all VxLAN GPE
1439 :rtype: dict or list
1441 def process_vxlan_gpe_dump(vxlan_dump):
1442 """Process vxlan_gpe dump.
1444 :param vxlan_dump: Vxlan_gpe nterface dump.
1445 :type vxlan_dump: dict
1446 :returns: Processed vxlan_gpe interface dump.
1449 if vxlan_dump[u"is_ipv6"]:
1450 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1451 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1453 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1454 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1457 if interface_name is not None:
1458 sw_if_index = InterfaceUtil.get_interface_index(
1459 node, interface_name
1462 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1464 cmd = u"vxlan_gpe_tunnel_dump"
1466 sw_if_index=sw_if_index
1468 err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1469 with PapiSocketExecutor(node) as papi_exec:
1470 details = papi_exec.add(cmd, **args).get_details(err_msg)
1472 data = list() if interface_name is None else dict()
1473 for dump in details:
1474 if interface_name is None:
1475 data.append(process_vxlan_gpe_dump(dump))
1476 elif dump[u"sw_if_index"] == sw_if_index:
1477 data = process_vxlan_gpe_dump(dump)
1480 logger.debug(f"VXLAN-GPE data:\n{data}")
1484 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1485 """Assign VPP interface to specific VRF/FIB table.
1487 :param node: VPP node where the FIB and interface are located.
1488 :param interface: Interface to be assigned to FIB.
1489 :param table_id: VRF table ID.
1490 :param ipv6: Assign to IPv6 table. Default False.
1492 :type interface: str or int
1496 cmd = u"sw_interface_set_table"
1498 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1500 vrf_id=int(table_id)
1502 err_msg = f"Failed to assign interface {interface} to FIB table"
1503 with PapiSocketExecutor(node) as papi_exec:
1504 papi_exec.add(cmd, **args).get_reply(err_msg)
1507 def set_linux_interface_mac(
1508 node, interface, mac, namespace=None, vf_id=None):
1509 """Set MAC address for interface in linux.
1511 :param node: Node where to execute command.
1512 :param interface: Interface in namespace.
1513 :param mac: MAC to be assigned to interface.
1514 :param namespace: Execute command in namespace. Optional
1515 :param vf_id: Virtual Function id. Optional
1517 :type interface: str
1519 :type namespace: str
1522 mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1523 else f"address {mac}"
1524 ns_str = f"ip netns exec {namespace}" if namespace else u""
1526 cmd = f"{ns_str} ip link set {interface} {mac_str}"
1527 exec_cmd_no_error(node, cmd, sudo=True)
1530 def set_linux_interface_trust_on(
1531 node, interface, namespace=None, vf_id=None):
1532 """Set trust on (promisc) for interface in linux.
1534 :param node: Node where to execute command.
1535 :param interface: Interface in namespace.
1536 :param namespace: Execute command in namespace. Optional
1537 :param vf_id: Virtual Function id. Optional
1539 :type interface: str
1540 :type namespace: str
1543 trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1544 ns_str = f"ip netns exec {namespace}" if namespace else u""
1546 cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1547 exec_cmd_no_error(node, cmd, sudo=True)
1550 def set_linux_interface_spoof_off(
1551 node, interface, namespace=None, vf_id=None):
1552 """Set spoof off for interface in linux.
1554 :param node: Node where to execute command.
1555 :param interface: Interface in namespace.
1556 :param namespace: Execute command in namespace. Optional
1557 :param vf_id: Virtual Function id. Optional
1559 :type interface: str
1560 :type namespace: str
1563 spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1565 ns_str = f"ip netns exec {namespace}" if namespace else u""
1567 cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1568 exec_cmd_no_error(node, cmd, sudo=True)
1571 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer=u"L2"):
1572 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1573 driver testing on DUT.
1575 :param node: DUT node.
1576 :param ifc_key: Interface key from topology file.
1577 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1578 :param osi_layer: OSI Layer type to initialize TG with.
1579 Default value "L2" sets linux interface spoof off.
1583 :type osi_layer: str
1584 :returns: Virtual Function topology interface keys.
1586 :raises RuntimeError: If a reason preventing initialization is found.
1588 # Read PCI address and driver.
1589 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1590 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1591 uio_driver = Topology.get_uio_driver(node)
1592 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1593 if kernel_driver not in (u"i40e", u"i40evf"):
1595 f"AVF needs i40e-compatible driver, not {kernel_driver} "
1596 f"at node {node[u'host']} ifc {ifc_key}"
1598 current_driver = DUTSetup.get_pci_dev_driver(
1599 node, pf_pci_addr.replace(u":", r"\:"))
1601 VPPUtil.stop_vpp_service(node)
1602 if current_driver != kernel_driver:
1603 # PCI device must be re-bound to kernel driver before creating VFs.
1604 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1605 # Stop VPP to prevent deadlock.
1606 # Unbind from current driver.
1607 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1608 # Bind to kernel driver.
1609 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1611 # Initialize PCI VFs.
1612 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1615 # Set MAC address and bind each virtual function to uio driver.
1616 for vf_id in range(numvfs):
1617 vf_mac_addr = u":".join(
1618 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1619 pf_mac_addr[5], f"{vf_id:02x}"
1623 pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1624 InterfaceUtil.set_linux_interface_trust_on(
1625 node, pf_dev, vf_id=vf_id
1627 if osi_layer == u"L2":
1628 InterfaceUtil.set_linux_interface_spoof_off(
1629 node, pf_dev, vf_id=vf_id
1631 InterfaceUtil.set_linux_interface_mac(
1632 node, pf_dev, vf_mac_addr, vf_id=vf_id
1635 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1636 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1638 # Add newly created ports into topology file
1639 vf_ifc_name = f"{ifc_key}_vif"
1640 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1641 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1642 Topology.update_interface_name(
1643 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1645 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1646 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1647 Topology.set_interface_numa_node(
1648 node, vf_ifc_key, Topology.get_interface_numa_node(
1652 vf_ifc_keys.append(vf_ifc_key)
1657 def vpp_sw_interface_rx_placement_dump(node):
1658 """Dump VPP interface RX placement on node.
1660 :param node: Node to run command on.
1662 :returns: Thread mapping information as a list of dictionaries.
1665 cmd = u"sw_interface_rx_placement_dump"
1666 err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1667 with PapiSocketExecutor(node) as papi_exec:
1668 for ifc in node[u"interfaces"].values():
1669 if ifc[u"vpp_sw_index"] is not None:
1670 papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1671 details = papi_exec.get_details(err_msg)
1672 return sorted(details, key=lambda k: k[u"sw_if_index"])
1675 def vpp_sw_interface_set_rx_placement(
1676 node, sw_if_index, queue_id, worker_id):
1677 """Set interface RX placement to worker on node.
1679 :param node: Node to run command on.
1680 :param sw_if_index: VPP SW interface index.
1681 :param queue_id: VPP interface queue ID.
1682 :param worker_id: VPP worker ID (indexing from 0).
1684 :type sw_if_index: int
1686 :type worker_id: int
1687 :raises RuntimeError: If failed to run command on host or if no API
1690 cmd = u"sw_interface_set_rx_placement"
1691 err_msg = f"Failed to set interface RX placement to worker " \
1692 f"on host {node[u'host']}!"
1694 sw_if_index=sw_if_index,
1696 worker_id=worker_id,
1699 with PapiSocketExecutor(node) as papi_exec:
1700 papi_exec.add(cmd, **args).get_reply(err_msg)
1703 def vpp_round_robin_rx_placement(node, prefix):
1704 """Set Round Robin interface RX placement on all worker threads
1707 :param node: Topology nodes.
1708 :param prefix: Interface name prefix.
1713 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1716 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1717 for interface in node[u"interfaces"].values():
1718 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
1719 and prefix in interface[u"name"]:
1720 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1721 node, placement[u"sw_if_index"], placement[u"queue_id"],
1722 worker_id % worker_cnt
1727 def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1728 """Set Round Robin interface RX placement on all worker threads
1731 :param nodes: Topology nodes.
1732 :param prefix: Interface name prefix.
1736 for node in nodes.values():
1737 if node[u"type"] == NodeType.DUT:
1738 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)