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 falgs."""
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 # pylint: disable=invalid-name
88 class LinkBondLoadBalance(IntEnum):
89 """Link bonding load balance."""
90 L2 = 0 # pylint: disable=invalid-name
95 class LinkBondMode(IntEnum):
96 """Link bonding load balance."""
104 class InterfaceUtil(object):
105 """General utilities for managing interfaces"""
107 __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
110 def pci_to_int(pci_str):
111 """Convert PCI address from string format (0000:18:0a.0) to
112 integer representation (169345024).
114 :param pci_str: PCI address in string representation.
116 :returns: Integer representation of PCI address.
119 pci = list(pci_str.split(':')[0:2])
120 pci.extend(pci_str.split(':')[2].split('.'))
122 return (int(pci[0], 16) | int(pci[1], 16) << 16 |
123 int(pci[2], 16) << 24 | int(pci[3], 16) << 29)
126 def get_interface_index(node, interface):
127 """Get interface sw_if_index from topology file.
129 :param node: Node where the interface is.
130 :param interface: Numeric index or name string of a specific interface.
132 :type interface: str or int
133 :returns: SW interface index.
137 sw_if_index = int(interface)
139 sw_if_index = Topology.get_interface_sw_index(node, interface)
140 if sw_if_index is None:
142 Topology.get_interface_sw_index_by_name(node, interface)
143 except TypeError as err:
144 raise TypeError('Wrong interface format {ifc}: {err}'.format(
145 ifc=interface, err=err.message))
150 def set_interface_state(node, interface, state, if_type='key'):
151 """Set interface state on a node.
153 Function can be used for DUTs as well as for TGs.
155 :param node: Node where the interface is.
156 :param interface: Interface key or sw_if_index or name.
157 :param state: One of 'up' or 'down'.
158 :param if_type: Interface type
160 :type interface: str or int
164 :raises ValueError: If the interface type is unknown.
165 :raises ValueError: If the state of interface is unexpected.
166 :raises ValueError: If the node has an unknown node type.
169 if isinstance(interface, basestring):
170 sw_if_index = Topology.get_interface_sw_index(node, interface)
171 iface_name = Topology.get_interface_name(node, interface)
173 sw_if_index = interface
174 elif if_type == 'name':
175 iface_key = Topology.get_interface_by_name(node, interface)
176 if iface_key is not None:
177 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
178 iface_name = interface
180 raise ValueError('Unknown if_type: {type}'.format(type=if_type))
182 if node['type'] == NodeType.DUT:
184 flags = InterfaceStatusFlags.IF_STATUS_API_FLAG_ADMIN_UP.value
185 elif state == 'down':
188 raise ValueError('Unexpected interface state: {state}'.format(
190 cmd = 'sw_interface_set_flags'
191 err_msg = 'Failed to set interface state on host {host}'.format(
194 sw_if_index=sw_if_index,
196 with PapiSocketExecutor(node) as papi_exec:
197 papi_exec.add(cmd, **args).get_reply(err_msg)
198 elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
199 cmd = 'ip link set {ifc} {state}'.format(
200 ifc=iface_name, state=state)
201 exec_cmd_no_error(node, cmd, sudo=True)
203 raise ValueError('Node {} has unknown NodeType: "{}"'
204 .format(node['host'], node['type']))
207 def set_interface_ethernet_mtu(node, iface_key, mtu):
208 """Set Ethernet MTU for specified interface.
210 Function can be used only for TGs.
212 :param node: Node where the interface is.
213 :param iface_key: Interface key from topology file.
214 :param mtu: MTU to set.
219 :raises ValueError: If the node type is "DUT".
220 :raises ValueError: If the node has an unknown node type.
222 if node['type'] == NodeType.DUT:
223 raise ValueError('Node {}: Setting Ethernet MTU for interface '
224 'on DUT nodes not supported', node['host'])
225 elif node['type'] == NodeType.TG:
226 iface_name = Topology.get_interface_name(node, iface_key)
227 cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
228 exec_cmd_no_error(node, cmd, sudo=True)
230 raise ValueError('Node {} has unknown NodeType: "{}"'
231 .format(node['host'], node['type']))
234 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
235 """Set default Ethernet MTU on all interfaces on node.
237 Function can be used only for TGs.
239 :param node: Node where to set default MTU.
243 for ifc in node['interfaces']:
244 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
247 def vpp_set_interface_mtu(node, interface, mtu=9200):
248 """Set Ethernet MTU on interface.
250 :param node: VPP node.
251 :param interface: Interface to setup MTU. Default: 9200.
252 :param mtu: Ethernet MTU size in Bytes.
254 :type interface: str or int
257 if isinstance(interface, basestring):
258 sw_if_index = Topology.get_interface_sw_index(node, interface)
260 sw_if_index = interface
262 cmd = 'hw_interface_set_mtu'
263 err_msg = 'Failed to set interface MTU on host {host}'.format(
265 args = dict(sw_if_index=sw_if_index,
268 with PapiSocketExecutor(node) as papi_exec:
269 papi_exec.add(cmd, **args).get_reply(err_msg)
270 except AssertionError as err:
271 # TODO: Make failure tolerance optional.
272 logger.debug("Setting MTU failed. Expected?\n{err}".format(
276 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
277 """Set Ethernet MTU on all interfaces.
279 :param node: VPP node.
280 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
284 for interface in node['interfaces']:
285 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
288 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
289 """Set Ethernet MTU on all interfaces on all DUTs.
291 :param nodes: VPP nodes.
292 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
296 for node in nodes.values():
297 if node['type'] == NodeType.DUT:
298 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
301 def vpp_node_interfaces_ready_wait(node, retries=15):
302 """Wait until all interfaces with admin-up are in link-up state.
304 :param node: Node to wait on.
305 :param retries: Number of retries to check interface status (optional,
310 :raises RuntimeError: If any interface is not in link-up state after
311 defined number of retries.
313 for _ in xrange(0, retries):
315 out = InterfaceUtil.vpp_get_interface_data(node)
316 for interface in out:
317 if interface.get('flags') == 1:
318 not_ready.append(interface.get('interface_name'))
322 logger.debug('Interfaces still not in link-up state:\n{ifs} '
323 '\nWaiting...'.format(ifs=not_ready))
326 err = 'Timeout, interfaces not up:\n{ifs}'.format(ifs=not_ready) \
327 if 'not_ready' in locals() else 'No check executed!'
328 raise RuntimeError(err)
331 def all_vpp_interfaces_ready_wait(nodes, retries=15):
332 """Wait until all interfaces with admin-up are in link-up state for all
333 nodes in the topology.
335 :param nodes: Nodes in the topology.
336 :param retries: Number of retries to check interface status (optional,
342 for node in nodes.values():
343 if node['type'] == NodeType.DUT:
344 InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries)
347 def vpp_get_interface_data(node, interface=None):
348 """Get all interface data from a VPP node. If a name or
349 sw_interface_index is provided, return only data for the matching
352 :param node: VPP node to get interface data from.
353 :param interface: Numeric index or name string of a specific interface.
355 :type interface: int or str
356 :returns: List of dictionaries containing data for each interface, or a
357 single dictionary for the specified interface.
359 :raises TypeError: if the data type of interface is neither basestring
362 def process_if_dump(if_dump):
363 """Process interface dump.
365 :param if_dump: Interface dump.
367 :returns: Processed interface dump.
370 if_dump['l2_address'] = str(if_dump['l2_address'])
371 if_dump['b_dmac'] = str(if_dump['b_dmac'])
372 if_dump['b_smac'] = str(if_dump['b_smac'])
373 if_dump['flags'] = if_dump['flags'].value
374 if_dump['type'] = if_dump['type'].value
375 if_dump['link_duplex'] = if_dump['link_duplex'].value
376 if_dump['sub_if_flags'] = if_dump['sub_if_flags'].value \
377 if hasattr(if_dump['sub_if_flags'], 'value') \
378 else int(if_dump['sub_if_flags'])
382 if interface is not None:
383 if isinstance(interface, basestring):
384 param = 'interface_name'
385 elif isinstance(interface, int):
386 param = 'sw_if_index'
388 raise TypeError('Wrong interface format {ifc}'.format(
393 cmd = 'sw_interface_dump'
395 name_filter_valid=False,
398 err_msg = 'Failed to get interface dump on host {host}'.format(
400 with PapiSocketExecutor(node) as papi_exec:
401 details = papi_exec.add(cmd, **args).get_details(err_msg)
402 logger.debug('Received data:\n{d!r}'.format(d=details))
404 data = list() if interface is None else dict()
406 if interface is None:
407 data.append(process_if_dump(dump))
408 elif str(dump.get(param)).rstrip('\x00') == str(interface):
409 data = process_if_dump(dump)
412 logger.debug('Interface data:\n{if_data}'.format(if_data=data))
416 def vpp_get_interface_name(node, sw_if_index):
417 """Get interface name for the given SW interface index from actual
420 :param node: VPP node to get interface data from.
421 :param sw_if_index: SW interface index of the specific interface.
423 :type sw_if_index: int
424 :returns: Name of the given interface.
427 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
428 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
429 if_data = InterfaceUtil.vpp_get_interface_data(
430 node, if_data['sup_sw_if_index'])
432 return if_data.get('interface_name')
435 def vpp_get_interface_sw_index(node, interface_name):
436 """Get interface name for the given SW interface index from actual
439 :param node: VPP node to get interface data from.
440 :param interface_name: Interface name.
442 :type interface_name: str
443 :returns: Name of the given interface.
446 if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name)
448 return if_data.get('sw_if_index')
451 def vpp_get_interface_mac(node, interface):
452 """Get MAC address for the given interface from actual interface dump.
454 :param node: VPP node to get interface data from.
455 :param interface: Numeric index or name string of a specific interface.
457 :type interface: int or str
458 :returns: MAC address.
461 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
462 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
463 if_data = InterfaceUtil.vpp_get_interface_data(
464 node, if_data['sup_sw_if_index'])
466 return if_data.get('l2_address')
469 def tg_set_interface_driver(node, pci_addr, driver):
470 """Set interface driver on the TG node.
472 :param node: Node to set interface driver on (must be TG node).
473 :param pci_addr: PCI address of the interface.
474 :param driver: Driver name.
478 :raises RuntimeError: If unbinding from the current driver fails.
479 :raises RuntimeError: If binding to the new driver fails.
481 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
482 if old_driver == driver:
488 # Unbind from current driver
489 if old_driver is not None:
490 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
491 .format(pci_addr, old_driver)
492 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
493 if int(ret_code) != 0:
494 raise RuntimeError("'{0}' failed on '{1}'"
495 .format(cmd, node['host']))
497 # Bind to the new driver
498 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
499 .format(pci_addr, driver)
500 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
501 if int(ret_code) != 0:
502 raise RuntimeError("'{0}' failed on '{1}'"
503 .format(cmd, node['host']))
506 def tg_get_interface_driver(node, pci_addr):
507 """Get interface driver from the TG node.
509 :param node: Node to get interface driver on (must be TG node).
510 :param pci_addr: PCI address of the interface.
513 :returns: Interface driver or None if not found.
515 :raises RuntimeError: If PCI rescan or lspci command execution failed.
517 return DUTSetup.get_pci_dev_driver(node, pci_addr)
520 def tg_set_interfaces_udev_rules(node):
521 """Set udev rules for interfaces.
523 Create udev rules file in /etc/udev/rules.d where are rules for each
524 interface used by TG node, based on MAC interface has specific name.
525 So after unbind and bind again to kernel driver interface has same
526 name as before. This must be called after TG has set name for each
527 port in topology dictionary.
529 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
532 :param node: Node to set udev rules on (must be TG node).
534 :raises RuntimeError: If setting of udev rules fails.
539 cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
540 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
541 if int(ret_code) != 0:
542 raise RuntimeError("'{0}' failed on '{1}'"
543 .format(cmd, node['host']))
545 for interface in node['interfaces'].values():
546 rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
547 '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
548 interface['name'] + '\\"'
549 cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
550 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
551 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
552 if int(ret_code) != 0:
553 raise RuntimeError("'{0}' failed on '{1}'"
554 .format(cmd, node['host']))
556 cmd = '/etc/init.d/udev restart'
557 ssh.exec_command_sudo(cmd)
560 def tg_set_interfaces_default_driver(node):
561 """Set interfaces default driver specified in topology yaml file.
563 :param node: Node to setup interfaces driver on (must be TG node).
566 for interface in node['interfaces'].values():
567 InterfaceUtil.tg_set_interface_driver(node,
568 interface['pci_address'],
572 def update_vpp_interface_data_on_node(node):
573 """Update vpp generated interface data for a given node in DICT__nodes.
575 Updates interface names, software if index numbers and any other details
576 generated specifically by vpp that are unknown before testcase run.
577 It does this by dumping interface list from all devices using python
578 api, and pairing known information from topology (mac address) to state
581 :param node: Node selected from DICT__nodes.
584 interface_list = InterfaceUtil.vpp_get_interface_data(node)
585 interface_dict = dict()
586 for ifc in interface_list:
587 interface_dict[ifc['l2_address']] = ifc
589 for if_name, if_data in node['interfaces'].items():
590 ifc_dict = interface_dict.get(if_data['mac_address'])
591 if ifc_dict is not None:
592 if_data['name'] = ifc_dict['interface_name']
593 if_data['vpp_sw_index'] = ifc_dict['sw_if_index']
594 if_data['mtu'] = ifc_dict['mtu'][0]
595 logger.trace('Interface {ifc} found by MAC {mac}'.format(
596 ifc=if_name, mac=if_data['mac_address']))
598 logger.trace('Interface {ifc} not found by MAC {mac}'.format(
599 ifc=if_name, mac=if_data['mac_address']))
600 if_data['vpp_sw_index'] = None
603 def update_nic_interface_names(node):
604 """Update interface names based on nic type and PCI address.
606 This method updates interface names in the same format as VPP does.
608 :param node: Node dictionary.
611 for ifc in node['interfaces'].values():
612 if_pci = ifc['pci_address'].replace('.', ':').split(':')
613 bus = '{:x}'.format(int(if_pci[1], 16))
614 dev = '{:x}'.format(int(if_pci[2], 16))
615 fun = '{:x}'.format(int(if_pci[3], 16))
616 loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
617 if ifc['model'] == 'Intel-XL710':
618 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
619 elif ifc['model'] == 'Intel-X710':
620 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
621 elif ifc['model'] == 'Intel-X520-DA2':
622 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
623 elif ifc['model'] == 'Cisco-VIC-1385':
624 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
625 elif ifc['model'] == 'Cisco-VIC-1227':
626 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
628 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
631 def update_nic_interface_names_on_all_duts(nodes):
632 """Update interface names based on nic type and PCI address on all DUTs.
634 This method updates interface names in the same format as VPP does.
636 :param nodes: Topology nodes.
639 for node in nodes.values():
640 if node['type'] == NodeType.DUT:
641 InterfaceUtil.update_nic_interface_names(node)
644 def update_tg_interface_data_on_node(node, skip_tg_udev=False):
645 """Update interface name for TG/linux node in DICT__nodes.
648 # for dev in `ls /sys/class/net/`;
649 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
650 "52:54:00:9f:82:63": "eth0"
651 "52:54:00:77:ae:a9": "eth1"
652 "52:54:00:e1:8a:0f": "eth2"
653 "00:00:00:00:00:00": "lo"
655 :param node: Node selected from DICT__nodes.
656 :param skip_tg_udev: Skip udev rename on TG node.
658 :type skip_tg_udev: bool
659 :raises RuntimeError: If getting of interface name and MAC fails.
661 # First setup interface driver specified in yaml file
662 InterfaceUtil.tg_set_interfaces_default_driver(node)
664 # Get interface names
668 cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
669 '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
671 (ret_code, stdout, _) = ssh.exec_command(cmd)
672 if int(ret_code) != 0:
673 raise RuntimeError('Get interface name and MAC failed')
674 tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
675 interfaces = JsonParser().parse_data(tmp)
676 for interface in node['interfaces'].values():
677 name = interfaces.get(interface['mac_address'])
680 interface['name'] = name
682 # Set udev rules for interfaces
684 InterfaceUtil.tg_set_interfaces_udev_rules(node)
687 def iface_update_numa_node(node):
688 """For all interfaces from topology file update numa node based on
689 information from the node.
691 :param node: Node from topology.
694 :raises ValueError: If numa node ia less than 0.
695 :raises RuntimeError: If update of numa node failes.
698 for if_key in Topology.get_node_interfaces(node):
699 if_pci = Topology.get_interface_pci_addr(node, if_key)
701 cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
703 (ret, out, _) = ssh.exec_command(cmd)
708 if CpuUtils.cpu_node_count(node) == 1:
713 logger.trace('Reading numa location failed for: {0}'
716 Topology.set_interface_numa_node(node, if_key,
720 raise RuntimeError('Update numa node failed for: {0}'
724 def update_all_numa_nodes(nodes, skip_tg=False):
725 """For all nodes and all their interfaces from topology file update numa
726 node information based on information from the node.
728 :param nodes: Nodes in the topology.
729 :param skip_tg: Skip TG node
734 for node in nodes.values():
735 if node['type'] == NodeType.DUT:
736 InterfaceUtil.iface_update_numa_node(node)
737 elif node['type'] == NodeType.TG and not skip_tg:
738 InterfaceUtil.iface_update_numa_node(node)
741 def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
744 """Update interface names on all nodes in DICT__nodes.
746 This method updates the topology dictionary by querying interface lists
747 of all nodes mentioned in the topology dictionary.
749 :param nodes: Nodes in the topology.
750 :param skip_tg: Skip TG node.
751 :param skip_tg_udev: Skip udev rename on TG node.
752 :param numa_node: Retrieve numa_node location.
755 :type skip_tg_udev: bool
756 :type numa_node: bool
758 for node_data in nodes.values():
759 if node_data['type'] == NodeType.DUT:
760 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
761 elif node_data['type'] == NodeType.TG and not skip_tg:
762 InterfaceUtil.update_tg_interface_data_on_node(
763 node_data, skip_tg_udev)
766 if node_data['type'] == NodeType.DUT:
767 InterfaceUtil.iface_update_numa_node(node_data)
768 elif node_data['type'] == NodeType.TG and not skip_tg:
769 InterfaceUtil.iface_update_numa_node(node_data)
772 def create_vlan_subinterface(node, interface, vlan):
773 """Create VLAN sub-interface on node.
775 :param node: Node to add VLAN subinterface on.
776 :param interface: Interface name or index on which create VLAN
778 :param vlan: VLAN ID of the subinterface to be created.
780 :type interface: str on int
782 :returns: Name and index of created subinterface.
784 :raises RuntimeError: if it is unable to create VLAN subinterface on the
785 node or interface cannot be converted.
787 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
789 cmd = 'create_vlan_subif'
791 sw_if_index=sw_if_index,
794 err_msg = 'Failed to create VLAN sub-interface on host {host}'.format(
796 with PapiSocketExecutor(node) as papi_exec:
797 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
799 if_key = Topology.add_new_port(node, 'vlan_subif')
800 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
801 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
802 Topology.update_interface_name(node, if_key, ifc_name)
804 return '{ifc}.{vlan}'.format(ifc=interface, vlan=vlan), sw_if_index
807 def create_vxlan_interface(node, vni, source_ip, destination_ip):
808 """Create VXLAN interface and return sw if index of created interface.
810 :param node: Node where to create VXLAN interface.
811 :param vni: VXLAN Network Identifier.
812 :param source_ip: Source IP of a VXLAN Tunnel End Point.
813 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
817 :type destination_ip: str
818 :returns: SW IF INDEX of created interface.
820 :raises RuntimeError: if it is unable to create VxLAN interface on the
823 src_address = ip_address(unicode(source_ip))
824 dst_address = ip_address(unicode(destination_ip))
826 cmd = 'vxlan_add_del_tunnel'
827 args = dict(is_add=1,
828 is_ipv6=1 if src_address.version == 6 else 0,
829 instance=Constants.BITWISE_NON_ZERO,
830 src_address=src_address.packed,
831 dst_address=dst_address.packed,
832 mcast_sw_if_index=Constants.BITWISE_NON_ZERO,
834 decap_next_index=Constants.BITWISE_NON_ZERO,
836 err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\
837 format(host=node['host'])
838 with PapiSocketExecutor(node) as papi_exec:
839 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
841 if_key = Topology.add_new_port(node, 'vxlan_tunnel')
842 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
843 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
844 Topology.update_interface_name(node, if_key, ifc_name)
849 def set_vxlan_bypass(node, interface=None):
850 """Add the 'ip4-vxlan-bypass' graph node for a given interface.
852 By adding the IPv4 vxlan-bypass graph node to an interface, the node
853 checks for and validate input vxlan packet and bypass ip4-lookup,
854 ip4-local, ip4-udp-lookup nodes to speedup vxlan packet forwarding.
855 This node will cause extra overhead to for non-vxlan packets which is
858 :param node: Node where to set VXLAN bypass.
859 :param interface: Numeric index or name string of a specific interface.
861 :type interface: int or str
862 :raises RuntimeError: if it failed to set VXLAN bypass on interface.
864 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
866 cmd = 'sw_interface_set_vxlan_bypass'
867 args = dict(is_ipv6=0,
868 sw_if_index=sw_if_index,
870 err_msg = 'Failed to set VXLAN bypass on interface on host {host}'.\
871 format(host=node['host'])
872 with PapiSocketExecutor(node) as papi_exec:
873 papi_exec.add(cmd, **args).get_replies(err_msg)
876 def vxlan_dump(node, interface=None):
877 """Get VxLAN data for the given interface.
879 :param node: VPP node to get interface data from.
880 :param interface: Numeric index or name string of a specific interface.
881 If None, information about all VxLAN interfaces is returned.
883 :type interface: int or str
884 :returns: Dictionary containing data for the given VxLAN interface or if
885 interface=None, the list of dictionaries with all VxLAN interfaces.
887 :raises TypeError: if the data type of interface is neither basestring
890 def process_vxlan_dump(vxlan_dump):
891 """Process vxlan dump.
893 :param vxlan_dump: Vxlan interface dump.
894 :type vxlan_dump: dict
895 :returns: Processed vxlan interface dump.
898 if vxlan_dump['is_ipv6']:
899 vxlan_dump['src_address'] = \
900 ip_address(unicode(vxlan_dump['src_address']))
901 vxlan_dump['dst_address'] = \
902 ip_address(unicode(vxlan_dump['dst_address']))
904 vxlan_dump['src_address'] = \
905 ip_address(unicode(vxlan_dump['src_address'][0:4]))
906 vxlan_dump['dst_address'] = \
907 ip_address(unicode(vxlan_dump['dst_address'][0:4]))
910 if interface is not None:
911 sw_if_index = InterfaceUtil.get_interface_index(node, interface)
913 sw_if_index = int(Constants.BITWISE_NON_ZERO)
915 cmd = 'vxlan_tunnel_dump'
916 args = dict(sw_if_index=sw_if_index)
917 err_msg = 'Failed to get VXLAN dump on host {host}'.format(
919 with PapiSocketExecutor(node) as papi_exec:
920 details = papi_exec.add(cmd, **args).get_details(err_msg)
922 data = list() if interface is None else dict()
924 if interface is None:
925 data.append(process_vxlan_dump(dump))
926 elif dump['sw_if_index'] == sw_if_index:
927 data = process_vxlan_dump(dump)
930 logger.debug('VXLAN data:\n{vxlan_data}'.format(vxlan_data=data))
934 def vhost_user_dump(node):
935 """Get vhost-user data for the given node.
937 TODO: Move to VhostUser.py
939 :param node: VPP node to get interface data from.
941 :returns: List of dictionaries with all vhost-user interfaces.
944 def process_vhost_dump(vhost_dump):
945 """Process vhost dump.
947 :param vhost_dump: Vhost interface dump.
948 :type vhost_dump: dict
949 :returns: Processed vhost interface dump.
952 vhost_dump['interface_name'] = \
953 vhost_dump['interface_name'].rstrip('\x00')
954 vhost_dump['sock_filename'] = \
955 vhost_dump['sock_filename'].rstrip('\x00')
958 cmd = 'sw_interface_vhost_user_dump'
959 err_msg = 'Failed to get vhost-user dump on host {host}'.format(
961 with PapiSocketExecutor(node) as papi_exec:
962 details = papi_exec.add(cmd).get_details(err_msg)
966 process_vhost_dump(dump)
968 logger.debug('Vhost-user details:\n{vhost_details}'.format(
969 vhost_details=details))
973 def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
974 inner_vlan_id=None, type_subif=None):
975 """Create sub-interface on node. It is possible to set required
976 sub-interface type and VLAN tag(s).
978 :param node: Node to add sub-interface.
979 :param interface: Interface name on which create sub-interface.
980 :param sub_id: ID of the sub-interface to be created.
981 :param outer_vlan_id: Optional outer VLAN ID.
982 :param inner_vlan_id: Optional inner VLAN ID.
983 :param type_subif: Optional type of sub-interface. Values supported by
984 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
987 :type interface: str or int
989 :type outer_vlan_id: int
990 :type inner_vlan_id: int
991 :type type_subif: str
992 :returns: Name and index of created sub-interface.
994 :raises RuntimeError: If it is not possible to create sub-interface.
996 subif_types = type_subif.split()
999 if 'no_tags' in subif_types:
1000 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_NO_TAGS
1001 if 'one_tag' in subif_types:
1002 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_ONE_TAG
1003 if 'two_tags' in subif_types:
1004 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_TWO_TAGS
1005 if 'dot1ad' in subif_types:
1006 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DOT1AD
1007 if 'exact_match' in subif_types:
1008 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_EXACT_MATCH
1009 if 'default_sub' in subif_types:
1010 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_DEFAULT
1011 if type_subif == 'default_sub':
1012 flags = flags | SubInterfaceFlags.SUB_IF_API_FLAG_INNER_VLAN_ID_ANY\
1013 | SubInterfaceFlags.SUB_IF_API_FLAG_OUTER_VLAN_ID_ANY
1015 cmd = 'create_subif'
1017 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1019 sub_if_flags=flags.value if hasattr(flags, 'value') else int(flags),
1020 outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0,
1021 inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0
1023 err_msg = 'Failed to create sub-interface on host {host}'.format(
1025 with PapiSocketExecutor(node) as papi_exec:
1026 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1028 if_key = Topology.add_new_port(node, 'subinterface')
1029 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1030 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1031 Topology.update_interface_name(node, if_key, ifc_name)
1033 return '{ifc}.{s_id}'.format(ifc=interface, s_id=sub_id), sw_if_index
1036 def create_gre_tunnel_interface(node, source_ip, destination_ip):
1037 """Create GRE tunnel interface on node.
1039 :param node: VPP node to add tunnel interface.
1040 :param source_ip: Source of the GRE tunnel.
1041 :param destination_ip: Destination of the GRE tunnel.
1043 :type source_ip: str
1044 :type destination_ip: str
1045 :returns: Name and index of created GRE tunnel interface.
1047 :raises RuntimeError: If unable to create GRE tunnel interface.
1049 cmd = 'gre_tunnel_add_del'
1050 tunnel = dict(type=0,
1051 instance=Constants.BITWISE_NON_ZERO,
1053 dst=str(destination_ip),
1056 args = dict(is_add=1,
1058 err_msg = 'Failed to create GRE tunnel interface on host {host}'.format(
1060 with PapiSocketExecutor(node) as papi_exec:
1061 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1063 if_key = Topology.add_new_port(node, 'gre_tunnel')
1064 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1065 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1066 Topology.update_interface_name(node, if_key, ifc_name)
1068 return ifc_name, sw_if_index
1071 def vpp_create_loopback(node, mac=None):
1072 """Create loopback interface on VPP node.
1074 :param node: Node to create loopback interface on.
1075 :param mac: Optional MAC address for loopback interface.
1078 :returns: SW interface index.
1080 :raises RuntimeError: If it is not possible to create loopback on the
1083 cmd = 'create_loopback'
1084 args = dict(mac_address=L2Util.mac_to_bin(mac) if mac else 0)
1085 err_msg = 'Failed to create loopback interface on host {host}'.format(
1087 with PapiSocketExecutor(node) as papi_exec:
1088 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1090 if_key = Topology.add_new_port(node, 'loopback')
1091 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1092 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1093 Topology.update_interface_name(node, if_key, ifc_name)
1095 mac = InterfaceUtil.vpp_get_interface_mac(node, ifc_name)
1096 Topology.update_interface_mac_address(node, if_key, mac)
1101 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
1102 """Create bond interface on VPP node.
1104 :param node: DUT node from topology.
1105 :param mode: Link bonding mode.
1106 :param load_balance: Load balance (optional, valid for xor and lacp
1107 modes, otherwise ignored).
1108 :param mac: MAC address to assign to the bond interface (optional).
1111 :type load_balance: str
1113 :returns: Interface key (name) in topology.
1115 :raises RuntimeError: If it is not possible to create bond interface on
1119 args = dict(id=int(Constants.BITWISE_NON_ZERO),
1120 use_custom_mac=0 if mac is None else 1,
1121 mac_address=0 if mac is None else L2Util.mac_to_bin(mac),
1122 mode=getattr(LinkBondMode, '{md}'.format(
1123 md=mode.replace('-', '_').upper())).value,
1124 lb=0 if load_balance is None else getattr(
1125 LinkBondLoadBalance, '{lb}'.format(
1126 lb=load_balance.upper())).value)
1127 err_msg = 'Failed to create bond interface on host {host}'.format(
1129 with PapiSocketExecutor(node) as papi_exec:
1130 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1132 InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1134 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1139 def add_eth_interface(node, ifc_name=None, sw_if_index=None, ifc_pfx=None):
1140 """Add ethernet interface to current topology.
1142 :param node: DUT node from topology.
1143 :param ifc_name: Name of the interface.
1144 :param sw_if_index: SW interface index.
1145 :param ifc_pfx: Interface key prefix.
1148 :type sw_if_index: int
1151 if_key = Topology.add_new_port(node, ifc_pfx)
1153 if ifc_name and sw_if_index is None:
1154 sw_if_index = InterfaceUtil.vpp_get_interface_sw_index(
1156 Topology.update_interface_sw_if_index(node, if_key, sw_if_index)
1157 if sw_if_index and ifc_name is None:
1158 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_index)
1159 Topology.update_interface_name(node, if_key, ifc_name)
1160 ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_index)
1161 Topology.update_interface_mac_address(node, if_key, ifc_mac)
1164 def vpp_create_avf_interface(node, vf_pci_addr, num_rx_queues=None):
1165 """Create AVF interface on VPP node.
1167 :param node: DUT node from topology.
1168 :param vf_pci_addr: Virtual Function PCI address.
1169 :param num_rx_queues: Number of RX queues.
1171 :type vf_pci_addr: str
1172 :type num_rx_queues: int
1173 :returns: Interface key (name) in topology.
1175 :raises RuntimeError: If it is not possible to create AVF interface on
1179 args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr),
1181 rxq_num=int(num_rx_queues) if num_rx_queues else 0,
1184 err_msg = 'Failed to create AVF interface on host {host}'.format(
1186 with PapiSocketExecutor(node) as papi_exec:
1187 sw_if_index = papi_exec.add(cmd, **args).get_sw_if_index(err_msg)
1189 InterfaceUtil.add_eth_interface(node, sw_if_index=sw_if_index,
1191 if_key = Topology.get_interface_by_sw_index(node, sw_if_index)
1196 def vpp_enslave_physical_interface(node, interface, bond_if):
1197 """Enslave physical interface to bond interface on VPP node.
1199 :param node: DUT node from topology.
1200 :param interface: Physical interface key from topology file.
1201 :param bond_if: Load balance
1203 :type interface: str
1205 :raises RuntimeError: If it is not possible to enslave physical
1206 interface to bond interface on the node.
1208 cmd = 'bond_enslave'
1210 sw_if_index=Topology.get_interface_sw_index(node, interface),
1211 bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if),
1214 err_msg = 'Failed to enslave physical interface {ifc} to bond ' \
1215 'interface {bond} on host {host}'.format(ifc=interface,
1218 with PapiSocketExecutor(node) as papi_exec:
1219 papi_exec.add(cmd, **args).get_reply(err_msg)
1222 def vpp_show_bond_data_on_node(node, verbose=False):
1223 """Show (detailed) bond information on VPP node.
1225 :param node: DUT node from topology.
1226 :param verbose: If detailed information is required or not.
1230 cmd = 'sw_interface_bond_dump'
1231 err_msg = 'Failed to get bond interface dump on host {host}'.format(
1234 data = ('Bond data on node {host}:\n'.format(host=node['host']))
1235 with PapiSocketExecutor(node) as papi_exec:
1236 details = papi_exec.add(cmd).get_details(err_msg)
1238 for bond in details:
1239 data += ('{b}\n'.format(b=bond['interface_name'].rstrip('\x00')))
1240 data += (' mode: {m}\n'.format(m=bond['mode']).lower())
1241 data += (' load balance: {lb}\n'.format(lb=bond['lb']).lower())
1242 data += (' number of active slaves: {n}\n'.format(
1243 n=bond['active_slaves']))
1245 slave_data = InterfaceUtil.vpp_bond_slave_dump(
1246 node, Topology.get_interface_by_sw_index(
1247 node, bond['sw_if_index']))
1248 for slave in slave_data:
1249 if not slave['is_passive']:
1250 data += (' {s}\n'.format(s=slave['interface_name']))
1251 data += (' number of slaves: {n}\n'.format(n=bond['slaves']))
1253 for slave in slave_data:
1254 data += (' {s}\n'.format(s=slave['interface_name']))
1255 data += (' interface id: {i}\n'.format(i=bond['id']))
1256 data += (' sw_if_index: {i}\n'.format(i=bond['sw_if_index']))
1260 def vpp_bond_slave_dump(node, interface):
1261 """Get bond interface slave(s) data on VPP node.
1263 :param node: DUT node from topology.
1264 :param interface: Physical interface key from topology file.
1266 :type interface: str
1267 :returns: Bond slave interface data.
1270 def process_slave_dump(slave_dump):
1271 """Process slave dump.
1273 :param slave_dump: Slave interface dump.
1274 :type slave_dump: dict
1275 :returns: Processed slave interface dump.
1278 slave_dump['interface_name'] = slave_dump['interface_name'].\
1282 cmd = 'sw_interface_slave_dump'
1283 args = dict(sw_if_index=Topology.get_interface_sw_index(
1285 err_msg = 'Failed to get slave dump on host {host}'.format(
1288 with PapiSocketExecutor(node) as papi_exec:
1289 details = papi_exec.add(cmd, **args).get_details(err_msg)
1291 for dump in details:
1293 process_slave_dump(dump)
1295 logger.debug('Slave data:\n{slave_data}'.format(slave_data=details))
1299 def vpp_show_bond_data_on_all_nodes(nodes, verbose=False):
1300 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1302 :param nodes: Nodes in the topology.
1303 :param verbose: If detailed information is required or not.
1307 for node_data in nodes.values():
1308 if node_data['type'] == NodeType.DUT:
1309 InterfaceUtil.vpp_show_bond_data_on_node(node_data, verbose)
1312 def vpp_enable_input_acl_interface(node, interface, ip_version,
1314 """Enable input acl on interface.
1316 :param node: VPP node to setup interface for input acl.
1317 :param interface: Interface to setup input acl.
1318 :param ip_version: Version of IP protocol.
1319 :param table_index: Classify table index.
1321 :type interface: str or int
1322 :type ip_version: str
1323 :type table_index: int
1325 cmd = 'input_acl_set_interface'
1327 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1328 ip4_table_index=table_index if ip_version == 'ip4'
1329 else Constants.BITWISE_NON_ZERO,
1330 ip6_table_index=table_index if ip_version == 'ip6'
1331 else Constants.BITWISE_NON_ZERO,
1332 l2_table_index=table_index if ip_version == 'l2'
1333 else Constants.BITWISE_NON_ZERO,
1335 err_msg = 'Failed to enable input acl on interface {ifc}'.format(
1337 with PapiSocketExecutor(node) as papi_exec:
1338 papi_exec.add(cmd, **args).get_reply(err_msg)
1341 def get_interface_classify_table(node, interface):
1342 """Get name of classify table for the given interface.
1344 TODO: Move to Classify.py.
1346 :param node: VPP node to get data from.
1347 :param interface: Name or sw_if_index of a specific interface.
1349 :type interface: str or int
1350 :returns: Classify table name.
1353 if isinstance(interface, basestring):
1354 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1356 sw_if_index = interface
1358 cmd = 'classify_table_by_interface'
1359 args = dict(sw_if_index=sw_if_index)
1360 err_msg = 'Failed to get classify table name by interface {ifc}'.format(
1362 with PapiSocketExecutor(node) as papi_exec:
1363 reply = papi_exec.add(cmd, **args).get_reply(err_msg)
1368 def get_sw_if_index(node, interface_name):
1369 """Get sw_if_index for the given interface from actual interface dump.
1371 :param node: VPP node to get interface data from.
1372 :param interface_name: Name of the specific interface.
1374 :type interface_name: str
1375 :returns: sw_if_index of the given interface.
1378 interface_data = InterfaceUtil.vpp_get_interface_data(
1379 node, interface=interface_name)
1380 return interface_data.get('sw_if_index')
1383 def vxlan_gpe_dump(node, interface_name=None):
1384 """Get VxLAN GPE data for the given interface.
1386 :param node: VPP node to get interface data from.
1387 :param interface_name: Name of the specific interface. If None,
1388 information about all VxLAN GPE interfaces is returned.
1390 :type interface_name: str
1391 :returns: Dictionary containing data for the given VxLAN GPE interface
1392 or if interface=None, the list of dictionaries with all VxLAN GPE
1394 :rtype: dict or list
1396 def process_vxlan_gpe_dump(vxlan_dump):
1397 """Process vxlan_gpe dump.
1399 :param vxlan_dump: Vxlan_gpe nterface dump.
1400 :type vxlan_dump: dict
1401 :returns: Processed vxlan_gpe interface dump.
1404 if vxlan_dump['is_ipv6']:
1405 vxlan_dump['local'] = \
1406 ip_address(unicode(vxlan_dump['local']))
1407 vxlan_dump['remote'] = \
1408 ip_address(unicode(vxlan_dump['remote']))
1410 vxlan_dump['local'] = \
1411 ip_address(unicode(vxlan_dump['local'][0:4]))
1412 vxlan_dump['remote'] = \
1413 ip_address(unicode(vxlan_dump['remote'][0:4]))
1416 if interface_name is not None:
1417 sw_if_index = InterfaceUtil.get_interface_index(
1418 node, interface_name)
1420 sw_if_index = int(Constants.BITWISE_NON_ZERO)
1422 cmd = 'vxlan_gpe_tunnel_dump'
1423 args = dict(sw_if_index=sw_if_index)
1424 err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format(
1426 with PapiSocketExecutor(node) as papi_exec:
1427 details = papi_exec.add(cmd, **args).get_details(err_msg)
1429 data = list() if interface_name is None else dict()
1430 for dump in details:
1431 if interface_name is None:
1432 data.append(process_vxlan_gpe_dump(dump))
1433 elif dump['sw_if_index'] == sw_if_index:
1434 data = process_vxlan_gpe_dump(dump)
1437 logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format(
1438 vxlan_gpe_data=data))
1442 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1443 """Assign VPP interface to specific VRF/FIB table.
1445 :param node: VPP node where the FIB and interface are located.
1446 :param interface: Interface to be assigned to FIB.
1447 :param table_id: VRF table ID.
1448 :param ipv6: Assign to IPv6 table. Default False.
1450 :type interface: str or int
1454 cmd = 'sw_interface_set_table'
1456 sw_if_index=InterfaceUtil.get_interface_index(node, interface),
1458 vrf_id=int(table_id))
1459 err_msg = 'Failed to assign interface {ifc} to FIB table'.format(
1461 with PapiSocketExecutor(node) as papi_exec:
1462 papi_exec.add(cmd, **args).get_reply(err_msg)
1465 def set_linux_interface_mac(node, interface, mac, namespace=None,
1467 """Set MAC address for interface in linux.
1469 :param node: Node where to execute command.
1470 :param interface: Interface in namespace.
1471 :param mac: MAC to be assigned to interface.
1472 :param namespace: Execute command in namespace. Optional
1473 :param vf_id: Virtual Function id. Optional
1475 :type interface: str
1477 :type namespace: str
1480 mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1481 if vf_id is not None else 'address {mac}'.format(mac=mac)
1482 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1484 cmd = ('{ns} ip link set {interface} {mac}'.
1485 format(ns=ns_str, interface=interface, mac=mac_str))
1486 exec_cmd_no_error(node, cmd, sudo=True)
1489 def set_linux_interface_trust_on(node, interface, namespace=None,
1491 """Set trust on (promisc) for interface in linux.
1493 :param node: Node where to execute command.
1494 :param interface: Interface in namespace.
1495 :param namespace: Execute command in namespace. Optional
1496 :param vf_id: Virtual Function id. Optional
1498 :type interface: str
1499 :type namespace: str
1502 trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1503 if vf_id is not None else 'trust on'
1504 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1506 cmd = ('{ns} ip link set dev {interface} {trust}'.
1507 format(ns=ns_str, interface=interface, trust=trust_str))
1508 exec_cmd_no_error(node, cmd, sudo=True)
1511 def set_linux_interface_spoof_off(node, interface, namespace=None,
1513 """Set spoof off for interface in linux.
1515 :param node: Node where to execute command.
1516 :param interface: Interface in namespace.
1517 :param namespace: Execute command in namespace. Optional
1518 :param vf_id: Virtual Function id. Optional
1520 :type interface: str
1521 :type namespace: str
1524 spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1525 if vf_id is not None else 'spoof off'
1526 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1528 cmd = ('{ns} ip link set dev {interface} {spoof}'.
1529 format(ns=ns_str, interface=interface, spoof=spoof_str))
1530 exec_cmd_no_error(node, cmd, sudo=True)
1533 def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'):
1534 """Init PCI device by creating VIFs and bind them to vfio-pci for AVF
1535 driver testing on DUT.
1537 :param node: DUT node.
1538 :param ifc_key: Interface key from topology file.
1539 :param numvfs: Number of VIFs to initialize, 0 - disable the VIFs.
1540 :param osi_layer: OSI Layer type to initialize TG with.
1541 Default value "L2" sets linux interface spoof off.
1545 :type osi_layer: str
1546 :returns: Virtual Function topology interface keys.
1548 :raises RuntimeError: If a reason preventing initialization is found.
1550 # Read PCI address and driver.
1551 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1552 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1553 uio_driver = Topology.get_uio_driver(node)
1554 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1555 if kernel_driver not in ("i40e", "i40evf"):
1557 "AVF needs i40e-compatible driver, not {driver} at node {host}"
1558 " ifc {ifc}".format(
1559 driver=kernel_driver, host=node["host"], ifc=ifc_key))
1560 current_driver = DUTSetup.get_pci_dev_driver(
1561 node, pf_pci_addr.replace(':', r'\:'))
1563 VPPUtil.stop_vpp_service(node)
1564 if current_driver != kernel_driver:
1565 # PCI device must be re-bound to kernel driver before creating VFs.
1566 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1567 # Stop VPP to prevent deadlock.
1568 # Unbind from current driver.
1569 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1570 # Bind to kernel driver.
1571 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1573 # Initialize PCI VFs.
1574 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1577 # Set MAC address and bind each virtual function to uio driver.
1578 for vf_id in range(numvfs):
1579 vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1580 pf_mac_addr[3], pf_mac_addr[4],
1581 pf_mac_addr[5], "{:02x}".format(vf_id)])
1583 pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1584 format(pci=pf_pci_addr)
1585 InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1587 if osi_layer == 'L2':
1588 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1590 InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1593 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1594 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1596 # Add newly created ports into topology file
1597 vf_ifc_name = '{pf_if_key}_vif'.format(pf_if_key=ifc_key)
1598 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1599 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1600 Topology.update_interface_name(node, vf_ifc_key,
1601 vf_ifc_name+str(vf_id+1))
1602 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1603 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1604 vf_ifc_keys.append(vf_ifc_key)
1609 def vpp_sw_interface_rx_placement_dump(node):
1610 """Dump VPP interface RX placement on node.
1612 :param node: Node to run command on.
1614 :returns: Thread mapping information as a list of dictionaries.
1617 cmd = 'sw_interface_rx_placement_dump'
1618 err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format(
1619 cmd=cmd, host=node['host'])
1620 with PapiSocketExecutor(node) as papi_exec:
1621 for ifc in node['interfaces'].values():
1622 if ifc['vpp_sw_index'] is not None:
1623 papi_exec.add(cmd, sw_if_index=ifc['vpp_sw_index'])
1624 details = papi_exec.get_details(err_msg)
1625 return sorted(details, key=lambda k: k['sw_if_index'])
1628 def vpp_sw_interface_set_rx_placement(node, sw_if_index, queue_id,
1630 """Set interface RX placement to worker on node.
1632 :param node: Node to run command on.
1633 :param sw_if_index: VPP SW interface index.
1634 :param queue_id: VPP interface queue ID.
1635 :param worker_id: VPP worker ID (indexing from 0).
1637 :type sw_if_index: int
1639 :type worker_id: int
1640 :raises RuntimeError: If failed to run command on host or if no API
1643 cmd = 'sw_interface_set_rx_placement'
1644 err_msg = "Failed to set interface RX placement to worker on host " \
1645 "{host}!".format(host=node['host'])
1647 sw_if_index=sw_if_index,
1649 worker_id=worker_id,
1652 with PapiSocketExecutor(node) as papi_exec:
1653 papi_exec.add(cmd, **args).get_reply(err_msg)
1656 def vpp_round_robin_rx_placement(node, prefix):
1657 """Set Round Robin interface RX placement on all worker threads
1660 :param node: Topology nodes.
1661 :param prefix: Interface name prefix.
1666 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1
1669 for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node):
1670 for interface in node['interfaces'].values():
1671 if placement['sw_if_index'] == interface['vpp_sw_index'] \
1672 and prefix in interface['name']:
1673 InterfaceUtil.vpp_sw_interface_set_rx_placement(
1674 node, placement['sw_if_index'], placement['queue_id'],
1675 worker_id % worker_cnt)
1679 def vpp_round_robin_rx_placement_on_all_duts(nodes, prefix):
1680 """Set Round Robin interface RX placement on all worker threads
1683 :param nodes: Topology nodes.
1684 :param prefix: Interface name prefix.
1688 for node in nodes.values():
1689 if node['type'] == NodeType.DUT:
1690 InterfaceUtil.vpp_round_robin_rx_placement(node, prefix)