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.parsers.JsonParser import JsonParser
30 class InterfaceUtil(object):
31 """General utilities for managing interfaces"""
33 __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules'
36 def set_interface_state(node, interface, state, if_type="key"):
37 """Set interface state on a node.
39 Function can be used for DUTs as well as for TGs.
41 :param node: Node where the interface is.
42 :param interface: Interface key or sw_if_index or name.
43 :param state: One of 'up' or 'down'.
44 :param if_type: Interface type
46 :type interface: str or int
50 :raises ValueError: If the interface type is unknown.
51 :raises ValueError: If the state of interface is unexpected.
52 :raises ValueError: If the node has an unknown node type.
56 if isinstance(interface, basestring):
57 sw_if_index = Topology.get_interface_sw_index(node, interface)
58 iface_name = Topology.get_interface_name(node, interface)
60 sw_if_index = interface
61 elif if_type == "name":
62 iface_key = Topology.get_interface_by_name(node, interface)
63 if iface_key is not None:
64 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
65 iface_name = interface
67 raise ValueError("if_type unknown: {}".format(if_type))
69 if node['type'] == NodeType.DUT:
75 raise ValueError('Unexpected interface state: {}'.format(state))
76 VatExecutor.cmd_from_template(node, 'set_if_state.vat',
77 sw_if_index=sw_if_index, state=state)
78 elif node['type'] == NodeType.TG or node['type'] == NodeType.VM:
79 cmd = 'ip link set {} {}'.format(iface_name, state)
80 exec_cmd_no_error(node, cmd, sudo=True)
82 raise ValueError('Node {} has unknown NodeType: "{}"'
83 .format(node['host'], node['type']))
86 def set_interface_ethernet_mtu(node, iface_key, mtu):
87 """Set Ethernet MTU for specified interface.
89 Function can be used only for TGs.
91 :param node: Node where the interface is.
92 :param iface_key: Interface key from topology file.
93 :param mtu: MTU to set.
98 :raises ValueError: If the node type is "DUT".
99 :raises ValueError: If the node has an unknown node type.
101 if node['type'] == NodeType.DUT:
102 raise ValueError('Node {}: Setting Ethernet MTU for interface '
103 'on DUT nodes not supported', node['host'])
104 elif node['type'] == NodeType.TG:
105 iface_name = Topology.get_interface_name(node, iface_key)
106 cmd = 'ip link set {} mtu {}'.format(iface_name, mtu)
107 exec_cmd_no_error(node, cmd, sudo=True)
109 raise ValueError('Node {} has unknown NodeType: "{}"'
110 .format(node['host'], node['type']))
113 def set_default_ethernet_mtu_on_all_interfaces_on_node(node):
114 """Set default Ethernet MTU on all interfaces on node.
116 Function can be used only for TGs.
118 :param node: Node where to set default MTU.
122 for ifc in node['interfaces']:
123 InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500)
126 def vpp_node_interfaces_ready_wait(node, timeout=10):
127 """Wait until all interfaces with admin-up are in link-up state.
129 :param node: Node to wait on.
130 :param timeout: Waiting timeout in seconds (optional, default 10s).
134 :raises RuntimeError: If the timeout period value has elapsed.
140 out = InterfaceUtil.vpp_get_interface_data(node)
141 if time() - start > timeout:
142 for interface in out:
143 if interface.get('admin_up_down') == 1:
144 if interface.get('link_up_down') != 1:
145 logger.debug('{0} link-down'.format(
146 interface.get('interface_name')))
147 raise RuntimeError('timeout, not up {0}'.format(not_ready))
149 for interface in out:
150 if interface.get('admin_up_down') == 1:
151 if interface.get('link_up_down') != 1:
152 not_ready.append(interface.get('interface_name'))
156 logger.debug('Interfaces still in link-down state: {0}, '
157 'waiting...'.format(not_ready))
161 def vpp_nodes_interfaces_ready_wait(nodes, timeout=10):
162 """Wait until all interfaces with admin-up are in link-up state for
165 :param nodes: List of nodes to wait on.
166 :param timeout: Seconds to wait per node for all interfaces to come up.
172 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
175 def all_vpp_interfaces_ready_wait(nodes, timeout=10):
176 """Wait until all interfaces with admin-up are in link-up state for all
177 nodes in the topology.
179 :param nodes: Nodes in the topology.
180 :param timeout: Seconds to wait per node for all interfaces to come up.
185 for node in nodes.values():
186 if node['type'] == NodeType.DUT:
187 InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout)
190 def vpp_get_interface_data(node, interface=None):
191 """Get all interface data from a VPP node. If a name or
192 sw_interface_index is provided, return only data for the matching
195 :param node: VPP node to get interface data from.
196 :param interface: Numeric index or name string of a specific interface.
198 :type interface: int or str
199 :returns: List of dictionaries containing data for each interface, or a
200 single dictionary for the specified interface.
202 :raises TypeError: if the data type of interface is neither basestring
205 with VatTerminal(node) as vat:
206 response = vat.vat_terminal_exec_cmd_from_template(
207 "interface_dump.vat")
211 if interface is not None:
212 if isinstance(interface, basestring):
213 param = "interface_name"
214 elif isinstance(interface, int):
215 param = "sw_if_index"
219 if data_if[param] == interface:
225 def vpp_get_interface_name(node, sw_if_index):
226 """Get interface name for the given SW interface index from actual
229 :param node: VPP node to get interface data from.
230 :param sw_if_index: SW interface index of the specific interface.
232 :type sw_if_index: int
233 :returns: Name of the given interface.
237 if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index)
238 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
239 if_data = InterfaceUtil.vpp_get_interface_data(
240 node, if_data['sup_sw_if_index'])
242 if_name = if_data["interface_name"]
248 def vpp_get_interface_mac(node, interface=None):
249 """Get MAC address for the given interface from actual interface dump.
251 :param node: VPP node to get interface data from.
252 :param interface: Numeric index or name string of a specific interface.
254 :type interface: int or str
255 :returns: MAC address.
259 if_data = InterfaceUtil.vpp_get_interface_data(node, interface)
260 if if_data['sup_sw_if_index'] != if_data['sw_if_index']:
261 if_data = InterfaceUtil.vpp_get_interface_data(
262 node, if_data['sup_sw_if_index'])
263 mac_data = [str(hex(item))[2:] for item in if_data['l2_address'][:6]]
265 for item in mac_data:
268 mac_data_nice.append(item)
269 mac = ":".join(mac_data_nice)
273 def vpp_get_interface_ip_addresses(node, interface, ip_version):
274 """Get list of IP addresses from an interface on a VPP node.
276 :param node: VPP node to get data from.
277 :param interface: Name of an interface on the VPP node.
278 :param ip_version: IP protocol version (ipv4 or ipv6).
281 :type ip_version: str
282 :returns: List of dictionaries, each containing IP address, subnet
283 prefix length and also the subnet mask for ipv4 addresses.
284 Note: A single interface may have multiple IP addresses assigned.
289 sw_if_index = Topology.convert_interface_reference(
290 node, interface, "sw_if_index")
292 if isinstance(interface, basestring):
293 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
297 with VatTerminal(node) as vat:
298 response = vat.vat_terminal_exec_cmd_from_template(
299 "ip_address_dump.vat", ip_version=ip_version,
300 sw_if_index=sw_if_index)
304 if ip_version == "ipv4":
306 item["netmask"] = convert_ipv4_netmask_prefix(
307 item["prefix_length"])
311 def tg_set_interface_driver(node, pci_addr, driver):
312 """Set interface driver on the TG node.
314 :param node: Node to set interface driver on (must be TG node).
315 :param pci_addr: PCI address of the interface.
316 :param driver: Driver name.
320 :raises RuntimeError: If unbinding from the current driver fails.
321 :raises RuntimeError: If binding to the new driver fails.
323 old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr)
324 if old_driver == driver:
330 # Unbind from current driver
331 if old_driver is not None:
332 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'\
333 .format(pci_addr, old_driver)
334 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
335 if int(ret_code) != 0:
336 raise RuntimeError("'{0}' failed on '{1}'"
337 .format(cmd, node['host']))
339 # Bind to the new driver
340 cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'\
341 .format(pci_addr, driver)
342 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
343 if int(ret_code) != 0:
344 raise RuntimeError("'{0}' failed on '{1}'"
345 .format(cmd, node['host']))
348 def tg_get_interface_driver(node, pci_addr):
349 """Get interface driver from the TG node.
351 :param node: Node to get interface driver on (must be TG node).
352 :param pci_addr: PCI address of the interface.
355 :returns: Interface driver or None if not found.
357 :raises RuntimeError: If PCI rescan or lspci command execution failed.
359 return DUTSetup.get_pci_dev_driver(node, pci_addr)
362 def tg_set_interfaces_udev_rules(node):
363 """Set udev rules for interfaces.
365 Create udev rules file in /etc/udev/rules.d where are rules for each
366 interface used by TG node, based on MAC interface has specific name.
367 So after unbind and bind again to kernel driver interface has same
368 name as before. This must be called after TG has set name for each
369 port in topology dictionary.
371 SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f",
374 :param node: Node to set udev rules on (must be TG node).
376 :raises RuntimeError: If setting of udev rules fails.
381 cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE)
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 for interface in node['interfaces'].values():
388 rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \
389 '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \
390 interface['name'] + '\\"'
391 cmd = 'sh -c "echo \'{0}\' >> {1}"'.format(
392 rule, InterfaceUtil.__UDEV_IF_RULES_FILE)
393 (ret_code, _, _) = ssh.exec_command_sudo(cmd)
394 if int(ret_code) != 0:
395 raise RuntimeError("'{0}' failed on '{1}'"
396 .format(cmd, node['host']))
398 cmd = '/etc/init.d/udev restart'
399 ssh.exec_command_sudo(cmd)
402 def tg_set_interfaces_default_driver(node):
403 """Set interfaces default driver specified in topology yaml file.
405 :param node: Node to setup interfaces driver on (must be TG node).
408 for interface in node['interfaces'].values():
409 InterfaceUtil.tg_set_interface_driver(node,
410 interface['pci_address'],
414 def update_vpp_interface_data_on_node(node):
415 """Update vpp generated interface data for a given node in DICT__nodes.
417 Updates interface names, software if index numbers and any other details
418 generated specifically by vpp that are unknown before testcase run.
419 It does this by dumping interface list to JSON output from all
420 devices using vpp_api_test, and pairing known information from topology
421 (mac address/pci address of interface) to state from VPP.
423 :param node: Node selected from DICT__nodes.
426 vat_executor = VatExecutor()
427 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
428 interface_dump_json = vat_executor.get_script_stdout()
429 VatJsonUtil.update_vpp_interface_data_from_json(node,
433 def update_nic_interface_names(node):
434 """Update interface names based on nic type and PCI address.
436 This method updates interface names in the same format as VPP does.
438 :param node: Node dictionary.
441 for ifc in node['interfaces'].values():
442 if_pci = ifc['pci_address'].replace('.', ':').split(':')
443 bus = '{:x}'.format(int(if_pci[1], 16))
444 dev = '{:x}'.format(int(if_pci[2], 16))
445 fun = '{:x}'.format(int(if_pci[3], 16))
446 loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun)
447 if ifc['model'] == 'Intel-XL710':
448 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
449 elif ifc['model'] == 'Intel-X710':
450 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
451 elif ifc['model'] == 'Intel-X520-DA2':
452 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
453 elif ifc['model'] == 'Cisco-VIC-1385':
454 ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc)
455 elif ifc['model'] == 'Cisco-VIC-1227':
456 ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc)
458 ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc)
461 def update_nic_interface_names_on_all_duts(nodes):
462 """Update interface names based on nic type and PCI address on all DUTs.
464 This method updates interface names in the same format as VPP does.
466 :param nodes: Topology nodes.
469 for node in nodes.values():
470 if node['type'] == NodeType.DUT:
471 InterfaceUtil.update_nic_interface_names(node)
474 def update_tg_interface_data_on_node(node):
475 """Update interface name for TG/linux node in DICT__nodes.
478 # for dev in `ls /sys/class/net/`;
479 > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done
480 "52:54:00:9f:82:63": "eth0"
481 "52:54:00:77:ae:a9": "eth1"
482 "52:54:00:e1:8a:0f": "eth2"
483 "00:00:00:00:00:00": "lo"
485 .. note:: TODO: parse lshw -json instead
487 :param node: Node selected from DICT__nodes.
489 :raises RuntimeError: If getting of interface name and MAC fails.
491 # First setup interface driver specified in yaml file
492 InterfaceUtil.tg_set_interfaces_default_driver(node)
494 # Get interface names
498 cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat '
499 '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;')
501 (ret_code, stdout, _) = ssh.exec_command(cmd)
502 if int(ret_code) != 0:
503 raise RuntimeError('Get interface name and MAC failed')
504 tmp = "{" + stdout.rstrip().replace('\n', ',') + "}"
505 interfaces = JsonParser().parse_data(tmp)
506 for interface in node['interfaces'].values():
507 name = interfaces.get(interface['mac_address'])
510 interface['name'] = name
512 # Set udev rules for interfaces
513 InterfaceUtil.tg_set_interfaces_udev_rules(node)
516 def iface_update_numa_node(node):
517 """For all interfaces from topology file update numa node based on
518 information from the node.
520 :param node: Node from topology.
523 :raises ValueError: If numa node ia less than 0.
524 :raises RuntimeError: If update of numa node failes.
527 for if_key in Topology.get_node_interfaces(node):
528 if_pci = Topology.get_interface_pci_addr(node, if_key)
530 cmd = "cat /sys/bus/pci/devices/{}/numa_node".format(if_pci)
532 (ret, out, _) = ssh.exec_command(cmd)
539 logger.trace('Reading numa location failed for: {0}'\
542 Topology.set_interface_numa_node(node, if_key,
546 raise RuntimeError('Update numa node failed for: {0}'\
550 def update_all_numa_nodes(nodes, skip_tg=False):
551 """For all nodes and all their interfaces from topology file update numa
552 node information based on information from the node.
554 :param nodes: Nodes in the topology.
555 :param skip_tg: Skip TG node
560 for node in nodes.values():
561 if node['type'] == NodeType.DUT:
562 InterfaceUtil.iface_update_numa_node(node)
563 elif node['type'] == NodeType.TG and not skip_tg:
564 InterfaceUtil.iface_update_numa_node(node)
567 def update_all_interface_data_on_all_nodes(nodes, skip_tg=False,
569 """Update interface names on all nodes in DICT__nodes.
571 This method updates the topology dictionary by querying interface lists
572 of all nodes mentioned in the topology dictionary.
574 :param nodes: Nodes in the topology.
575 :param skip_tg: Skip TG node
576 :param numa_node: Retrieve numa_node location.
579 :type numa_node: bool
581 for node_data in nodes.values():
582 if node_data['type'] == NodeType.DUT:
583 InterfaceUtil.update_vpp_interface_data_on_node(node_data)
584 elif node_data['type'] == NodeType.TG and not skip_tg:
585 InterfaceUtil.update_tg_interface_data_on_node(node_data)
588 if node_data['type'] == NodeType.DUT:
589 InterfaceUtil.iface_update_numa_node(node_data)
590 elif node_data['type'] == NodeType.TG and not skip_tg:
591 InterfaceUtil.iface_update_numa_node(node_data)
594 def create_vlan_subinterface(node, interface, vlan):
595 """Create VLAN subinterface on node.
597 :param node: Node to add VLAN subinterface on.
598 :param interface: Interface name on which create VLAN subinterface.
599 :param vlan: VLAN ID of the subinterface to be created.
603 :returns: Name and index of created subinterface.
605 :raises RuntimeError: if it is unable to create VLAN subinterface on the
608 iface_key = Topology.get_interface_by_name(node, interface)
609 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
611 output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat",
612 sw_if_index=sw_if_index,
614 if output[0]["retval"] == 0:
615 sw_subif_idx = output[0]["sw_if_index"]
616 logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} '
617 'created on node {}'.format(sw_subif_idx,
619 if_key = Topology.add_new_port(node, "vlan_subif")
620 Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
621 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
622 Topology.update_interface_name(node, if_key, ifc_name)
624 raise RuntimeError('Unable to create VLAN subinterface on node {}'
625 .format(node['host']))
627 with VatTerminal(node, False) as vat:
628 vat.vat_terminal_exec_cmd('exec show interfaces')
630 return '{}.{}'.format(interface, vlan), sw_subif_idx
633 def create_vxlan_interface(node, vni, source_ip, destination_ip):
634 """Create VXLAN interface and return sw if index of created interface.
636 Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT
639 :param node: Node where to create VXLAN interface.
640 :param vni: VXLAN Network Identifier.
641 :param source_ip: Source IP of a VXLAN Tunnel End Point.
642 :param destination_ip: Destination IP of a VXLAN Tunnel End Point.
646 :type destination_ip: str
647 :returns: SW IF INDEX of created interface.
649 :raises RuntimeError: if it is unable to create VxLAN interface on the
652 output = VatExecutor.cmd_from_template(node, "vxlan_create.vat",
658 if output["retval"] == 0:
659 sw_if_idx = output["sw_if_index"]
660 if_key = Topology.add_new_port(node, "vxlan_tunnel")
661 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
662 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
663 Topology.update_interface_name(node, if_key, ifc_name)
666 raise RuntimeError("Unable to create VXLAN interface on node {0}"
670 def vxlan_dump(node, interface=None):
671 """Get VxLAN data for the given interface.
673 :param node: VPP node to get interface data from.
674 :param interface: Numeric index or name string of a specific interface.
675 If None, information about all VxLAN interfaces is returned.
677 :type interface: int or str
678 :returns: Dictionary containing data for the given VxLAN interface or if
679 interface=None, the list of dictionaries with all VxLAN interfaces.
681 :raises TypeError: if the data type of interface is neither basestring
684 param = "sw_if_index"
685 if interface is None:
688 elif isinstance(interface, basestring):
689 sw_if_index = Topology.get_interface_sw_index(node, interface)
690 elif isinstance(interface, int):
691 sw_if_index = interface
693 raise TypeError("Wrong interface format {0}".format(interface))
695 with VatTerminal(node) as vat:
696 response = vat.vat_terminal_exec_cmd_from_template(
697 "vxlan_dump.vat", param=param, sw_if_index=sw_if_index)
700 for vxlan in response[0]:
701 if vxlan["sw_if_index"] == sw_if_index:
707 def vhost_user_dump(node):
708 """Get vhost-user data for the given node.
710 :param node: VPP node to get interface data from.
712 :returns: List of dictionaries with all vhost-user interfaces.
715 with VatTerminal(node) as vat:
716 response = vat.vat_terminal_exec_cmd_from_template(
717 "vhost_user_dump.vat")
722 def tap_dump(node, name=None):
723 """Get all TAP interface data from the given node, or data about
724 a specific TAP interface.
726 :param node: VPP node to get data from.
727 :param name: Optional name of a specific TAP interface.
730 :returns: Dictionary of information about a specific TAP interface, or
731 a List of dictionaries containing all TAP data for the given node.
734 with VatTerminal(node) as vat:
735 response = vat.vat_terminal_exec_cmd_from_template(
740 for item in response[0]:
741 if name == item['dev_name']:
746 def create_subinterface(node, interface, sub_id, outer_vlan_id=None,
747 inner_vlan_id=None, type_subif=None):
748 """Create sub-interface on node. It is possible to set required
749 sub-interface type and VLAN tag(s).
751 :param node: Node to add sub-interface.
752 :param interface: Interface name on which create sub-interface.
753 :param sub_id: ID of the sub-interface to be created.
754 :param outer_vlan_id: Optional outer VLAN ID.
755 :param inner_vlan_id: Optional inner VLAN ID.
756 :param type_subif: Optional type of sub-interface. Values supported by
757 VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match]
760 :type interface: str or int
762 :type outer_vlan_id: int
763 :type inner_vlan_id: int
764 :type type_subif: str
765 :returns: Name and index of created sub-interface.
767 :raises RuntimeError: If it is not possible to create sub-interface.
770 outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\
771 if outer_vlan_id else ''
773 inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\
774 if inner_vlan_id else ''
776 if type_subif is None:
779 if isinstance(interface, basestring):
780 iface_key = Topology.get_interface_by_name(node, interface)
781 sw_if_index = Topology.get_interface_sw_index(node, iface_key)
783 sw_if_index = interface
785 output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat",
786 sw_if_index=sw_if_index,
788 outer_vlan_id=outer_vlan_id,
789 inner_vlan_id=inner_vlan_id,
790 type_subif=type_subif)
792 if output[0]["retval"] == 0:
793 sw_subif_idx = output[0]["sw_if_index"]
794 logger.trace('Created subinterface with index {}'
795 .format(sw_subif_idx))
796 if_key = Topology.add_new_port(node, "subinterface")
797 Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx)
798 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx)
799 Topology.update_interface_name(node, if_key, ifc_name)
801 raise RuntimeError('Unable to create sub-interface on node {}'
802 .format(node['host']))
804 with VatTerminal(node, json_param=False) as vat:
805 vat.vat_terminal_exec_cmd('exec show interfaces')
807 name = '{}.{}'.format(interface, sub_id)
808 return name, sw_subif_idx
811 def create_gre_tunnel_interface(node, source_ip, destination_ip):
812 """Create GRE tunnel interface on node.
814 :param node: VPP node to add tunnel interface.
815 :param source_ip: Source of the GRE tunnel.
816 :param destination_ip: Destination of the GRE tunnel.
819 :type destination_ip: str
820 :returns: Name and index of created GRE tunnel interface.
822 :raises RuntimeError: If unable to create GRE tunnel interface.
824 output = VatExecutor.cmd_from_template(node, "create_gre.vat",
829 if output["retval"] == 0:
830 sw_if_idx = output["sw_if_index"]
832 vat_executor = VatExecutor()
833 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
834 interface_dump_json = vat_executor.get_script_stdout()
835 name = VatJsonUtil.get_interface_name_from_json(
836 interface_dump_json, sw_if_idx)
838 if_key = Topology.add_new_port(node, "gre_tunnel")
839 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
840 Topology.update_interface_name(node, if_key, name)
842 return name, sw_if_idx
844 raise RuntimeError('Unable to create GRE tunnel on node {}.'
848 def vpp_create_loopback(node):
849 """Create loopback interface on VPP node.
851 :param node: Node to create loopback interface on.
853 :returns: SW interface index.
855 :raises RuntimeError: If it is not possible to create loopback on the
858 out = VatExecutor.cmd_from_template(node, "create_loopback.vat")
859 if out[0].get('retval') == 0:
860 sw_if_idx = out[0].get('sw_if_index')
861 if_key = Topology.add_new_port(node, "loopback")
862 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
863 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
864 Topology.update_interface_name(node, if_key, ifc_name)
867 raise RuntimeError('Create loopback failed on node "{}"'
868 .format(node['host']))
871 def vpp_create_bond_interface(node, mode, load_balance=None, mac=None):
872 """Create bond interface on VPP node.
874 :param node: DUT node from topology.
875 :param mode: Link bonding mode.
876 :param load_balance: Load balance (optional, valid for xor and lacp
877 modes, otherwise ignored).
878 :param mac: MAC address to assign to the bond interface (optional).
881 :type load_balance: str
883 :returns: Interface key (name) in topology.
885 :raises RuntimeError: If it is not possible to create bond interface on
888 hw_addr = '' if mac is None else 'hw-addr {mac}'.format(mac=mac)
889 lb = '' if load_balance is None \
890 else 'lb {lb}'.format(lb=load_balance)
892 output = VatExecutor.cmd_from_template(
893 node, 'create_bond_interface.vat', mode=mode, lb=lb, mac=hw_addr)
895 if output[0].get('retval') == 0:
896 sw_if_idx = output[0].get('sw_if_index')
897 InterfaceUtil.add_bond_eth_interface(node, sw_if_idx=sw_if_idx)
898 if_key = Topology.get_interface_by_sw_index(node, sw_if_idx)
901 raise RuntimeError('Create bond interface failed on node "{n}"'
902 .format(n=node['host']))
905 def add_bond_eth_interface(node, ifc_name=None, sw_if_idx=None):
906 """Add BondEthernet interface to current topology.
908 :param node: DUT node from topology.
909 :param ifc_name: Name of the BondEthernet interface.
910 :param sw_if_idx: SW interface index.
915 if_key = Topology.add_new_port(node, 'eth_bond')
917 vat_executor = VatExecutor()
918 vat_executor.execute_script_json_out("dump_interfaces.vat", node)
919 interface_dump_json = vat_executor.get_script_stdout()
921 if ifc_name and sw_if_idx is None:
922 sw_if_idx = VatJsonUtil.get_interface_sw_index_from_json(
923 interface_dump_json, ifc_name)
924 Topology.update_interface_sw_if_index(node, if_key, sw_if_idx)
925 if sw_if_idx and ifc_name is None:
926 ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx)
927 Topology.update_interface_name(node, if_key, ifc_name)
928 ifc_mac = VatJsonUtil.get_interface_mac_from_json(
929 interface_dump_json, sw_if_idx)
930 Topology.update_interface_mac_address(node, if_key, ifc_mac)
933 def vpp_enslave_physical_interface(node, interface, bond_interface):
934 """Enslave physical interface to bond interface on VPP node.
936 :param node: DUT node from topology.
937 :param interface: Physical interface key from topology file.
938 :param bond_interface: Load balance
941 :type bond_interface: str
942 :raises RuntimeError: If it is not possible to enslave physical
943 interface to bond interface on the node.
945 ifc = Topology.get_interface_sw_index(node, interface)
946 bond_ifc = Topology.get_interface_sw_index(node, bond_interface)
948 output = VatExecutor.cmd_from_template(
949 node, 'enslave_physical_interface.vat', p_int=ifc, b_int=bond_ifc)
951 retval = output[0].get('retval', None)
952 if retval is None or int(retval) != 0:
953 raise RuntimeError('Enslave physical interface {ifc} to bond '
954 'interface {bond} failed on node "{n}"'
955 .format(ifc=interface, bond=bond_interface,
959 def vpp_show_bond_data_on_node(node, details=False):
960 """Show (detailed) bond information on VPP node.
962 :param node: DUT node from topology.
963 :param details: If detailed information is required or not.
967 cmd = 'exec show bond details' if details else 'exec show bond'
968 with VatTerminal(node, json_param=False) as vat:
969 vat.vat_terminal_exec_cmd(cmd)
972 def vpp_show_bond_data_on_all_nodes(nodes, details=False):
973 """Show (detailed) bond information on all VPP nodes in DICT__nodes.
975 :param nodes: Nodes in the topology.
976 :param details: If detailed information is required or not.
980 for node_data in nodes.values():
981 if node_data['type'] == NodeType.DUT:
982 InterfaceUtil.vpp_show_bond_data_on_node(node_data, details)
985 def vpp_enable_input_acl_interface(node, interface, ip_version,
987 """Enable input acl on interface.
989 :param node: VPP node to setup interface for input acl.
990 :param interface: Interface to setup input acl.
991 :param ip_version: Version of IP protocol.
992 :param table_index: Classify table index.
994 :type interface: str or int
995 :type ip_version: str
996 :type table_index: int
998 if isinstance(interface, basestring):
999 sw_if_index = Topology.get_interface_sw_index(node, interface)
1001 sw_if_index = interface
1003 with VatTerminal(node) as vat:
1004 vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat",
1005 sw_if_index=sw_if_index,
1006 ip_version=ip_version,
1007 table_index=table_index)
1010 def get_interface_classify_table(node, interface):
1011 """Get name of classify table for the given interface.
1013 :param node: VPP node to get data from.
1014 :param interface: Name or sw_if_index of a specific interface.
1016 :type interface: str or int
1017 :returns: Classify table name.
1020 if isinstance(interface, basestring):
1021 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1023 sw_if_index = interface
1025 with VatTerminal(node) as vat:
1026 data = vat.vat_terminal_exec_cmd_from_template(
1027 "classify_interface_table.vat",
1028 sw_if_index=sw_if_index)
1032 def get_interface_vrf_table(node, interface):
1033 """Get vrf ID for the given interface.
1035 :param node: VPP node.
1036 :param interface: Name or sw_if_index of a specific interface.
1038 :type interface: str or int
1039 :returns: vrf ID of the specified interface.
1043 if isinstance(interface, basestring):
1044 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1046 sw_if_index = interface
1048 with VatTerminal(node) as vat:
1049 data = vat.vat_terminal_exec_cmd_from_template(
1050 "interface_vrf_dump.vat",
1051 sw_if_index=sw_if_index)
1052 return data[0]["vrf_id"]
1055 def get_sw_if_index(node, interface_name):
1056 """Get sw_if_index for the given interface from actual interface dump.
1058 :param node: VPP node to get interface data from.
1059 :param interface_name: Name of the specific interface.
1061 :type interface_name: str
1062 :returns: sw_if_index of the given interface.
1066 with VatTerminal(node) as vat:
1067 if_data = vat.vat_terminal_exec_cmd_from_template(
1068 "interface_dump.vat")
1069 for interface in if_data[0]:
1070 if interface["interface_name"] == interface_name:
1071 return interface["sw_if_index"]
1076 def vxlan_gpe_dump(node, interface_name=None):
1077 """Get VxLAN GPE data for the given interface.
1079 :param node: VPP node to get interface data from.
1080 :param interface_name: Name of the specific interface. If None,
1081 information about all VxLAN GPE interfaces is returned.
1083 :type interface_name: str
1084 :returns: Dictionary containing data for the given VxLAN GPE interface
1085 or if interface=None, the list of dictionaries with all VxLAN GPE
1087 :rtype: dict or list
1090 with VatTerminal(node) as vat:
1091 vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template(
1092 "vxlan_gpe_dump.vat")
1095 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name)
1097 for vxlan_gpe in vxlan_gpe_data[0]:
1098 if vxlan_gpe["sw_if_index"] == sw_if_index:
1102 return vxlan_gpe_data[0]
1105 def vpp_proxy_arp_interface_enable(node, interface):
1106 """Enable proxy ARP on interface.
1108 :param node: VPP node to enable proxy ARP on interface.
1109 :param interface: Interface to enable proxy ARP.
1111 :type interface: str or int
1113 if isinstance(interface, basestring):
1114 sw_if_index = InterfaceUtil.get_sw_if_index(node, interface)
1116 sw_if_index = interface
1118 with VatTerminal(node) as vat:
1119 vat.vat_terminal_exec_cmd_from_template(
1120 "proxy_arp_intfc_enable.vat",
1121 sw_if_index=sw_if_index)
1124 def vpp_ip_source_check_setup(node, interface):
1125 """Setup Reverse Path Forwarding source check on interface.
1127 :param node: Node to setup RPF source check.
1128 :param interface: Interface name to setup RPF source check.
1130 :type interface: str
1132 with VatTerminal(node) as vat:
1133 vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat",
1134 interface_name=interface)
1137 def assign_interface_to_fib_table(node, interface, table_id, ipv6=False):
1138 """Assign VPP interface to specific VRF/FIB table.
1140 :param node: VPP node where the FIB and interface are located.
1141 :param interface: Interface to be assigned to FIB.
1142 :param table_id: VRF table ID.
1143 :param ipv6: Assign to IPv6 table. Default False.
1145 :type interface: str or int
1149 if isinstance(interface, basestring):
1150 sw_if_index = Topology.get_interface_sw_index(node, interface)
1152 sw_if_index = interface
1154 ipv6 = 'ipv6' if ipv6 else ''
1156 with VatTerminal(node) as vat:
1157 ret = vat.vat_terminal_exec_cmd_from_template(
1158 "set_fib_to_interface.vat",
1159 sw_index=sw_if_index, vrf=table_id, ipv6=ipv6)
1161 if ret[0]["retval"] != 0:
1162 raise RuntimeError('Unable to assign interface to FIB node {}.'
1166 def set_linux_interface_mac(node, interface, mac, namespace=None):
1167 """Set MAC address for interface in linux.
1169 :param node: Node where to execute command.
1170 :param interface: Interface in namespace.
1171 :param mac: MAC to be assigned to interface.
1172 :param namespace: Execute command in namespace. Optional
1174 :type interface: str
1176 :type namespace: str
1178 if namespace is not None:
1179 cmd = 'ip netns exec {} ip link set {} address {}'.format(
1180 namespace, interface, mac)
1182 cmd = 'ip link set {} address {}'.format(interface, mac)
1183 exec_cmd_no_error(node, cmd, sudo=True)