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(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1142 """Add ethernet interface to current topology.
1144 :param node: DUT node from topology.
1145 :param ifc_name: Name of the interface.
1146 :param sw_if_index: SW interface index.
1147 :param ifc_pfx: Interface key prefix.
1150 :type sw_if_index: int
1153 if_key = Topology.add_new_port(node, ifc_pfx)
1155 if ifc_name and sw_if_index is None:
1156 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1158 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1159 if sw_if_index and ifc_name is None:
1160 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1161 Topology.update_interface_name(node, if_key, ifc_name)
1162 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1163 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1166 def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1167 """Create AVF interface on VPP node.
1169 :param node: DUT node from topology.
1170 :param vf_pci_addr: PCI address binded to i40evf driver.
1171 :param num_rx_queues: Number of RX queues.
1173 :type vf_pci_addr: str
1174 :type num_rx_queues: int
1175 :returns: Interface key (name) in topology.
1177 :raises RuntimeError: If it is not possible to create AVF interface on
1180 PapiSocketExecutor.run_cli_cmd(
1181 node, u"set logging class avf level debug"
1186 pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1188 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1192 err_msg = f"Failed to create AVF interface on host {node[u'host']}"
1193 with PapiSocketExecutor(node) as papi_exec:
1194 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1196 InterfaceUtil.add_eth_interface(
1197 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_avf"
1199 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1204 def vpp_create_rdma_interface(node, pci_addr, num_rx_queues=None):
1205 """Create RDMA interface on VPP node.
1207 :param node: DUT node from topology.
1208 :param pci_addr: PCI address binded to rdma-core driver.
1209 :param num_rx_queues: Number of RX queues.
1212 :type num_rx_queues: int
1213 :returns: Interface key (name) in topology.
1215 :raises RuntimeError: If it is not possible to create RDMA interface on
1218 cmd = u"rdma_create"
1220 name=InterfaceUtil.pci_to_eth(node, pci_addr),
1221 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1222 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1226 err_msg = f"Failed to create RDMA interface on host {node[u'host']}"
1227 with PapiSocketExecutor(node) as papi_exec:
1228 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1230 InterfaceUtil.add_eth_interface(
1231 node, sw_if_index=sw_if_index, ifc_pfx=u"eth_rdma"
1233 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1238 def vpp_enslave_physical_interface(node, interface, bond_if):
1239 """Enslave physical interface to bond interface on VPP node.
1241 :param node: DUT node from topology.
1242 :param interface: Physical interface key from topology file.
1243 :param bond_if: Load balance
1245 :type interface: str
1247 :raises RuntimeError: If it is not possible to enslave physical
1248 interface to bond interface on the node.
1250 cmd = u"bond_enslave"
1252 sw_if_index=Topology.get_interface_sw_index(node, interface),
1253 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1255 is_long_timeout=False
1257 err_msg = f"Failed to enslave physical interface {interface} to bond " \
1258 f"interface {bond_if} on host {node[u'host']}"
1259 with PapiSocketExecutor(node) as papi_exec:
1260 papi_exec.add(cmd, **args).get_reply(err_msg)
1263 def vpp_show_bond_data_on_node(node, verbose=False):
1264 """Show (detailed) bond information on VPP node.
1266 :param node: DUT node from topology.
1267 :param verbose: If detailed information is required or not.
1271 cmd = u"sw_interface_bond_dump"
1272 err_msg = f"Failed to get bond interface dump on host {node[u'host']}"
1274 data = f"Bond data on node {node[u'host']}:\n"
1275 with PapiSocketExecutor(node) as papi_exec:
1276 details = papi_exec.add(cmd).get_details(err_msg)
1278 for bond in details:
1279 data += f"{bond[u'interface_name']}\n"
1280 data += u" mode: {m}\n".format(
1281 m=bond[u"mode"].name.replace(u"BOND_API_MODE_", u"").lower()
1283 data += u" load balance: {lb}\n".format(
1284 lb=bond[u"lb"].name.replace(u"BOND_API_LB_ALGO_", u"").lower()
1286 data += f" number of active slaves: {bond[u'active_slaves']}\n"
1288 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1289 node, Topology.get_interface_by_sw_index(
1290 node, bond[u"sw_if_index"]
1293 for slave in slave_data:
1294 if not slave[u"is_passive"]:
1295 data += f" {slave[u'interface_name']}\n"
1296 data += f" number of slaves: {bond[u'slaves']}\n"
1298 for slave in slave_data:
1299 data += f" {slave[u'interface_name']}\n"
1300 data += f" interface id: {bond[u'id']}\n"
1301 data += f" sw_if_index: {bond[u'sw_if_index']}\n"
1305 def vpp_bond_slave_dump(node, interface):
1306 """Get bond interface slave(s) data on VPP node.
1308 :param node: DUT node from topology.
1309 :param interface: Physical interface key from topology file.
1311 :type interface: str
1312 :returns: Bond slave interface data.
1315 cmd = u"sw_interface_slave_dump"
1317 sw_if_index=Topology.get_interface_sw_index(node, interface)
1319 err_msg = f"Failed to get slave dump on host {node[u'host']}"
1321 with PapiSocketExecutor(node) as papi_exec:
1322 details = papi_exec.add(cmd, **args).get_details(err_msg)
1324 logger.debug(f"Slave data:\n{details}")
1328 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1329 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1331 :param nodes: Nodes in the topology.
1332 :param verbose: If detailed information is required or not.
1336 for node_data in nodes.values():
1337 if node_data[u"type"] == NodeType.DUT:
1338 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1341 def vpp_enable_input_acl_interface(
1342 node, interface, ip_version, table_index):
1343 """Enable input acl on interface.
1345 :param node: VPP node to setup interface for input acl.
1346 :param interface: Interface to setup input acl.
1347 :param ip_version: Version of IP protocol.
1348 :param table_index: Classify table index.
1350 :type interface: str or int
1351 :type ip_version: str
1352 :type table_index: int
1354 cmd = u"input_acl_set_interface"
1356 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1357 ip4_table_index=table_index if ip_version == u"ip4"
1358 else Constants.BITWISE_NON_ZERO,
1359 ip6_table_index=table_index if ip_version == u"ip6"
1360 else Constants.BITWISE_NON_ZERO,
1361 l2_table_index=table_index if ip_version == u"l2"
1362 else Constants.BITWISE_NON_ZERO,
1364 err_msg = f"Failed to enable input acl on interface {interface}"
1365 with PapiSocketExecutor(node) as papi_exec:
1366 papi_exec.add(cmd, **args).get_reply(err_msg)
1369 def get_interface_classify_table(node, interface):
1370 """Get name of classify table for the given interface.
1372 TODO: Move to Classify.py.
1374 :param node: VPP node to get data from.
1375 :param interface: Name or sw_if_index of a specific interface.
1377 :type interface: str or int
1378 :returns: Classify table name.
1381 if isinstance(interface, str):
1382 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1384 sw_if_index = interface
1386 cmd = u"classify_table_by_interface"
1388 sw_if_index=sw_if_index
1390 err_msg = f"Failed to get classify table name by interface {interface}"
1391 with PapiSocketExecutor(node) as papi_exec:
1392 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1397 def get_sw_if_index(node, interface_name):
1398 """Get sw_if_index for the given interface from actual interface dump.
1400 :param node: VPP node to get interface data from.
1401 :param interface_name: Name of the specific interface.
1403 :type interface_name: str
1404 :returns: sw_if_index of the given interface.
1407 interface_data = InterfaceUtil.vpp_get_interface_data(
1408 node, interface=interface_name
1410 return interface_data.get(u"sw_if_index")
1413 def vxlan_gpe_dump(node, interface_name=None):
1414 """Get VxLAN GPE data for the given interface.
1416 :param node: VPP node to get interface data from.
1417 :param interface_name: Name of the specific interface. If None,
1418 information about all VxLAN GPE interfaces is returned.
1420 :type interface_name: str
1421 :returns: Dictionary containing data for the given VxLAN GPE interface
1422 or if interface=None, the list of dictionaries with all VxLAN GPE
1424 :rtype: dict or list
1426 def process_vxlan_gpe_dump(vxlan_dump):
1427 """Process vxlan_gpe dump.
1429 :param vxlan_dump: Vxlan_gpe nterface dump.
1430 :type vxlan_dump: dict
1431 :returns: Processed vxlan_gpe interface dump.
1434 if vxlan_dump[u"is_ipv6"]:
1435 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"])
1436 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"])
1438 vxlan_dump[u"local"] = ip_address(vxlan_dump[u"local"][0:4])
1439 vxlan_dump[u"remote"] = ip_address(vxlan_dump[u"remote"][0:4])
1442 if interface_name is not None:
1443 sw_if_index = InterfaceUtil.get_interface_index(
1444 node, interface_name
1447 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1449 cmd = u"vxlan_gpe_tunnel_dump"
1451 sw_if_index=sw_if_index
1453 err_msg = f"Failed to get VXLAN-GPE dump on host {node[u'host']}"
1454 with PapiSocketExecutor(node) as papi_exec:
1455 details = papi_exec.add(cmd, **args).get_details(err_msg)
1457 data = list() if interface_name is None else dict()
1458 for dump in details:
1459 if interface_name is None:
1460 data.append(process_vxlan_gpe_dump(dump))
1461 elif dump[u"sw_if_index"] == sw_if_index:
1462 data = process_vxlan_gpe_dump(dump)
1465 logger.debug(f"VXLAN-GPE data:\n{data}")
1469 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1470 """Assign VPP interface to specific VRF/FIB table.
1472 :param node: VPP node where the FIB and interface are located.
1473 :param interface: Interface to be assigned to FIB.
1474 :param table_id: VRF table ID.
1475 :param ipv6: Assign to IPv6 table. Default False.
1477 :type interface: str or int
1481 cmd = u"sw_interface_set_table"
1483 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1485 vrf_id=int(table_id)
1487 err_msg = f"Failed to assign interface {interface} to FIB table"
1488 with PapiSocketExecutor(node) as papi_exec:
1489 papi_exec.add(cmd, **args).get_reply(err_msg)
1492 def set_linux_interface_mac(
1493 node, interface, mac, namespace=None, vf_id=None):
1494 """Set MAC address for interface in linux.
1496 :param node: Node where to execute command.
1497 :param interface: Interface in namespace.
1498 :param mac: MAC to be assigned to interface.
1499 :param namespace: Execute command in namespace. Optional
1500 :param vf_id: Virtual Function id. Optional
1502 :type interface: str
1504 :type namespace: str
1507 mac_str = f"vf {vf_id} mac {mac}" if vf_id is not None \
1508 else f"address {mac}"
1509 ns_str = f"ip netns exec {namespace}" if namespace else u""
1511 cmd = f"{ns_str} ip link set {interface} {mac_str}"
1512 exec_cmd_no_error(node, cmd, sudo=True)
1515 def set_linux_interface_trust_on(
1516 node, interface, namespace=None, vf_id=None):
1517 """Set trust on (promisc) for interface in linux.
1519 :param node: Node where to execute command.
1520 :param interface: Interface in namespace.
1521 :param namespace: Execute command in namespace. Optional
1522 :param vf_id: Virtual Function id. Optional
1524 :type interface: str
1525 :type namespace: str
1528 trust_str = f"vf {vf_id} trust on" if vf_id is not None else u"trust on"
1529 ns_str = f"ip netns exec {namespace}" if namespace else u""
1531 cmd = f"{ns_str} ip link set dev {interface} {trust_str}"
1532 exec_cmd_no_error(node, cmd, sudo=True)
1535 def set_linux_interface_spoof_off(
1536 node, interface, namespace=None, vf_id=None):
1537 """Set spoof off for interface in linux.
1539 :param node: Node where to execute command.
1540 :param interface: Interface in namespace.
1541 :param namespace: Execute command in namespace. Optional
1542 :param vf_id: Virtual Function id. Optional
1544 :type interface: str
1545 :type namespace: str
1548 spoof_str = f"vf {vf_id} spoof off" if vf_id is not None \
1550 ns_str = f"ip netns exec {namespace}" if namespace else u""
1552 cmd = f"{ns_str} ip link set dev {interface} {spoof_str}"
1553 exec_cmd_no_error(node, cmd, sudo=True)
1556 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer=u"L2"):
1557 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1558 driver testing on DUT.
1560 :param node: DUT node.
1561 :param ifc_key: Interface key from topology file.
1562 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1563 :param osi_layer: OSI Layer type to initialize TG with.
1564 Default value "L2" sets linux interface spoof off.
1568 :type osi_layer: str
1569 :returns: Virtual Function topology interface keys.
1571 :raises RuntimeError: If a reason preventing initialization is found.
1573 # Read PCI address and driver.
1574 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1575 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1576 uio_driver = Topology.get_uio_driver(node)
1577 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1578 if kernel_driver not in (u"i40e", u"i40evf"):
1580 f"AVF needs i40e-compatible driver, not {kernel_driver} "
1581 f"at node {node[u'host']} ifc {ifc_key}"
1583 current_driver = DUTSetup.get_pci_dev_driver(
1584 node, pf_pci_addr.replace(u":", r"\:"))
1586 VPPUtil.stop_vpp_service(node)
1587 if current_driver != kernel_driver:
1588 # PCI device must be re-bound to kernel driver before creating VFs.
1589 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1590 # Stop VPP to prevent deadlock.
1591 # Unbind from current driver.
1592 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1593 # Bind to kernel driver.
1594 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1596 # Initialize PCI VFs.
1597 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1600 # Set MAC address and bind each virtual function to uio driver.
1601 for vf_id in range(numvfs):
1602 vf_mac_addr = u":".join(
1603 [pf_mac_addr[0], pf_mac_addr[2], pf_mac_addr[3], pf_mac_addr[4],
1604 pf_mac_addr[5], f"{vf_id:02x}"
1608 pf_dev = f"`basename /sys/bus/pci/devices/{pf_pci_addr}/net/*`"
1609 InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1611 if osi_layer == u"L2":
1612 InterfaceUtil.set_linux_interface_spoof_off(
1613 node, pf_dev, vf_id=vf_id
1615 InterfaceUtil.set_linux_interface_mac(
1616 node, pf_dev, vf_mac_addr, vf_id=vf_id
1619 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1620 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1622 # Add newly created ports into topology file
1623 vf_ifc_name = f"{ifc_key}_vif"
1624 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1625 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1626 Topology.update_interface_name(
1627 node, vf_ifc_key, vf_ifc_name+str(vf_id+1)
1629 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1630 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1631 vf_ifc_keys.append(vf_ifc_key)
1636 def vpp_sw_interface_rx_placement_dump(node):
1637 """Dump VPP interface RX placement on node.
1639 :param node: Node to run command on.
1641 :returns: Thread mapping information as a list of dictionaries.
1644 cmd = u"sw_interface_rx_placement_dump"
1645 err_msg = f"Failed to run '{cmd}' PAPI command on host {node[u'host']}!"
1646 with PapiSocketExecutor(node) as papi_exec:
1647 for ifc in node[u"interfaces"].values():
1648 if ifc[u"vpp_sw_index"] is not None:
1649 papi_exec.add(cmd, sw_if_index=ifc[u"vpp_sw_index"])
1650 details = papi_exec.get_details(err_msg)
1651 return sorted(details, key=lambda k: k[u"sw_if_index"])
1654 def vpp_sw_interface_set_rx_placement(
1655 node, sw_if_index, queue_id, worker_id):
1656 """Set interface RX placement to worker on node.
1658 :param node: Node to run command on.
1659 :param sw_if_index: VPP SW interface index.
1660 :param queue_id: VPP interface queue ID.
1661 :param worker_id: VPP worker ID (indexing from 0).
1663 :type sw_if_index: int
1665 :type worker_id: int
1666 :raises RuntimeError: If failed to run command on host or if no API
1669 cmd = u"sw_interface_set_rx_placement"
1670 err_msg = f"Failed to set interface RX placement to worker " \
1671 f"on host {node[u'host']}!"
1673 sw_if_index=sw_if_index,
1675 worker_id=worker_id,
1678 with PapiSocketExecutor(node) as papi_exec:
1679 papi_exec.add(cmd, **args).get_reply(err_msg)
1682 def vpp_round_robin_rx_placement(node, prefix):
1683 """Set Round Robin interface RX placement on all worker threads
1686 :param node: Topology nodes.
1687 :param prefix: Interface name prefix.
1692 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1695 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1696 for interface in node[u"interfaces"].values():
1697 if placement[u"sw_if_index"] == interface[u"vpp_sw_index"] \
1698 and prefix in interface[u"name"]:
1699 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1700 node, placement[u"sw_if_index"], placement[u"queue_id"],
1701 worker_id % worker_cnt
1706 def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1707 """Set Round Robin interface RX placement on all worker threads
1710 :param nodes: Topology nodes.
1711 :param prefix: Interface name prefix.
1715 for node in nodes.values():
1716 if node[u"type"] == NodeType.DUT:
1717 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)