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
18 from enum import IntEnum
19 from ipaddress import ip_address
20 from robot.api import logger
22 from resources.libraries.python.Constants import Constants
23 from resources.libraries.python.CpuUtils import CpuUtils
24 from resources.libraries.python.DUTSetup import DUTSetup
25 from resources.libraries.python.L2Util import L2Util
26 from resources.libraries.python.PapiExecutor import PapiSocketExecutor
27 from resources.libraries.python.parsers.JsonParser import JsonParser
28 from resources.libraries.python.ssh import SSH, exec_cmd_no_error
29 from resources.libraries.python.topology import NodeType, Topology
30 from resources.libraries.python.VPPUtil import VPPUtil
33 class InterfaceStatusFlags(IntEnum):
34 """Interface status flags."""
35 IF_STATUS_API_FLAG_ADMIN_UP = 1
36 IF_STATUS_API_FLAG_LINK_UP = 2
39 class MtuProto(IntEnum):
44 MTU_PROTO_API_MPLS = 3
48 class LinkDuplex(IntEnum):
50 LINK_DUPLEX_API_UNKNOWN = 0
51 LINK_DUPLEX_API_HALF = 1
52 LINK_DUPLEX_API_FULL = 2
55 class SubInterfaceFlags(IntEnum):
56 """Sub-interface flags."""
57 SUB_IF_API_FLAG_NO_TAGS = 1
58 SUB_IF_API_FLAG_ONE_TAG = 2
59 SUB_IF_API_FLAG_TWO_TAGS = 4
60 SUB_IF_API_FLAG_DOT1AD = 8
61 SUB_IF_API_FLAG_EXACT_MATCH = 16
62 SUB_IF_API_FLAG_DEFAULT = 32
63 SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY = 64
64 SUB_IF_API_FLAG_INNER_VLAN_ID_ANY = 128
65 SUB_IF_API_FLAG_DOT1AH = 256
68 class RxMode(IntEnum):
70 RX_MODE_API_UNKNOWN = 0
71 RX_MODE_API_POLLING = 1
72 RX_MODE_API_INTERRUPT = 2
73 RX_MODE_API_ADAPTIVE = 3
74 RX_MODE_API_DEFAULT = 4
77 class IfType(IntEnum):
80 IF_API_TYPE_HARDWARE = 0
87 class LinkBondLoadBalanceAlgo(IntEnum):
88 """Link bonding load balance algorithm."""
89 BOND_API_LB_ALGO_L2 = 0
90 BOND_API_LB_ALGO_L34 = 1
91 BOND_API_LB_ALGO_L23 = 2
92 BOND_API_LB_ALGO_RR = 3
93 BOND_API_LB_ALGO_BC = 4
94 BOND_API_LB_ALGO_AB = 5
97 class LinkBondMode(IntEnum):
98 """Link bonding mode."""
99 BOND_API_MODE_ROUND_ROBIN = 1
100 BOND_API_MODE_ACTIVE_BACKUP = 2
101 BOND_API_MODE_XOR = 3
102 BOND_API_MODE_BROADCAST = 4
103 BOND_API_MODE_LACP = 5
106 class InterfaceUtil(object):
107 """General utilities for managing interfaces"""
109 __UDEV_IF_RULES_FILE = '/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(':')[0:2])
122 pci.extend(pci_str.split(':')[2].split('.'))
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 to Linux ethernet name.
131 :param pci_str: PCI address.
133 :returns: Ethernet name.
136 cmd = ('basename /sys/bus/pci/devices/{pci_str}/net/*'.
137 format(pci_str=pci_str))
139 stdout, _ = exec_cmd_no_error(node, cmd)
141 raise RuntimeError("Cannot convert {pci_str} to ethernet name!".
142 format(pci_str=pci_str))
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('Wrong interface format {ifc}: {err}'.format(
166 ifc=interface, err=err.message))
171 def set_interface_state(node, interface, state, if_type='key'):
172 """Set interface state on a node.
174 Function can be used for DUTs as well as for TGs.
176 :param node: Node where the interface is.
177 :param interface: Interface key or sw_if_index or name.
178 :param state: One of 'up' or 'down'.
179 :param if_type: Interface type
181 :type interface: str or int
185 :raises ValueError: If the interface type is unknown.
186 :raises ValueError: If the state of interface is unexpected.
187 :raises ValueError: If the node has an unknown node type.
190 if isinstance(interface, basestring):
191 sw_if_index = Topology.get_interface_sw_index(node, interface)
192 iface_name = Topology.get_interface_name(node, interface)
194 sw_if_index = interface
195 elif if_type == 'name':
196 iface_key = Topology.get_interface_by_name(node, interface)
197 if iface_key is not None:
198 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
199 iface_name = interface
201 raise ValueError('Unknown if_type: {type}'.format(type=if_type))
203 if node['type'] == NodeType.DUT:
205 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
206 elif state == 'down':
209 raise ValueError('Unexpected interface state: {state}'.format(
211 cmd = 'sw_interface_set_flags'
212 err_msg = 'Failed to set interface state on host {host}'.format(
215 sw_if_index=int(sw_if_index),
217 with PapiSocketExecutor(node) as papi_exec:
218 papi_exec.add(cmd, **args).get_reply(err_msg)
219 elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
220 cmd = 'ip link set {ifc} {state}'.format(
221 ifc=iface_name, state=state)
222 exec_cmd_no_error(node, cmd, sudo=True)
224 raise ValueError('Node {} has unknown NodeType: "{}"'
225 .format(node['host'], node['type']))
228 def set_interface_ethernet_mtu(node, iface_key, mtu):
229 """Set Ethernet MTU for specified interface.
231 Function can be used only for TGs.
233 :param node: Node where the interface is.
234 :param iface_key: Interface key from topology file.
235 :param mtu: MTU to set.
240 :raises ValueError: If the node type is "DUT".
241 :raises ValueError: If the node has an unknown node type.
243 if node['type'] == NodeType.DUT:
244 raise ValueError('Node {}: Setting Ethernet MTU for interface '
245 'on DUT nodes not supported', node['host'])
246 elif node['type'] == NodeType.TG:
247 iface_name = Topology.get_interface_name(node, iface_key)
248 cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
249 exec_cmd_no_error(node, cmd, sudo=True)
251 raise ValueError('Node {} has unknown NodeType: "{}"'
252 .format(node['host'], node['type']))
255 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
256 """Set default Ethernet MTU on all interfaces on node.
258 Function can be used only for TGs.
260 :param node: Node where to set default MTU.
264 for ifc in node['interfaces']:
265 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
268 def vpp_set_interface_mtu(node, interface, mtu=9200):
269 """Set Ethernet MTU on interface.
271 :param node: VPP node.
272 :param interface: Interface to setup MTU. Default: 9200.
273 :param mtu: Ethernet MTU size in Bytes.
275 :type interface: str or int
278 if isinstance(interface, basestring):
279 sw_if_index = Topology.get_interface_sw_index(node, interface)
281 sw_if_index = interface
283 cmd = 'hw_interface_set_mtu'
284 err_msg = 'Failed to set interface MTU on host {host}'.format(
286 args = dict(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("Setting MTU failed. Expected?\n{err}".format(
297 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
298 """Set Ethernet MTU on all interfaces.
300 :param node: VPP node.
301 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
305 for interface in node['interfaces']:
306 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
309 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
310 """Set Ethernet MTU on all interfaces on all DUTs.
312 :param nodes: VPP nodes.
313 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
317 for node in nodes.values():
318 if node['type'] == NodeType.DUT:
319 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
322 def vpp_node_interfaces_ready_wait(node, retries=15):
323 """Wait until all interfaces with admin-up are in link-up state.
325 :param node: Node to wait on.
326 :param retries: Number of retries to check interface status (optional,
331 :raises RuntimeError: If any interface is not in link-up state after
332 defined number of retries.
334 for _ in xrange(0, retries):
336 out = InterfaceUtil.vpp_get_interface_data(node)
337 for interface in out:
338 if interface.get('flags') == 1:
339 not_ready.append(interface.get('interface_name'))
343 logger.debug('Interfaces still not in link-up state:\n{ifs} '
344 '\nWaiting...'.format(ifs=not_ready))
347 err = 'Timeout, interfaces not up:\n{ifs}'.format(ifs=not_ready) \
348 if 'not_ready' in locals() else '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['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['l2_address'] = str(if_dump['l2_address'])
392 if_dump['b_dmac'] = str(if_dump['b_dmac'])
393 if_dump['b_smac'] = str(if_dump['b_smac'])
394 if_dump['flags'] = if_dump['flags'].value
395 if_dump['type'] = if_dump['type'].value
396 if_dump['link_duplex'] = if_dump['link_duplex'].value
397 if_dump['sub_if_flags'] = if_dump['sub_if_flags'].value \
398 if hasattr(if_dump['sub_if_flags'], 'value') \
399 else int(if_dump['sub_if_flags'])
403 if interface is not None:
404 if isinstance(interface, basestring):
405 param = 'interface_name'
406 elif isinstance(interface, int):
407 param = 'sw_if_index'
409 raise TypeError('Wrong interface format {ifc}'.format(
414 cmd = 'sw_interface_dump'
416 name_filter_valid=False,
419 err_msg = 'Failed to get interface dump on host {host}'.format(
421 with PapiSocketExecutor(node) as papi_exec:
422 details = papi_exec.add(cmd, **args).get_details(err_msg)
423 logger.debug('Received data:\n{d!r}'.format(d=details))
425 data = list() if interface is None else dict()
427 if interface is None:
428 data.append(process_if_dump(dump))
429 elif str(dump.get(param)).rstrip('\x00') == str(interface):
430 data = process_if_dump(dump)
433 logger.debug('Interface data:\n{if_data}'.format(if_data=data))
437 def vpp_get_interface_name(node, sw_if_index):
438 """Get interface name for the given SW interface index from actual
441 :param node: VPP node to get interface data from.
442 :param sw_if_index: SW interface index of the specific interface.
444 :type sw_if_index: int
445 :returns: Name of the given interface.
448 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
449 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
450 if_data = InterfaceUtil.vpp_get_interface_data(
451 node, if_data['sup_sw_if_index'])
453 return if_data.get('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('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['sup_sw_if_index'] != if_data['sw_if_index']:
484 if_data = InterfaceUtil.vpp_get_interface_data(
485 node, if_data['sup_sw_if_index'])
487 return if_data.get('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 = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
512 .format(pci_addr, old_driver)
513 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
514 if int(ret_code) != 0:
515 raise RuntimeError("'{0}' failed on '{1}'"
516 .format(cmd, node['host']))
518 # Bind to the new driver
519 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
520 .format(pci_addr, driver)
521 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
522 if int(ret_code) != 0:
523 raise RuntimeError("'{0}' failed on '{1}'"
524 .format(cmd, node['host']))
527 def tg_get_interface_driver(node, pci_addr):
528 """Get interface driver from the TG node.
530 :param node: Node to get interface driver on (must be TG node).
531 :param pci_addr: PCI address of the interface.
534 :returns: Interface driver or None if not found.
536 :raises RuntimeError: If PCI rescan or lspci command execution failed.
538 return DUTSetup.get_pci_dev_driver(node, pci_addr)
541 def tg_set_interfaces_udev_rules(node):
542 """Set udev rules for interfaces.
544 Create udev rules file in /etc/udev/rules.d where are rules for each
545 interface used by TG node, based on MAC interface has specific name.
546 So after unbind and bind again to kernel driver interface has same
547 name as before. This must be called after TG has set name for each
548 port in topology dictionary.
550 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
553 :param node: Node to set udev rules on (must be TG node).
555 :raises RuntimeError: If setting of udev rules fails.
560 cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
561 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
562 if int(ret_code) != 0:
563 raise RuntimeError("'{0}' failed on '{1}'"
564 .format(cmd, node['host']))
566 for interface in node['interfaces'].values():
567 rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
568 '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
569 interface['name'] + '\\"'
570 cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
571 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
572 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
573 if int(ret_code) != 0:
574 raise RuntimeError("'{0}' failed on '{1}'"
575 .format(cmd, node['host']))
577 cmd = '/etc/init.d/udev restart'
578 ssh.exec_command_sudo(cmd)
581 def tg_set_interfaces_default_driver(node):
582 """Set interfaces default driver specified in topology yaml file.
584 :param node: Node to setup interfaces driver on (must be TG node).
587 for interface in node['interfaces'].values():
588 InterfaceUtil.tg_set_interface_driver(node,
589 interface['pci_address'],
593 def update_vpp_interface_data_on_node(node):
594 """Update vpp generated interface data for a given node in DICT__nodes.
596 Updates interface names, software if index numbers and any other details
597 generated specifically by vpp that are unknown before testcase run.
598 It does this by dumping interface list from all devices using python
599 api, and pairing known information from topology (mac address) to state
602 :param node: Node selected from DICT__nodes.
605 interface_list = InterfaceUtil.vpp_get_interface_data(node)
606 interface_dict = dict()
607 for ifc in interface_list:
608 interface_dict[ifc['l2_address']] = ifc
610 for if_name, if_data in node['interfaces'].items():
611 ifc_dict = interface_dict.get(if_data['mac_address'])
612 if ifc_dict is not None:
613 if_data['name'] = ifc_dict['interface_name']
614 if_data['vpp_sw_index'] = ifc_dict['sw_if_index']
615 if_data['mtu'] = ifc_dict['mtu'][0]
616 logger.trace('Interface {ifc} found by MAC {mac}'.format(
617 ifc=if_name, mac=if_data['mac_address']))
619 logger.trace('Interface {ifc} not found by MAC {mac}'.format(
620 ifc=if_name, mac=if_data['mac_address']))
621 if_data['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['interfaces'].values():
633 if_pci = ifc['pci_address'].replace('.', ':').split(':')
634 bus = '{:x}'.format(int(if_pci[1], 16))
635 dev = '{:x}'.format(int(if_pci[2], 16))
636 fun = '{:x}'.format(int(if_pci[3], 16))
637 loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
638 if ifc['model'] == 'Intel-XL710':
639 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
640 elif ifc['model'] == 'Intel-X710':
641 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
642 elif ifc['model'] == 'Intel-X520-DA2':
643 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
644 elif ifc['model'] == 'Cisco-VIC-1385':
645 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
646 elif ifc['model'] == 'Cisco-VIC-1227':
647 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
649 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
652 def update_nic_interface_names_on_all_duts(nodes):
653 """Update interface names based on nic type and PCI address on all DUTs.
655 This method updates interface names in the same format as VPP does.
657 :param nodes: Topology nodes.
660 for node in nodes.values():
661 if node['type'] == NodeType.DUT:
662 InterfaceUtil.update_nic_interface_names(node)
665 def update_tg_interface_data_on_node(node, skip_tg_udev=False):
666 """Update interface name for TG/linux node in DICT__nodes.
669 # for dev in `ls /sys/class/net/`;
670 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
671 "52:54:00:9f:82:63": "eth0"
672 "52:54:00:77:ae:a9": "eth1"
673 "52:54:00:e1:8a:0f": "eth2"
674 "00:00:00:00:00:00": "lo"
676 :param node: Node selected from DICT__nodes.
677 :param skip_tg_udev: Skip udev rename on TG node.
679 :type skip_tg_udev: bool
680 :raises RuntimeError: If getting of interface name and MAC fails.
682 # First setup interface driver specified in yaml file
683 InterfaceUtil.tg_set_interfaces_default_driver(node)
685 # Get interface names
689 cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
690 '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
692 (ret_code, stdout, _) = ssh.exec_command(cmd)
693 if int(ret_code) != 0:
694 raise RuntimeError('Get interface name and MAC failed')
695 tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
696 interfaces = JsonParser().parse_data(tmp)
697 for interface in node['interfaces'].values():
698 name = interfaces.get(interface['mac_address'])
701 interface['name'] = name
703 # Set udev rules for interfaces
705 InterfaceUtil.tg_set_interfaces_udev_rules(node)
708 def iface_update_numa_node(node):
709 """For all interfaces from topology file update numa node based on
710 information from the node.
712 :param node: Node from topology.
715 :raises ValueError: If numa node ia less than 0.
716 :raises RuntimeError: If update of numa node failes.
719 for if_key in Topology.get_node_interfaces(node):
720 if_pci = Topology.get_interface_pci_addr(node, if_key)
722 cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
724 (ret, out, _) = ssh.exec_command(cmd)
729 if CpuUtils.cpu_node_count(node) == 1:
734 logger.trace('Reading numa location failed for: {0}'
737 Topology.set_interface_numa_node(node, if_key,
741 raise RuntimeError('Update numa node failed for: {0}'
745 def update_all_numa_nodes(nodes, skip_tg=False):
746 """For all nodes and all their interfaces from topology file update numa
747 node information based on information from the node.
749 :param nodes: Nodes in the topology.
750 :param skip_tg: Skip TG node
755 for node in nodes.values():
756 if node['type'] == NodeType.DUT:
757 InterfaceUtil.iface_update_numa_node(node)
758 elif node['type'] == NodeType.TG and not skip_tg:
759 InterfaceUtil.iface_update_numa_node(node)
762 def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
765 """Update interface names on all nodes in DICT__nodes.
767 This method updates the topology dictionary by querying interface lists
768 of all nodes mentioned in the topology dictionary.
770 :param nodes: Nodes in the topology.
771 :param skip_tg: Skip TG node.
772 :param skip_tg_udev: Skip udev rename on TG node.
773 :param numa_node: Retrieve numa_node location.
776 :type skip_tg_udev: bool
777 :type numa_node: bool
779 for node_data in nodes.values():
780 if node_data['type'] == NodeType.DUT:
781 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
782 elif node_data['type'] == NodeType.TG and not skip_tg:
783 InterfaceUtil.update_tg_interface_data_on_node(
784 node_data, skip_tg_udev)
787 if node_data['type'] == NodeType.DUT:
788 InterfaceUtil.iface_update_numa_node(node_data)
789 elif node_data['type'] == NodeType.TG and not skip_tg:
790 InterfaceUtil.iface_update_numa_node(node_data)
793 def create_vlan_subinterface(node, interface, vlan):
794 """Create VLAN sub-interface on node.
796 :param node: Node to add VLAN subinterface on.
797 :param interface: Interface name or index on which create VLAN
799 :param vlan: VLAN ID of the subinterface to be created.
801 :type interface: str on int
803 :returns: Name and index of created subinterface.
805 :raises RuntimeError: if it is unable to create VLAN subinterface on the
806 node or interface cannot be converted.
808 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
810 cmd = 'create_vlan_subif'
812 sw_if_index=sw_if_index,
815 err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
817 with PapiSocketExecutor(node) as papi_exec:
818 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
820 if_key = Topology.add_new_port(node, 'vlan_subif')
821 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
822 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
823 Topology.update_interface_name(node, if_key, ifc_name)
825 return '{ifc}.{vlan}'.format(ifc=interface, vlan=vlan), sw_if_index
828 def create_vxlan_interface(node, vni, source_ip, destination_ip):
829 """Create VXLAN interface and return sw if index of created interface.
831 :param node: Node where to create VXLAN interface.
832 :param vni: VXLAN Network Identifier.
833 :param source_ip: Source IP of a VXLAN Tunnel End Point.
834 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
838 :type destination_ip: str
839 :returns: SW IF INDEX of created interface.
841 :raises RuntimeError: if it is unable to create VxLAN interface on the
844 src_address = ip_address(unicode(source_ip))
845 dst_address = ip_address(unicode(destination_ip))
847 cmd = 'vxlan_add_del_tunnel'
848 args = dict(is_add=1,
849 is_ipv6=1 if src_address.version == 6 else 0,
850 instance=Constants.BITWISE_NON_ZERO,
851 src_address=src_address.packed,
852 dst_address=dst_address.packed,
853 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
855 decap_next_index=Constants.BITWISE_NON_ZERO,
857 err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
858 format(host=node['host'])
859 with PapiSocketExecutor(node) as papi_exec:
860 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
862 if_key = Topology.add_new_port(node, 'vxlan_tunnel')
863 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
864 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
865 Topology.update_interface_name(node, if_key, ifc_name)
870 def set_vxlan_bypass(node, interface=None):
871 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
873 By adding the IPv4 vxlan-bypass graph node to an interface, the node
874 checks for and validate input vxlan packet and bypass ip4-lookup,
875 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
876 This node will cause extra overhead to for non-vxlan packets which is
879 :param node: Node where to set VXLAN bypass.
880 :param interface: Numeric index or name string of a specific interface.
882 :type interface: int or str
883 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
885 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
887 cmd = 'sw_interface_set_vxlan_bypass'
888 args = dict(is_ipv6=0,
889 sw_if_index=sw_if_index,
891 err_msg = 'Failed to set VXLAN bypass on interface on host {host}'.\
892 format(host=node['host'])
893 with PapiSocketExecutor(node) as papi_exec:
894 papi_exec.add(cmd, **args).get_replies(err_msg)
897 def vxlan_dump(node, interface=None):
898 """Get VxLAN data for the given interface.
900 :param node: VPP node to get interface data from.
901 :param interface: Numeric index or name string of a specific interface.
902 If None, information about all VxLAN interfaces is returned.
904 :type interface: int or str
905 :returns: Dictionary containing data for the given VxLAN interface or if
906 interface=None, the list of dictionaries with all VxLAN interfaces.
908 :raises TypeError: if the data type of interface is neither basestring
911 def process_vxlan_dump(vxlan_dump):
912 """Process vxlan dump.
914 :param vxlan_dump: Vxlan interface dump.
915 :type vxlan_dump: dict
916 :returns: Processed vxlan interface dump.
919 if vxlan_dump['is_ipv6']:
920 vxlan_dump['src_address'] = \
921 ip_address(unicode(vxlan_dump['src_address']))
922 vxlan_dump['dst_address'] = \
923 ip_address(unicode(vxlan_dump['dst_address']))
925 vxlan_dump['src_address'] = \
926 ip_address(unicode(vxlan_dump['src_address'][0:4]))
927 vxlan_dump['dst_address'] = \
928 ip_address(unicode(vxlan_dump['dst_address'][0:4]))
931 if interface is not None:
932 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
934 sw_if_index = int(Constants.BITWISE_NON_ZERO)
936 cmd = 'vxlan_tunnel_dump'
937 args = dict(sw_if_index=sw_if_index)
938 err_msg = 'Failed to get VXLAN dump on host {host}'.format(
940 with PapiSocketExecutor(node) as papi_exec:
941 details = papi_exec.add(cmd, **args).get_details(err_msg)
943 data = list() if interface is None else dict()
945 if interface is None:
946 data.append(process_vxlan_dump(dump))
947 elif dump['sw_if_index'] == sw_if_index:
948 data = process_vxlan_dump(dump)
951 logger.debug('VXLAN data:\n{vxlan_data}'.format(vxlan_data=data))
955 def vhost_user_dump(node):
956 """Get vhost-user data for the given node.
958 TODO: Move to VhostUser.py
960 :param node: VPP node to get interface data from.
962 :returns: List of dictionaries with all vhost-user interfaces.
965 def process_vhost_dump(vhost_dump):
966 """Process vhost dump.
968 :param vhost_dump: Vhost interface dump.
969 :type vhost_dump: dict
970 :returns: Processed vhost interface dump.
973 vhost_dump['interface_name'] = \
974 vhost_dump['interface_name'].rstrip('\x00')
975 vhost_dump['sock_filename'] = \
976 vhost_dump['sock_filename'].rstrip('\x00')
979 cmd = 'sw_interface_vhost_user_dump'
980 err_msg = 'Failed to get vhost-user dump on host {host}'.format(
982 with PapiSocketExecutor(node) as papi_exec:
983 details = papi_exec.add(cmd).get_details(err_msg)
987 process_vhost_dump(dump)
989 logger.debug('Vhost-user details:\n{vhost_details}'.format(
990 vhost_details=details))
994 def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
995 inner_vlan_id=None, type_subif=None):
996 """Create sub-interface on node. It is possible to set required
997 sub-interface type and VLAN tag(s).
999 :param node: Node to add sub-interface.
1000 :param interface: Interface name on which create sub-interface.
1001 :param sub_id: ID of the sub-interface to be created.
1002 :param outer_vlan_id: Optional outer VLAN ID.
1003 :param inner_vlan_id: Optional inner VLAN ID.
1004 :param type_subif: Optional type of sub-interface. Values supported by
1005 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
1008 :type interface: str or int
1010 :type outer_vlan_id: int
1011 :type inner_vlan_id: int
1012 :type type_subif: str
1013 :returns: Name and index of created sub-interface.
1015 :raises RuntimeError: If it is not possible to create sub-interface.
1017 subif_types = type_subif.split()
1020 if 'no_tags' in subif_types:
1021 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
1022 if 'one_tag' in subif_types:
1023 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
1024 if 'two_tags' in subif_types:
1025 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
1026 if 'dot1ad' in subif_types:
1027 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
1028 if 'exact_match' in subif_types:
1029 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
1030 if 'default_sub' in subif_types:
1031 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
1032 if type_subif == 'default_sub':
1033 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
1034 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
1036 cmd = 'create_subif'
1038 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1040 sub_if_flags=flags.value if hasattr(flags, 'value') else int(flags),
1041 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
1042 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
1044 err_msg = 'Failed to create sub-interface on host {host}'.format(
1046 with PapiSocketExecutor(node) as papi_exec:
1047 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1049 if_key = Topology.add_new_port(node, 'subinterface')
1050 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1051 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1052 Topology.update_interface_name(node, if_key, ifc_name)
1054 return '{ifc}.{s_id}'.format(ifc=interface, s_id=sub_id), sw_if_index
1057 def create_gre_tunnel_interface(node, source_ip, destination_ip):
1058 """Create GRE tunnel interface on node.
1060 :param node: VPP node to add tunnel interface.
1061 :param source_ip: Source of the GRE tunnel.
1062 :param destination_ip: Destination of the GRE tunnel.
1064 :type source_ip: str
1065 :type destination_ip: str
1066 :returns: Name and index of created GRE tunnel interface.
1068 :raises RuntimeError: If unable to create GRE tunnel interface.
1070 cmd = 'gre_tunnel_add_del'
1071 tunnel = dict(type=0,
1072 instance=Constants.BITWISE_NON_ZERO,
1074 dst=str(destination_ip),
1077 args = dict(is_add=1,
1079 err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
1081 with PapiSocketExecutor(node) as papi_exec:
1082 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1084 if_key = Topology.add_new_port(node, 'gre_tunnel')
1085 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1086 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1087 Topology.update_interface_name(node, if_key, ifc_name)
1089 return ifc_name, sw_if_index
1092 def vpp_create_loopback(node, mac=None):
1093 """Create loopback interface on VPP node.
1095 :param node: Node to create loopback interface on.
1096 :param mac: Optional MAC address for loopback interface.
1099 :returns: SW interface index.
1101 :raises RuntimeError: If it is not possible to create loopback on the
1104 cmd = 'create_loopback'
1105 args = dict(mac_address=L2Util.mac_to_bin(mac) if mac else 0)
1106 err_msg = 'Failed to create loopback interface on host {host}'.format(
1108 with PapiSocketExecutor(node) as papi_exec:
1109 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1111 if_key = Topology.add_new_port(node, 'loopback')
1112 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1113 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1114 Topology.update_interface_name(node, if_key, ifc_name)
1116 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1117 Topology.update_interface_mac_address(node, if_key, mac)
1122 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1123 """Create bond interface on VPP node.
1125 :param node: DUT node from topology.
1126 :param mode: Link bonding mode.
1127 :param load_balance: Load balance (optional, valid for xor and lacp
1128 modes, otherwise ignored).
1129 :param mac: MAC address to assign to the bond interface (optional).
1132 :type load_balance: str
1134 :returns: Interface key (name) in topology.
1136 :raises RuntimeError: If it is not possible to create bond interface on
1141 id=int(Constants.BITWISE_NON_ZERO),
1142 use_custom_mac=False if mac is None else True,
1143 mac_address=L2Util.mac_to_bin(mac) if mac else None,
1144 mode=getattr(LinkBondMode, 'BOND_API_MODE_{md}'.format(
1145 md=mode.replace('-', '_').upper())).value,
1146 lb=0 if load_balance is None else getattr(
1147 LinkBondLoadBalanceAlgo, 'BOND_API_LB_ALGO_{lb}'.format(
1148 lb=load_balance.upper())).value,
1151 err_msg = 'Failed to create bond interface on host {host}'.format(
1153 with PapiSocketExecutor(node) as papi_exec:
1154 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1156 InterfaceUtil.add_eth_interface(
1157 node, sw_if_index=sw_if_index, ifc_pfx='eth_bond')
1158 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1163 def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1164 """Add ethernet interface to current topology.
1166 :param node: DUT node from topology.
1167 :param ifc_name: Name of the interface.
1168 :param sw_if_index: SW interface index.
1169 :param ifc_pfx: Interface key prefix.
1172 :type sw_if_index: int
1175 if_key = Topology.add_new_port(node, ifc_pfx)
1177 if ifc_name and sw_if_index is None:
1178 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1180 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1181 if sw_if_index and ifc_name is None:
1182 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1183 Topology.update_interface_name(node, if_key, ifc_name)
1184 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1185 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1188 def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1189 """Create AVF interface on VPP node.
1191 :param node: DUT node from topology.
1192 :param vf_pci_addr: PCI address binded to i40evf driver.
1193 :param num_rx_queues: Number of RX queues.
1195 :type vf_pci_addr: str
1196 :type num_rx_queues: int
1197 :returns: Interface key (name) in topology.
1199 :raises RuntimeError: If it is not possible to create AVF interface on
1202 PapiSocketExecutor.run_cli_cmd(
1203 node, 'set logging class avf level debug')
1206 args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1208 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1211 err_msg = 'Failed to create AVF interface on host {host}'.format(
1213 with PapiSocketExecutor(node) as papi_exec:
1214 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1216 InterfaceUtil.add_eth_interface(
1217 node, sw_if_index=sw_if_index, ifc_pfx='eth_avf')
1218 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1223 def vpp_create_rdma_interface(node, pci_addr, num_rx_queues=None):
1224 """Create RDMA interface on VPP node.
1226 :param node: DUT node from topology.
1227 :param pci_addr: PCI address binded to rdma-core driver.
1228 :param num_rx_queues: Number of RX queues.
1231 :type num_rx_queues: int
1232 :returns: Interface key (name) in topology.
1234 :raises RuntimeError: If it is not possible to create RDMA interface on
1238 args = dict(name=InterfaceUtil.pci_to_eth(node, pci_addr),
1239 host_if=InterfaceUtil.pci_to_eth(node, pci_addr),
1240 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1243 err_msg = 'Failed to create RDMA interface on host {host}'.format(
1245 with PapiSocketExecutor(node) as papi_exec:
1246 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1248 InterfaceUtil.add_eth_interface(
1249 node, sw_if_index=sw_if_index, ifc_pfx='eth_rdma')
1250 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1255 def vpp_enslave_physical_interface(node, interface, bond_if):
1256 """Enslave physical interface to bond interface on VPP node.
1258 :param node: DUT node from topology.
1259 :param interface: Physical interface key from topology file.
1260 :param bond_if: Load balance
1262 :type interface: str
1264 :raises RuntimeError: If it is not possible to enslave physical
1265 interface to bond interface on the node.
1267 cmd = 'bond_enslave'
1269 sw_if_index=Topology.get_interface_sw_index(node, interface),
1270 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1272 is_long_timeout=False
1274 err_msg = 'Failed to enslave physical interface {ifc} to bond ' \
1275 'interface {bond} on host {host}'.format(ifc=interface,
1278 with PapiSocketExecutor(node) as papi_exec:
1279 papi_exec.add(cmd, **args).get_reply(err_msg)
1282 def vpp_show_bond_data_on_node(node, verbose=False):
1283 """Show (detailed) bond information on VPP node.
1285 :param node: DUT node from topology.
1286 :param verbose: If detailed information is required or not.
1290 cmd = 'sw_interface_bond_dump'
1291 err_msg = 'Failed to get bond interface dump on host {host}'.format(
1294 data = ('Bond data on node {host}:\n'.format(host=node['host']))
1295 with PapiSocketExecutor(node) as papi_exec:
1296 details = papi_exec.add(cmd).get_details(err_msg)
1298 for bond in details:
1299 data += ('{b}\n'.format(b=bond['interface_name']))
1300 data += (' mode: {m}\n'.format(
1301 m=bond['mode'].name.replace('BOND_API_MODE_', '').lower()))
1302 data += (' load balance: {lb}\n'.format(
1303 lb=bond['lb'].name.replace('BOND_API_LB_ALGO_', '').lower()))
1304 data += (' number of active slaves: {n}\n'.format(
1305 n=bond['active_slaves']))
1307 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1308 node, Topology.get_interface_by_sw_index(
1309 node, bond['sw_if_index']))
1310 for slave in slave_data:
1311 if not slave['is_passive']:
1312 data += (' {s}\n'.format(s=slave['interface_name']))
1313 data += (' number of slaves: {n}\n'.format(n=bond['slaves']))
1315 for slave in slave_data:
1316 data += (' {s}\n'.format(s=slave['interface_name']))
1317 data += (' interface id: {i}\n'.format(i=bond['id']))
1318 data += (' sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1322 def vpp_bond_slave_dump(node, interface):
1323 """Get bond interface slave(s) data on VPP node.
1325 :param node: DUT node from topology.
1326 :param interface: Physical interface key from topology file.
1328 :type interface: str
1329 :returns: Bond slave interface data.
1332 cmd = 'sw_interface_slave_dump'
1333 args = dict(sw_if_index=Topology.get_interface_sw_index(
1335 err_msg = 'Failed to get slave dump on host {host}'.format(
1338 with PapiSocketExecutor(node) as papi_exec:
1339 details = papi_exec.add(cmd, **args).get_details(err_msg)
1341 logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1345 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1346 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1348 :param nodes: Nodes in the topology.
1349 :param verbose: If detailed information is required or not.
1353 for node_data in nodes.values():
1354 if node_data['type'] == NodeType.DUT:
1355 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1358 def vpp_enable_input_acl_interface(node, interface, ip_version,
1360 """Enable input acl on interface.
1362 :param node: VPP node to setup interface for input acl.
1363 :param interface: Interface to setup input acl.
1364 :param ip_version: Version of IP protocol.
1365 :param table_index: Classify table index.
1367 :type interface: str or int
1368 :type ip_version: str
1369 :type table_index: int
1371 cmd = 'input_acl_set_interface'
1373 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1374 ip4_table_index=table_index if ip_version == 'ip4'
1375 else Constants.BITWISE_NON_ZERO,
1376 ip6_table_index=table_index if ip_version == 'ip6'
1377 else Constants.BITWISE_NON_ZERO,
1378 l2_table_index=table_index if ip_version == 'l2'
1379 else Constants.BITWISE_NON_ZERO,
1381 err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1383 with PapiSocketExecutor(node) as papi_exec:
1384 papi_exec.add(cmd, **args).get_reply(err_msg)
1387 def get_interface_classify_table(node, interface):
1388 """Get name of classify table for the given interface.
1390 TODO: Move to Classify.py.
1392 :param node: VPP node to get data from.
1393 :param interface: Name or sw_if_index of a specific interface.
1395 :type interface: str or int
1396 :returns: Classify table name.
1399 if isinstance(interface, basestring):
1400 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1402 sw_if_index = interface
1404 cmd = 'classify_table_by_interface'
1405 args = dict(sw_if_index=sw_if_index)
1406 err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1408 with PapiSocketExecutor(node) as papi_exec:
1409 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1414 def get_sw_if_index(node, interface_name):
1415 """Get sw_if_index for the given interface from actual interface dump.
1417 :param node: VPP node to get interface data from.
1418 :param interface_name: Name of the specific interface.
1420 :type interface_name: str
1421 :returns: sw_if_index of the given interface.
1424 interface_data = InterfaceUtil.vpp_get_interface_data(
1425 node, interface=interface_name)
1426 return interface_data.get('sw_if_index')
1429 def vxlan_gpe_dump(node, interface_name=None):
1430 """Get VxLAN GPE data for the given interface.
1432 :param node: VPP node to get interface data from.
1433 :param interface_name: Name of the specific interface. If None,
1434 information about all VxLAN GPE interfaces is returned.
1436 :type interface_name: str
1437 :returns: Dictionary containing data for the given VxLAN GPE interface
1438 or if interface=None, the list of dictionaries with all VxLAN GPE
1440 :rtype: dict or list
1442 def process_vxlan_gpe_dump(vxlan_dump):
1443 """Process vxlan_gpe dump.
1445 :param vxlan_dump: Vxlan_gpe nterface dump.
1446 :type vxlan_dump: dict
1447 :returns: Processed vxlan_gpe interface dump.
1450 if vxlan_dump['is_ipv6']:
1451 vxlan_dump['local'] = \
1452 ip_address(unicode(vxlan_dump['local']))
1453 vxlan_dump['remote'] = \
1454 ip_address(unicode(vxlan_dump['remote']))
1456 vxlan_dump['local'] = \
1457 ip_address(unicode(vxlan_dump['local'][0:4]))
1458 vxlan_dump['remote'] = \
1459 ip_address(unicode(vxlan_dump['remote'][0:4]))
1462 if interface_name is not None:
1463 sw_if_index = InterfaceUtil.get_interface_index(
1464 node, interface_name)
1466 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1468 cmd = 'vxlan_gpe_tunnel_dump'
1469 args = dict(sw_if_index=sw_if_index)
1470 err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1472 with PapiSocketExecutor(node) as papi_exec:
1473 details = papi_exec.add(cmd, **args).get_details(err_msg)
1475 data = list() if interface_name is None else dict()
1476 for dump in details:
1477 if interface_name is None:
1478 data.append(process_vxlan_gpe_dump(dump))
1479 elif dump['sw_if_index'] == sw_if_index:
1480 data = process_vxlan_gpe_dump(dump)
1483 logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1484 vxlan_gpe_data=data))
1488 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1489 """Assign VPP interface to specific VRF/FIB table.
1491 :param node: VPP node where the FIB and interface are located.
1492 :param interface: Interface to be assigned to FIB.
1493 :param table_id: VRF table ID.
1494 :param ipv6: Assign to IPv6 table. Default False.
1496 :type interface: str or int
1500 cmd = 'sw_interface_set_table'
1502 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1504 vrf_id=int(table_id))
1505 err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1507 with PapiSocketExecutor(node) as papi_exec:
1508 papi_exec.add(cmd, **args).get_reply(err_msg)
1511 def set_linux_interface_mac(node, interface, mac, namespace=None,
1513 """Set MAC address for interface in linux.
1515 :param node: Node where to execute command.
1516 :param interface: Interface in namespace.
1517 :param mac: MAC to be assigned to interface.
1518 :param namespace: Execute command in namespace. Optional
1519 :param vf_id: Virtual Function id. Optional
1521 :type interface: str
1523 :type namespace: str
1526 mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1527 if vf_id is not None else 'address {mac}'.format(mac=mac)
1528 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1530 cmd = ('{ns} ip link set {interface} {mac}'.
1531 format(ns=ns_str, interface=interface, mac=mac_str))
1532 exec_cmd_no_error(node, cmd, sudo=True)
1535 def set_linux_interface_trust_on(node, interface, namespace=None,
1537 """Set trust on (promisc) 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 trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1549 if vf_id is not None else 'trust on'
1550 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1552 cmd = ('{ns} ip link set dev {interface} {trust}'.
1553 format(ns=ns_str, interface=interface, trust=trust_str))
1554 exec_cmd_no_error(node, cmd, sudo=True)
1557 def set_linux_interface_spoof_off(node, interface, namespace=None,
1559 """Set spoof off for interface in linux.
1561 :param node: Node where to execute command.
1562 :param interface: Interface in namespace.
1563 :param namespace: Execute command in namespace. Optional
1564 :param vf_id: Virtual Function id. Optional
1566 :type interface: str
1567 :type namespace: str
1570 spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1571 if vf_id is not None else 'spoof off'
1572 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1574 cmd = ('{ns} ip link set dev {interface} {spoof}'.
1575 format(ns=ns_str, interface=interface, spoof=spoof_str))
1576 exec_cmd_no_error(node, cmd, sudo=True)
1579 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1580 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1581 driver testing on DUT.
1583 :param node: DUT node.
1584 :param ifc_key: Interface key from topology file.
1585 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1586 :param osi_layer: OSI Layer type to initialize TG with.
1587 Default value "L2" sets linux interface spoof off.
1591 :type osi_layer: str
1592 :returns: Virtual Function topology interface keys.
1594 :raises RuntimeError: If a reason preventing initialization is found.
1596 # Read PCI address and driver.
1597 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1598 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1599 uio_driver = Topology.get_uio_driver(node)
1600 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1601 if kernel_driver not in ("i40e", "i40evf"):
1603 "AVF needs i40e-compatible driver, not {driver} at node {host}"
1604 " ifc {ifc}".format(
1605 driver=kernel_driver, host=node["host"], ifc=ifc_key))
1606 current_driver = DUTSetup.get_pci_dev_driver(
1607 node, pf_pci_addr.replace(':', r'\:'))
1609 VPPUtil.stop_vpp_service(node)
1610 if current_driver != kernel_driver:
1611 # PCI device must be re-bound to kernel driver before creating VFs.
1612 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1613 # Stop VPP to prevent deadlock.
1614 # Unbind from current driver.
1615 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1616 # Bind to kernel driver.
1617 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1619 # Initialize PCI VFs.
1620 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1623 # Set MAC address and bind each virtual function to uio driver.
1624 for vf_id in range(numvfs):
1625 vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1626 pf_mac_addr[3], pf_mac_addr[4],
1627 pf_mac_addr[5], "{:02x}".format(vf_id)])
1629 pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1630 format(pci=pf_pci_addr)
1631 InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1633 if osi_layer == 'L2':
1634 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1636 InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1639 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1640 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1642 # Add newly created ports into topology file
1643 vf_ifc_name = '{pf_if_key}_vif'.format(pf_if_key=ifc_key)
1644 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1645 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1646 Topology.update_interface_name(node, vf_ifc_key,
1647 vf_ifc_name+str(vf_id+1))
1648 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1649 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1650 vf_ifc_keys.append(vf_ifc_key)
1655 def vpp_sw_interface_rx_placement_dump(node):
1656 """Dump VPP interface RX placement on node.
1658 :param node: Node to run command on.
1660 :returns: Thread mapping information as a list of dictionaries.
1663 cmd = 'sw_interface_rx_placement_dump'
1664 err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1665 cmd=cmd, host=node['host'])
1666 with PapiSocketExecutor(node) as papi_exec:
1667 for ifc in node['interfaces'].values():
1668 if ifc['vpp_sw_index'] is not None:
1669 papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1670 details = papi_exec.get_details(err_msg)
1671 return sorted(details, key=lambda k: k['sw_if_index'])
1674 def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1676 """Set interface RX placement to worker on node.
1678 :param node: Node to run command on.
1679 :param sw_if_index: VPP SW interface index.
1680 :param queue_id: VPP interface queue ID.
1681 :param worker_id: VPP worker ID (indexing from 0).
1683 :type sw_if_index: int
1685 :type worker_id: int
1686 :raises RuntimeError: If failed to run command on host or if no API
1689 cmd = 'sw_interface_set_rx_placement'
1690 err_msg = "Failed to set interface RX placement to worker on host " \
1691 "{host}!".format(host=node['host'])
1693 sw_if_index=sw_if_index,
1695 worker_id=worker_id,
1698 with PapiSocketExecutor(node) as papi_exec:
1699 papi_exec.add(cmd, **args).get_reply(err_msg)
1702 def vpp_round_robin_rx_placement(node, prefix):
1703 """Set Round Robin interface RX placement on all worker threads
1706 :param node: Topology nodes.
1707 :param prefix: Interface name prefix.
1712 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1715 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1716 for interface in node['interfaces'].values():
1717 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1718 and prefix in interface['name']:
1719 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1720 node, placement['sw_if_index'], placement['queue_id'],
1721 worker_id % worker_cnt)
1725 def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1726 """Set Round Robin interface RX placement on all worker threads
1729 :param nodes: Topology nodes.
1730 :param prefix: Interface name prefix.
1734 for node in nodes.values():
1735 if node['type'] == NodeType.DUT:
1736 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)