1 # Copyright (c) 2018 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 time, sleep
18 from robot.api import logger
20 from resources.libraries.python.ssh import SSH
21 from resources.libraries.python.IPUtil import convert_ipv4_netmask_prefix
22 from resources.libraries.python.DUTSetup import DUTSetup
23 from resources.libraries.python.ssh import exec_cmd_no_error
24 from resources.libraries.python.topology import NodeType, Topology
25 from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal
26 from resources.libraries.python.VatJsonUtil import VatJsonUtil
27 from resources.libraries.python.VPPUtil import VPPUtil
28 from resources.libraries.python.parsers.JsonParser import JsonParser
29 from resources.libraries.python.CpuUtils import CpuUtils
31 class InterfaceUtil(object):
32 """General utilities for managing interfaces"""
34 __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
37 def set_interface_state(node, interface, state, if_type="key"):
38 """Set interface state on a node.
40 Function can be used for DUTs as well as for TGs.
42 :param node: Node where the interface is.
43 :param interface: Interface key or sw_if_index or name.
44 :param state: One of 'up' or 'down'.
45 :param if_type: Interface type
47 :type interface: str or int
51 :raises ValueError: If the interface type is unknown.
52 :raises ValueError: If the state of interface is unexpected.
53 :raises ValueError: If the node has an unknown node type.
57 if isinstance(interface, basestring):
58 sw_if_index = Topology.get_interface_sw_index(node, interface)
59 iface_name = Topology.get_interface_name(node, interface)
61 sw_if_index = interface
62 elif if_type == "name":
63 iface_key = Topology.get_interface_by_name(node, interface)
64 if iface_key is not None:
65 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
66 iface_name = interface
68 raise ValueError("if_type unknown: {}".format(if_type))
70 if node['type'] == NodeType.DUT:
72 state = 'admin-up link-up'
74 state = 'admin-down link-down'
76 raise ValueError('Unexpected interface state: {}'.format(state))
77 VatExecutor.cmd_from_template(node, 'set_if_state.vat',
78 sw_if_index=sw_if_index, state=state)
79 elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
80 cmd = 'ip link set {} {}'.format(iface_name, state)
81 exec_cmd_no_error(node, cmd, sudo=True)
83 raise ValueError('Node {} has unknown NodeType: "{}"'
84 .format(node['host'], node['type']))
87 def set_interface_ethernet_mtu(node, iface_key, mtu):
88 """Set Ethernet MTU for specified interface.
90 Function can be used only for TGs.
92 :param node: Node where the interface is.
93 :param iface_key: Interface key from topology file.
94 :param mtu: MTU to set.
99 :raises ValueError: If the node type is "DUT".
100 :raises ValueError: If the node has an unknown node type.
102 if node['type'] == NodeType.DUT:
103 raise ValueError('Node {}: Setting Ethernet MTU for interface '
104 'on DUT nodes not supported', node['host'])
105 elif node['type'] == NodeType.TG:
106 iface_name = Topology.get_interface_name(node, iface_key)
107 cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
108 exec_cmd_no_error(node, cmd, sudo=True)
110 raise ValueError('Node {} has unknown NodeType: "{}"'
111 .format(node['host'], node['type']))
114 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
115 """Set default Ethernet MTU on all interfaces on node.
117 Function can be used only for TGs.
119 :param node: Node where to set default MTU.
123 for ifc in node['interfaces']:
124 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
127 def vpp_set_interface_mtu(node, interface, mtu=9200):
128 """Set Ethernet MTU on interface.
130 :param node: VPP node.
131 :param interface: Interface to setup MTU. Default: 9200.
132 :param mtu: Ethernet MTU size in Bytes.
134 :type interface: str or int
137 if isinstance(interface, basestring):
138 sw_if_index = Topology.get_interface_sw_index(node, interface)
140 sw_if_index = interface
143 with VatTerminal(node, json_param=False) as vat:
144 vat.vat_terminal_exec_cmd_from_template(
145 "hw_interface_set_mtu.vat", sw_if_index=sw_if_index,
149 def vpp_set_interfaces_mtu_on_node(node, mtu=9200):
150 """Set Ethernet MTU on all interfaces.
152 :param node: VPP node.
153 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
157 for interface in node['interfaces']:
158 InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu)
161 def vpp_set_interfaces_mtu_on_all_duts(nodes, mtu=9200):
162 """Set Ethernet MTU on all interfaces on all DUTs.
164 :param nodes: VPP nodes.
165 :param mtu: Ethernet MTU size in Bytes. Default: 9200.
169 for node in nodes.values():
170 if node['type'] == NodeType.DUT:
171 InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu)
174 def vpp_node_interfaces_ready_wait(node, timeout=30):
175 """Wait until all interfaces with admin-up are in link-up state.
177 :param node: Node to wait on.
178 :param timeout: Waiting timeout in seconds (optional, default 10s).
182 :raises RuntimeError: If the timeout period value has elapsed.
188 out = InterfaceUtil.vpp_get_interface_data(node)
189 if time() - start > timeout:
190 for interface in out:
191 if interface.get('admin_up_down') == 1:
192 if interface.get('link_up_down') != 1:
193 logger.debug('{0} link-down'.format(
194 interface.get('interface_name')))
195 raise RuntimeError('timeout, not up {0}'.format(not_ready))
197 for interface in out:
198 if interface.get('admin_up_down') == 1:
199 if interface.get('link_up_down') != 1:
200 not_ready.append(interface.get('interface_name'))
204 logger.debug('Interfaces still in link-down state: {0}, '
205 'waiting...'.format(not_ready))
209 def vpp_nodes_interfaces_ready_wait(nodes, timeout=30):
210 """Wait until all interfaces with admin-up are in link-up state for
213 :param nodes: List of nodes to wait on.
214 :param timeout: Seconds to wait per node for all interfaces to come up.
220 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
223 def all_vpp_interfaces_ready_wait(nodes, timeout=30):
224 """Wait until all interfaces with admin-up are in link-up state for all
225 nodes in the topology.
227 :param nodes: Nodes in the topology.
228 :param timeout: Seconds to wait per node for all interfaces to come up.
233 for node in nodes.values():
234 if node['type'] == NodeType.DUT:
235 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
238 def vpp_get_interface_data(node, interface=None):
239 """Get all interface data from a VPP node. If a name or
240 sw_interface_index is provided, return only data for the matching
243 :param node: VPP node to get interface data from.
244 :param interface: Numeric index or name string of a specific interface.
246 :type interface: int or str
247 :returns: List of dictionaries containing data for each interface, or a
248 single dictionary for the specified interface.
250 :raises TypeError: if the data type of interface is neither basestring
253 with VatTerminal(node) as vat:
254 response = vat.vat_terminal_exec_cmd_from_template(
255 "interface_dump.vat")
259 if interface is not None:
260 if isinstance(interface, basestring):
261 param = "interface_name"
262 elif isinstance(interface, int):
263 param = "sw_if_index"
267 if data_if[param] == interface:
273 def vpp_get_interface_name(node, sw_if_index):
274 """Get interface name for the given SW interface index from actual
277 :param node: VPP node to get interface data from.
278 :param sw_if_index: SW interface index of the specific interface.
280 :type sw_if_index: int
281 :returns: Name of the given interface.
285 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
286 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
287 if_data = InterfaceUtil.vpp_get_interface_data(
288 node, if_data['sup_sw_if_index'])
290 if_name = if_data["interface_name"]
296 def vpp_get_interface_mac(node, interface=None):
297 """Get MAC address for the given interface from actual interface dump.
299 :param node: VPP node to get interface data from.
300 :param interface: Numeric index or name string of a specific interface.
302 :type interface: int or str
303 :returns: MAC address.
307 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
308 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
309 if_data = InterfaceUtil.vpp_get_interface_data(
310 node, if_data['sup_sw_if_index'])
311 mac_data = [str(hex(item))[2:] for item in if_data['l2_address'][:6]]
313 for item in mac_data:
316 mac_data_nice.append(item)
317 mac = ":".join(mac_data_nice)
321 def vpp_get_interface_ip_addresses(node, interface, ip_version):
322 """Get list of IP addresses from an interface on a VPP node.
324 :param node: VPP node to get data from.
325 :param interface: Name of an interface on the VPP node.
326 :param ip_version: IP protocol version (ipv4 or ipv6).
329 :type ip_version: str
330 :returns: List of dictionaries, each containing IP address, subnet
331 prefix length and also the subnet mask for ipv4 addresses.
332 Note: A single interface may have multiple IP addresses assigned.
337 sw_if_index = Topology.convert_interface_reference(
338 node, interface, "sw_if_index")
340 if isinstance(interface, basestring):
341 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
345 with VatTerminal(node) as vat:
346 response = vat.vat_terminal_exec_cmd_from_template(
347 "ip_address_dump.vat", ip_version=ip_version,
348 sw_if_index=sw_if_index)
352 if ip_version == "ipv4":
354 item["netmask"] = convert_ipv4_netmask_prefix(
355 item["prefix_length"])
359 def tg_set_interface_driver(node, pci_addr, driver):
360 """Set interface driver on the TG node.
362 :param node: Node to set interface driver on (must be TG node).
363 :param pci_addr: PCI address of the interface.
364 :param driver: Driver name.
368 :raises RuntimeError: If unbinding from the current driver fails.
369 :raises RuntimeError: If binding to the new driver fails.
371 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
372 if old_driver == driver:
378 # Unbind from current driver
379 if old_driver is not None:
380 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
381 .format(pci_addr, old_driver)
382 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
383 if int(ret_code) != 0:
384 raise RuntimeError("'{0}' failed on '{1}'"
385 .format(cmd, node['host']))
387 # Bind to the new driver
388 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
389 .format(pci_addr, driver)
390 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
391 if int(ret_code) != 0:
392 raise RuntimeError("'{0}' failed on '{1}'"
393 .format(cmd, node['host']))
396 def tg_get_interface_driver(node, pci_addr):
397 """Get interface driver from the TG node.
399 :param node: Node to get interface driver on (must be TG node).
400 :param pci_addr: PCI address of the interface.
403 :returns: Interface driver or None if not found.
405 :raises RuntimeError: If PCI rescan or lspci command execution failed.
407 return DUTSetup.get_pci_dev_driver(node, pci_addr)
410 def tg_set_interfaces_udev_rules(node):
411 """Set udev rules for interfaces.
413 Create udev rules file in /etc/udev/rules.d where are rules for each
414 interface used by TG node, based on MAC interface has specific name.
415 So after unbind and bind again to kernel driver interface has same
416 name as before. This must be called after TG has set name for each
417 port in topology dictionary.
419 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
422 :param node: Node to set udev rules on (must be TG node).
424 :raises RuntimeError: If setting of udev rules fails.
429 cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
430 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
431 if int(ret_code) != 0:
432 raise RuntimeError("'{0}' failed on '{1}'"
433 .format(cmd, node['host']))
435 for interface in node['interfaces'].values():
436 rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
437 '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
438 interface['name'] + '\\"'
439 cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
440 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
441 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
442 if int(ret_code) != 0:
443 raise RuntimeError("'{0}' failed on '{1}'"
444 .format(cmd, node['host']))
446 cmd = '/etc/init.d/udev restart'
447 ssh.exec_command_sudo(cmd)
450 def tg_set_interfaces_default_driver(node):
451 """Set interfaces default driver specified in topology yaml file.
453 :param node: Node to setup interfaces driver on (must be TG node).
456 for interface in node['interfaces'].values():
457 InterfaceUtil.tg_set_interface_driver(node,
458 interface['pci_address'],
462 def update_vpp_interface_data_on_node(node):
463 """Update vpp generated interface data for a given node in DICT__nodes.
465 Updates interface names, software if index numbers and any other details
466 generated specifically by vpp that are unknown before testcase run.
467 It does this by dumping interface list to JSON output from all
468 devices using vpp_api_test, and pairing known information from topology
469 (mac address/pci address of interface) to state from VPP.
471 :param node: Node selected from DICT__nodes.
474 vat_executor = VatExecutor()
475 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
476 interface_dump_json = vat_executor.get_script_stdout()
477 VatJsonUtil.update_vpp_interface_data_from_json(node,
481 def update_nic_interface_names(node):
482 """Update interface names based on nic type and PCI address.
484 This method updates interface names in the same format as VPP does.
486 :param node: Node dictionary.
489 for ifc in node['interfaces'].values():
490 if_pci = ifc['pci_address'].replace('.', ':').split(':')
491 bus = '{:x}'.format(int(if_pci[1], 16))
492 dev = '{:x}'.format(int(if_pci[2], 16))
493 fun = '{:x}'.format(int(if_pci[3], 16))
494 loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
495 if ifc['model'] == 'Intel-XL710':
496 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
497 elif ifc['model'] == 'Intel-X710':
498 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
499 elif ifc['model'] == 'Intel-X520-DA2':
500 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
501 elif ifc['model'] == 'Cisco-VIC-1385':
502 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
503 elif ifc['model'] == 'Cisco-VIC-1227':
504 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
506 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
509 def update_nic_interface_names_on_all_duts(nodes):
510 """Update interface names based on nic type and PCI address on all DUTs.
512 This method updates interface names in the same format as VPP does.
514 :param nodes: Topology nodes.
517 for node in nodes.values():
518 if node['type'] == NodeType.DUT:
519 InterfaceUtil.update_nic_interface_names(node)
522 def update_tg_interface_data_on_node(node):
523 """Update interface name for TG/linux node in DICT__nodes.
526 # for dev in `ls /sys/class/net/`;
527 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
528 "52:54:00:9f:82:63": "eth0"
529 "52:54:00:77:ae:a9": "eth1"
530 "52:54:00:e1:8a:0f": "eth2"
531 "00:00:00:00:00:00": "lo"
533 .. note:: TODO: parse lshw -json instead
535 :param node: Node selected from DICT__nodes.
537 :raises RuntimeError: If getting of interface name and MAC fails.
539 # First setup interface driver specified in yaml file
540 InterfaceUtil.tg_set_interfaces_default_driver(node)
542 # Get interface names
546 cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
547 '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
549 (ret_code, stdout, _) = ssh.exec_command(cmd)
550 if int(ret_code) != 0:
551 raise RuntimeError('Get interface name and MAC failed')
552 tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
553 interfaces = JsonParser().parse_data(tmp)
554 for interface in node['interfaces'].values():
555 name = interfaces.get(interface['mac_address'])
558 interface['name'] = name
560 # Set udev rules for interfaces
561 InterfaceUtil.tg_set_interfaces_udev_rules(node)
564 def iface_update_numa_node(node):
565 """For all interfaces from topology file update numa node based on
566 information from the node.
568 :param node: Node from topology.
571 :raises ValueError: If numa node ia less than 0.
572 :raises RuntimeError: If update of numa node failes.
575 for if_key in Topology.get_node_interfaces(node):
576 if_pci = Topology.get_interface_pci_addr(node, if_key)
578 cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
580 (ret, out, _) = ssh.exec_command(cmd)
585 if CpuUtils.cpu_node_count(node) == 1:
590 logger.trace('Reading numa location failed for: {0}'\
593 Topology.set_interface_numa_node(node, if_key,
597 raise RuntimeError('Update numa node failed for: {0}'\
601 def update_all_numa_nodes(nodes, skip_tg=False):
602 """For all nodes and all their interfaces from topology file update numa
603 node information based on information from the node.
605 :param nodes: Nodes in the topology.
606 :param skip_tg: Skip TG node
611 for node in nodes.values():
612 if node['type'] == NodeType.DUT:
613 InterfaceUtil.iface_update_numa_node(node)
614 elif node['type'] == NodeType.TG and not skip_tg:
615 InterfaceUtil.iface_update_numa_node(node)
618 def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
620 """Update interface names on all nodes in DICT__nodes.
622 This method updates the topology dictionary by querying interface lists
623 of all nodes mentioned in the topology dictionary.
625 :param nodes: Nodes in the topology.
626 :param skip_tg: Skip TG node
627 :param numa_node: Retrieve numa_node location.
630 :type numa_node: bool
632 for node_data in nodes.values():
633 if node_data['type'] == NodeType.DUT:
634 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
635 elif node_data['type'] == NodeType.TG and not skip_tg:
636 InterfaceUtil.update_tg_interface_data_on_node(node_data)
639 if node_data['type'] == NodeType.DUT:
640 InterfaceUtil.iface_update_numa_node(node_data)
641 elif node_data['type'] == NodeType.TG and not skip_tg:
642 InterfaceUtil.iface_update_numa_node(node_data)
645 def create_vlan_subinterface(node, interface, vlan):
646 """Create VLAN subinterface on node.
648 :param node: Node to add VLAN subinterface on.
649 :param interface: Interface name on which create VLAN subinterface.
650 :param vlan: VLAN ID of the subinterface to be created.
654 :returns: Name and index of created subinterface.
656 :raises RuntimeError: if it is unable to create VLAN subinterface on the
659 iface_key = Topology.get_interface_by_name(node, interface)
660 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
662 output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat",
663 sw_if_index=sw_if_index,
665 if output[0]["retval"] == 0:
666 sw_subif_idx = output[0]["sw_if_index"]
667 logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} '
668 'created on node {}'.format(sw_subif_idx,
670 if_key = Topology.add_new_port(node, "vlan_subif")
671 Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
672 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
673 Topology.update_interface_name(node, if_key, ifc_name)
675 raise RuntimeError('Unable to create VLAN subinterface on node {}'
676 .format(node['host']))
678 with VatTerminal(node, False) as vat:
679 vat.vat_terminal_exec_cmd('exec show interfaces')
681 return '{}.{}'.format(interface, vlan), sw_subif_idx
684 def create_vxlan_interface(node, vni, source_ip, destination_ip):
685 """Create VXLAN interface and return sw if index of created interface.
687 Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT
690 :param node: Node where to create VXLAN interface.
691 :param vni: VXLAN Network Identifier.
692 :param source_ip: Source IP of a VXLAN Tunnel End Point.
693 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
697 :type destination_ip: str
698 :returns: SW IF INDEX of created interface.
700 :raises RuntimeError: if it is unable to create VxLAN interface on the
703 output = VatExecutor.cmd_from_template(node, "vxlan_create.vat",
709 if output["retval"] == 0:
710 sw_if_idx = output["sw_if_index"]
711 if_key = Topology.add_new_port(node, "vxlan_tunnel")
712 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
713 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
714 Topology.update_interface_name(node, if_key, ifc_name)
717 raise RuntimeError("Unable to create VXLAN interface on node {0}"
721 def vxlan_dump(node, interface=None):
722 """Get VxLAN data for the given interface.
724 :param node: VPP node to get interface data from.
725 :param interface: Numeric index or name string of a specific interface.
726 If None, information about all VxLAN interfaces is returned.
728 :type interface: int or str
729 :returns: Dictionary containing data for the given VxLAN interface or if
730 interface=None, the list of dictionaries with all VxLAN interfaces.
732 :raises TypeError: if the data type of interface is neither basestring
735 param = "sw_if_index"
736 if interface is None:
739 elif isinstance(interface, basestring):
740 sw_if_index = Topology.get_interface_sw_index(node, interface)
741 elif isinstance(interface, int):
742 sw_if_index = interface
744 raise TypeError("Wrong interface format {0}".format(interface))
746 with VatTerminal(node) as vat:
747 response = vat.vat_terminal_exec_cmd_from_template(
748 "vxlan_dump.vat", param=param, sw_if_index=sw_if_index)
751 for vxlan in response[0]:
752 if vxlan["sw_if_index"] == sw_if_index:
758 def vhost_user_dump(node):
759 """Get vhost-user data for the given node.
761 :param node: VPP node to get interface data from.
763 :returns: List of dictionaries with all vhost-user interfaces.
766 with VatTerminal(node) as vat:
767 response = vat.vat_terminal_exec_cmd_from_template(
768 "vhost_user_dump.vat")
773 def tap_dump(node, name=None):
774 """Get all TAP interface data from the given node, or data about
775 a specific TAP interface.
777 :param node: VPP node to get data from.
778 :param name: Optional name of a specific TAP interface.
781 :returns: Dictionary of information about a specific TAP interface, or
782 a List of dictionaries containing all TAP data for the given node.
785 with VatTerminal(node) as vat:
786 response = vat.vat_terminal_exec_cmd_from_template(
790 for item in response[0]:
791 if name == item['dev_name']:
796 def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
797 inner_vlan_id=None, type_subif=None):
798 """Create sub-interface on node. It is possible to set required
799 sub-interface type and VLAN tag(s).
801 :param node: Node to add sub-interface.
802 :param interface: Interface name on which create sub-interface.
803 :param sub_id: ID of the sub-interface to be created.
804 :param outer_vlan_id: Optional outer VLAN ID.
805 :param inner_vlan_id: Optional inner VLAN ID.
806 :param type_subif: Optional type of sub-interface. Values supported by
807 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
810 :type interface: str or int
812 :type outer_vlan_id: int
813 :type inner_vlan_id: int
814 :type type_subif: str
815 :returns: Name and index of created sub-interface.
817 :raises RuntimeError: If it is not possible to create sub-interface.
820 outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\
821 if outer_vlan_id else ''
823 inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\
824 if inner_vlan_id else ''
826 if type_subif is None:
829 if isinstance(interface, basestring):
830 iface_key = Topology.get_interface_by_name(node, interface)
831 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
833 sw_if_index = interface
835 output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat",
836 sw_if_index=sw_if_index,
838 outer_vlan_id=outer_vlan_id,
839 inner_vlan_id=inner_vlan_id,
840 type_subif=type_subif)
842 if output[0]["retval"] == 0:
843 sw_subif_idx = output[0]["sw_if_index"]
844 logger.trace('Created subinterface with index {}'
845 .format(sw_subif_idx))
846 if_key = Topology.add_new_port(node, "subinterface")
847 Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
848 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
849 Topology.update_interface_name(node, if_key, ifc_name)
851 raise RuntimeError('Unable to create sub-interface on node {}'
852 .format(node['host']))
854 with VatTerminal(node, json_param=False) as vat:
855 vat.vat_terminal_exec_cmd('exec show interfaces')
857 name = '{}.{}'.format(interface, sub_id)
858 return name, sw_subif_idx
861 def create_gre_tunnel_interface(node, source_ip, destination_ip):
862 """Create GRE tunnel interface on node.
864 :param node: VPP node to add tunnel interface.
865 :param source_ip: Source of the GRE tunnel.
866 :param destination_ip: Destination of the GRE tunnel.
869 :type destination_ip: str
870 :returns: Name and index of created GRE tunnel interface.
872 :raises RuntimeError: If unable to create GRE tunnel interface.
874 output = VatExecutor.cmd_from_template(node, "create_gre.vat",
879 if output["retval"] == 0:
880 sw_if_idx = output["sw_if_index"]
882 vat_executor = VatExecutor()
883 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
884 interface_dump_json = vat_executor.get_script_stdout()
885 name = VatJsonUtil.get_interface_name_from_json(
886 interface_dump_json, sw_if_idx)
888 if_key = Topology.add_new_port(node, "gre_tunnel")
889 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
890 Topology.update_interface_name(node, if_key, name)
892 return name, sw_if_idx
894 raise RuntimeError('Unable to create GRE tunnel on node {}.'
898 def vpp_create_loopback(node):
899 """Create loopback interface on VPP node.
901 :param node: Node to create loopback interface on.
903 :returns: SW interface index.
905 :raises RuntimeError: If it is not possible to create loopback on the
908 out = VatExecutor.cmd_from_template(node, "create_loopback.vat")
909 if out[0].get('retval') == 0:
910 sw_if_idx = out[0].get('sw_if_index')
911 if_key = Topology.add_new_port(node, "loopback")
912 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
913 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
914 Topology.update_interface_name(node, if_key, ifc_name)
917 raise RuntimeError('Create loopback failed on node "{}"'
918 .format(node['host']))
921 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
922 """Create bond interface on VPP node.
924 :param node: DUT node from topology.
925 :param mode: Link bonding mode.
926 :param load_balance: Load balance (optional, valid for xor and lacp
927 modes, otherwise ignored).
928 :param mac: MAC address to assign to the bond interface (optional).
931 :type load_balance: str
933 :returns: Interface key (name) in topology.
935 :raises RuntimeError: If it is not possible to create bond interface on
938 hw_addr = '' if mac is None else 'hw-addr {mac}'.format(mac=mac)
939 ldb = '' if load_balance is None \
940 else 'lb {ldb}'.format(ldb=load_balance)
942 output = VatExecutor.cmd_from_template(
943 node, 'create_bond_interface.vat', mode=mode, lb=ldb, mac=hw_addr)
945 if output[0].get('retval') == 0:
946 sw_if_idx = output[0].get('sw_if_index')
947 InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx,
949 if_key = Topology.get_interface_by_sw_index(node, sw_if_idx)
952 raise RuntimeError('Create bond interface failed on "{host}"'.
953 format(host=node['host']))
956 def add_eth_interface(node, ifc_name=None, sw_if_idx=None, ifc_pfx=None):
957 """Add ethernet interface to current topology.
959 :param node: DUT node from topology.
960 :param ifc_name: Name of the interface.
961 :param sw_if_idx: SW interface index.
962 :param ifc_pfx: Interface key prefix.
968 if_key = Topology.add_new_port(node, ifc_pfx)
970 vat_executor = VatExecutor()
971 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
972 interface_dump_json = vat_executor.get_script_stdout()
974 if ifc_name and sw_if_idx is None:
975 sw_if_idx = VatJsonUtil.get_interface_sw_index_from_json(
976 interface_dump_json, ifc_name)
977 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
978 if sw_if_idx and ifc_name is None:
979 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
980 Topology.update_interface_name(node, if_key, ifc_name)
981 ifc_mac = VatJsonUtil.get_interface_mac_from_json(
982 interface_dump_json, sw_if_idx)
983 Topology.update_interface_mac_address(node, if_key, ifc_mac)
986 def vpp_create_avf_interface(node, vf_pci_addr):
987 """Create AVF interface on VPP node.
989 :param node: DUT node from topology.
990 :param vf_pci_addr: Virtual Function PCI address.
992 :type vf_pci_addr: str
993 :returns: Interface key (name) in topology.
995 :raises RuntimeError: If it is not possible to create AVF interface on
998 with VatTerminal(node, json_param=False) as vat:
999 vat.vat_terminal_exec_cmd_from_template('create_avf_interface.vat',
1000 vf_pci_addr=vf_pci_addr)
1001 output = vat.vat_stdout
1003 if output is not None:
1004 sw_if_idx = int(output.split()[4])
1005 InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx,
1007 if_key = Topology.get_interface_by_sw_index(node, sw_if_idx)
1010 raise RuntimeError('Create AVF interface failed on {host}'.
1011 format(host=node['host']))
1014 def vpp_enslave_physical_interface(node, interface, bond_interface):
1015 """Enslave physical interface to bond interface on VPP node.
1017 :param node: DUT node from topology.
1018 :param interface: Physical interface key from topology file.
1019 :param bond_interface: Load balance
1021 :type interface: str
1022 :type bond_interface: str
1023 :raises RuntimeError: If it is not possible to enslave physical
1024 interface to bond interface on the node.
1026 ifc = Topology.get_interface_sw_index(node, interface)
1027 bond_ifc = Topology.get_interface_sw_index(node, bond_interface)
1029 output = VatExecutor.cmd_from_template(
1030 node, 'enslave_physical_interface.vat', p_int=ifc, b_int=bond_ifc)
1032 retval = output[0].get('retval', None)
1033 if retval is None or int(retval) != 0:
1034 raise RuntimeError('Enslave physical interface {ifc} to bond '
1035 'interface {bond} failed on node "{n}"'
1036 .format(ifc=interface, bond=bond_interface,
1040 def vpp_show_bond_data_on_node(node, details=False):
1041 """Show (detailed) bond information on VPP node.
1043 :param node: DUT node from topology.
1044 :param details: If detailed information is required or not.
1048 cmd = 'exec show bond details' if details else 'exec show bond'
1049 with VatTerminal(node, json_param=False) as vat:
1050 vat.vat_terminal_exec_cmd(cmd)
1053 def vpp_show_bond_data_on_all_nodes(nodes, details=False):
1054 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
1056 :param nodes: Nodes in the topology.
1057 :param details: If detailed information is required or not.
1061 for node_data in nodes.values():
1062 if node_data['type'] == NodeType.DUT:
1063 InterfaceUtil.vpp_show_bond_data_on_node(node_data, details)
1066 def vpp_enable_input_acl_interface(node, interface, ip_version,
1068 """Enable input acl on interface.
1070 :param node: VPP node to setup interface for input acl.
1071 :param interface: Interface to setup input acl.
1072 :param ip_version: Version of IP protocol.
1073 :param table_index: Classify table index.
1075 :type interface: str or int
1076 :type ip_version: str
1077 :type table_index: int
1079 if isinstance(interface, basestring):
1080 sw_if_index = Topology.get_interface_sw_index(node, interface)
1082 sw_if_index = interface
1084 with VatTerminal(node) as vat:
1085 vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat",
1086 sw_if_index=sw_if_index,
1087 ip_version=ip_version,
1088 table_index=table_index)
1091 def get_interface_classify_table(node, interface):
1092 """Get name of classify table for the given interface.
1094 :param node: VPP node to get data from.
1095 :param interface: Name or sw_if_index of a specific interface.
1097 :type interface: str or int
1098 :returns: Classify table name.
1101 if isinstance(interface, basestring):
1102 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1104 sw_if_index = interface
1106 with VatTerminal(node) as vat:
1107 data = vat.vat_terminal_exec_cmd_from_template(
1108 "classify_interface_table.vat",
1109 sw_if_index=sw_if_index)
1113 def get_interface_vrf_table(node, interface):
1114 """Get vrf ID for the given interface.
1116 :param node: VPP node.
1117 :param interface: Name or sw_if_index of a specific interface.
1119 :type interface: str or int
1120 :returns: vrf ID of the specified interface.
1124 if isinstance(interface, basestring):
1125 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1127 sw_if_index = interface
1129 with VatTerminal(node) as vat:
1130 data = vat.vat_terminal_exec_cmd_from_template(
1131 "interface_vrf_dump.vat",
1132 sw_if_index=sw_if_index)
1133 return data[0]["vrf_id"]
1136 def get_sw_if_index(node, interface_name):
1137 """Get sw_if_index for the given interface from actual interface dump.
1139 :param node: VPP node to get interface data from.
1140 :param interface_name: Name of the specific interface.
1142 :type interface_name: str
1143 :returns: sw_if_index of the given interface.
1147 with VatTerminal(node) as vat:
1148 if_data = vat.vat_terminal_exec_cmd_from_template(
1149 "interface_dump.vat")
1150 for interface in if_data[0]:
1151 if interface["interface_name"] == interface_name:
1152 return interface["sw_if_index"]
1157 def vxlan_gpe_dump(node, interface_name=None):
1158 """Get VxLAN GPE data for the given interface.
1160 :param node: VPP node to get interface data from.
1161 :param interface_name: Name of the specific interface. If None,
1162 information about all VxLAN GPE interfaces is returned.
1164 :type interface_name: str
1165 :returns: Dictionary containing data for the given VxLAN GPE interface
1166 or if interface=None, the list of dictionaries with all VxLAN GPE
1168 :rtype: dict or list
1171 with VatTerminal(node) as vat:
1172 vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template(
1173 "vxlan_gpe_dump.vat")
1176 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name)
1178 for vxlan_gpe in vxlan_gpe_data[0]:
1179 if vxlan_gpe["sw_if_index"] == sw_if_index:
1183 return vxlan_gpe_data[0]
1186 def vpp_proxy_arp_interface_enable(node, interface):
1187 """Enable proxy ARP on interface.
1189 :param node: VPP node to enable proxy ARP on interface.
1190 :param interface: Interface to enable proxy ARP.
1192 :type interface: str or int
1194 if isinstance(interface, basestring):
1195 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1197 sw_if_index = interface
1199 with VatTerminal(node) as vat:
1200 vat.vat_terminal_exec_cmd_from_template(
1201 "proxy_arp_intfc_enable.vat",
1202 sw_if_index=sw_if_index)
1205 def vpp_ip_source_check_setup(node, interface):
1206 """Setup Reverse Path Forwarding source check on interface.
1208 :param node: Node to setup RPF source check.
1209 :param interface: Interface name to setup RPF source check.
1211 :type interface: str
1213 with VatTerminal(node) as vat:
1214 vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat",
1215 interface_name=interface)
1218 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1219 """Assign VPP interface to specific VRF/FIB table.
1221 :param node: VPP node where the FIB and interface are located.
1222 :param interface: Interface to be assigned to FIB.
1223 :param table_id: VRF table ID.
1224 :param ipv6: Assign to IPv6 table. Default False.
1226 :type interface: str or int
1230 if isinstance(interface, basestring):
1231 sw_if_index = Topology.get_interface_sw_index(node, interface)
1233 sw_if_index = interface
1235 ipv6 = 'ipv6' if ipv6 else ''
1237 with VatTerminal(node) as vat:
1238 ret = vat.vat_terminal_exec_cmd_from_template(
1239 "set_fib_to_interface.vat",
1240 sw_index=sw_if_index, vrf=table_id, ipv6=ipv6)
1242 if ret[0]["retval"] != 0:
1243 raise RuntimeError('Unable to assign interface to FIB node {}.'
1247 def set_linux_interface_mac(node, interface, mac, namespace=None,
1249 """Set MAC address for interface in linux.
1251 :param node: Node where to execute command.
1252 :param interface: Interface in namespace.
1253 :param mac: MAC to be assigned to interface.
1254 :param namespace: Execute command in namespace. Optional
1255 :param vf_id: Virtual Function id. Optional
1257 :type interface: str
1259 :type namespace: str
1262 mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \
1263 if vf_id is not None else 'address {mac}'.format(mac=mac)
1264 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1266 cmd = ('{ns} ip link set {interface} {mac}'.
1267 format(ns=ns_str, interface=interface, mac=mac_str))
1268 exec_cmd_no_error(node, cmd, sudo=True)
1271 def set_linux_interface_trust_on(node, interface, namespace=None,
1273 """Set trust on (promisc) for interface in linux.
1275 :param node: Node where to execute command.
1276 :param interface: Interface in namespace.
1277 :param namespace: Execute command in namespace. Optional
1278 :param vf_id: Virtual Function id. Optional
1280 :type interface: str
1281 :type namespace: str
1284 trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \
1285 if vf_id is not None else 'trust on'
1286 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1288 cmd = ('{ns} ip link set dev {interface} {trust}'.
1289 format(ns=ns_str, interface=interface, trust=trust_str))
1290 exec_cmd_no_error(node, cmd, sudo=True)
1293 def set_linux_interface_spoof_off(node, interface, namespace=None,
1295 """Set spoof off for interface in linux.
1297 :param node: Node where to execute command.
1298 :param interface: Interface in namespace.
1299 :param namespace: Execute command in namespace. Optional
1300 :param vf_id: Virtual Function id. Optional
1302 :type interface: str
1303 :type namespace: str
1306 spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \
1307 if vf_id is not None else 'spoof off'
1308 ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else ''
1310 cmd = ('{ns} ip link set dev {interface} {spoof}'.
1311 format(ns=ns_str, interface=interface, spoof=spoof_str))
1312 exec_cmd_no_error(node, cmd, sudo=True)
1315 def init_avf_interface(node, ifc_key, numvfs=1, topology_type='L2'):
1316 """Init PCI device by creating VFs and bind them to vfio-pci for AVF
1317 driver testing on DUT.
1319 :param node: DUT node.
1320 :param iface_key: Interface key from topology file.
1321 :param numvfs: Number of VFs to initialize, 0 - disable the VFs.
1322 :param topology_type: Topology type.
1326 :typ topology_type: str
1327 :returns: Virtual Function topology interface keys.
1333 # Read PCI address and driver.
1334 pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key)
1335 pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":")
1336 uio_driver = Topology.get_uio_driver(node)
1337 kernel_driver = Topology.get_interface_driver(node, ifc_key)
1338 current_driver = DUTSetup.get_pci_dev_driver(node,\
1339 pf_pci_addr.replace(':', r'\:'))
1341 if current_driver != kernel_driver:
1342 # PCI device must be re-bound to kernel driver before creating VFs.
1343 DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True)
1344 # Stop VPP to prevent deadlock.
1345 VPPUtil.stop_vpp_service(node)
1346 # Unbind from current driver.
1347 DUTSetup.pci_driver_unbind(node, pf_pci_addr)
1348 # Bind to kernel driver.
1349 DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver)
1351 # Initialize PCI VFs
1352 DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs)
1355 # Set MAC address and bind each virtual function to uio driver.
1356 for vf_id in range(numvfs):
1357 vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2],
1358 pf_mac_addr[3], pf_mac_addr[4],
1359 pf_mac_addr[5], "{:02x}".format(vf_id)])
1361 pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\
1362 format(pci=pf_pci_addr)
1363 InterfaceUtil.set_linux_interface_trust_on(node, pf_dev,
1365 if topology_type == 'L2':
1366 InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev,
1368 InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr,
1371 DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id)
1372 DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver)
1374 # Add newly created ports into topology file
1375 vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key)
1376 vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id)
1377 vf_ifc_key = Topology.add_new_port(node, vf_ifc_name)
1378 Topology.update_interface_name(node, vf_ifc_key,
1379 vf_ifc_name+str(vf_id+1))
1380 Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr)
1381 Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr)
1382 vf_ifc_keys.append(vf_ifc_key)