X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FInterfaceUtil.py;h=7144e0adefa2179ce3632e33b5161dfbeb648372;hp=df45e39560ca033e53977d9ef886a50c079b8e35;hb=82094363f6077e1b28845719db3a6191c0c93a99;hpb=5e6145a4260ffce1c302e94b9b241851f90838e1 diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index df45e39560..7144e0adef 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -13,30 +13,87 @@ """Interface util library.""" -from time import time, sleep +from socket import AF_INET, AF_INET6, inet_ntop +from time import sleep +from enum import IntEnum +from ipaddress import ip_address from robot.api import logger +from resources.libraries.python.Constants import Constants from resources.libraries.python.CpuUtils import CpuUtils from resources.libraries.python.DUTSetup import DUTSetup +from resources.libraries.python.L2Util import L2Util from resources.libraries.python.PapiExecutor import PapiExecutor -from resources.libraries.python.IPUtil import convert_ipv4_netmask_prefix -from resources.libraries.python.IPUtil import IPUtil from resources.libraries.python.parsers.JsonParser import JsonParser from resources.libraries.python.ssh import SSH, exec_cmd_no_error from resources.libraries.python.topology import NodeType, Topology -from resources.libraries.python.VatExecutor import VatExecutor, VatTerminal -from resources.libraries.python.VatJsonUtil import VatJsonUtil from resources.libraries.python.VPPUtil import VPPUtil +class LinkBondLoadBalance(IntEnum): + """Link bonding load balance.""" + L2 = 0 + L34 = 1 + L23 = 2 + + +class LinkBondMode(IntEnum): + """Link bonding load balance.""" + ROUND_ROBIN = 1 + ACTIVE_BACKUP = 2 + XOR = 3 + BROADCAST = 4 + LACP = 5 + + class InterfaceUtil(object): """General utilities for managing interfaces""" __UDEV_IF_RULES_FILE = '/etc/udev/rules.d/10-network.rules' @staticmethod - def set_interface_state(node, interface, state, if_type="key"): + def pci_to_int(pci_str): + """Convert PCI address from string format (0000:18:0a.0) to + integer representation (169345024). + + :param pci_str: PCI address in string representation. + :type pci_str: str + :returns: Integer representation of PCI address. + :rtype: int + """ + pci = list(pci_str.split(':')[0:2]) + pci.extend(pci_str.split(':')[2].split('.')) + + return (int(pci[0], 16) | int(pci[1], 16) << 16 | + int(pci[2], 16) << 24 | int(pci[3], 16) << 29) + + @staticmethod + def get_interface_index(node, interface): + """Get interface sw_if_index from topology file. + + :param node: Node where the interface is. + :param interface: Numeric index or name string of a specific interface. + :type node: dict + :type interface: str or int + :returns: SW interface index. + :rtype: int + """ + try: + sw_if_index = int(interface) + except ValueError: + sw_if_index = Topology.get_interface_sw_index(node, interface) + if sw_if_index is None: + sw_if_index = \ + Topology.get_interface_sw_index_by_name(node, interface) + except TypeError as err: + raise TypeError('Wrong interface format {ifc}: {err}'.format( + ifc=interface, err=err.message)) + + return sw_if_index + + @staticmethod + def set_interface_state(node, interface, state, if_type='key'): """Set interface state on a node. Function can be used for DUTs as well as for TGs. @@ -54,32 +111,39 @@ class InterfaceUtil(object): :raises ValueError: If the state of interface is unexpected. :raises ValueError: If the node has an unknown node type. """ - - if if_type == "key": + if if_type == 'key': if isinstance(interface, basestring): sw_if_index = Topology.get_interface_sw_index(node, interface) iface_name = Topology.get_interface_name(node, interface) else: sw_if_index = interface - elif if_type == "name": + elif if_type == 'name': iface_key = Topology.get_interface_by_name(node, interface) if iface_key is not None: sw_if_index = Topology.get_interface_sw_index(node, iface_key) iface_name = interface else: - raise ValueError("if_type unknown: {}".format(if_type)) + raise ValueError('Unknown if_type: {type}'.format(type=if_type)) if node['type'] == NodeType.DUT: if state == 'up': - state = 'admin-up link-up' + admin_up_down = 1 elif state == 'down': - state = 'admin-down link-down' + admin_up_down = 0 else: - raise ValueError('Unexpected interface state: {}'.format(state)) - VatExecutor.cmd_from_template(node, 'set_if_state.vat', - sw_if_index=sw_if_index, state=state) + raise ValueError('Unexpected interface state: {state}'.format( + state=state)) + cmd = 'sw_interface_set_flags' + err_msg = 'Failed to set interface state on host {host}'.format( + host=node['host']) + args = dict(sw_if_index=sw_if_index, + admin_up_down=admin_up_down) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) elif node['type'] == NodeType.TG or node['type'] == NodeType.VM: - cmd = 'ip link set {} {}'.format(iface_name, state) + cmd = 'ip link set {ifc} {state}'.format( + ifc=iface_name, state=state) exec_cmd_no_error(node, cmd, sudo=True) else: raise ValueError('Node {} has unknown NodeType: "{}"' @@ -141,11 +205,19 @@ class InterfaceUtil(object): else: sw_if_index = interface - if sw_if_index: - with VatTerminal(node, json_param=False) as vat: - vat.vat_terminal_exec_cmd_from_template( - "hw_interface_set_mtu.vat", sw_if_index=sw_if_index, - mtu=mtu) + cmd = 'hw_interface_set_mtu' + err_msg = 'Failed to set interface MTU on host {host}'.format( + host=node['host']) + args = dict(sw_if_index=sw_if_index, + mtu=int(mtu)) + try: + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) + except AssertionError as err: + # TODO: Make failure tolerance optional. + logger.debug("Setting MTU failed. Expected?\n{err}".format( + err=err)) @staticmethod def vpp_set_interfaces_mtu_on_node(node, mtu=9200): @@ -173,74 +245,57 @@ class InterfaceUtil(object): InterfaceUtil.vpp_set_interfaces_mtu_on_node(node, mtu) @staticmethod - def vpp_node_interfaces_ready_wait(node, timeout=30): + def vpp_node_interfaces_ready_wait(node, retries=15): """Wait until all interfaces with admin-up are in link-up state. :param node: Node to wait on. - :param timeout: Waiting timeout in seconds (optional, default 10s). + :param retries: Number of retries to check interface status (optional, + default 15). :type node: dict - :type timeout: int + :type retries: int :returns: Nothing. - :raises RuntimeError: If the timeout period value has elapsed. + :raises RuntimeError: If any interface is not in link-up state after + defined number of retries. """ - if_ready = False - not_ready = [] - start = time() - while not if_ready: + for _ in xrange(0, retries): + not_ready = list() out = InterfaceUtil.vpp_get_interface_data(node) - if time() - start > timeout: - for interface in out: - if interface.get('admin_up_down') == 1: - if interface.get('link_up_down') != 1: - logger.debug('{0} link-down'.format( - interface.get('interface_name'))) - raise RuntimeError('timeout, not up {0}'.format(not_ready)) - not_ready = [] for interface in out: if interface.get('admin_up_down') == 1: if interface.get('link_up_down') != 1: not_ready.append(interface.get('interface_name')) if not not_ready: - if_ready = True + break else: - logger.debug('Interfaces still in link-down state: {0}, ' - 'waiting...'.format(not_ready)) + logger.debug('Interfaces still in link-down state:\n{ifs} ' + '\nWaiting...'.format(ifs=not_ready)) sleep(1) + else: + err = 'Timeout, interfaces not up:\n{ifs}'.format(ifs=not_ready) \ + if 'not_ready' in locals() else 'No check executed!' + raise RuntimeError(err) @staticmethod - def vpp_nodes_interfaces_ready_wait(nodes, timeout=30): - """Wait until all interfaces with admin-up are in link-up state for - listed nodes. - - :param nodes: List of nodes to wait on. - :param timeout: Seconds to wait per node for all interfaces to come up. - :type nodes: list - :type timeout: int - :returns: Nothing. - """ - for node in nodes: - InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout) - - @staticmethod - def all_vpp_interfaces_ready_wait(nodes, timeout=30): + def all_vpp_interfaces_ready_wait(nodes, retries=15): """Wait until all interfaces with admin-up are in link-up state for all nodes in the topology. :param nodes: Nodes in the topology. - :param timeout: Seconds to wait per node for all interfaces to come up. + :param retries: Number of retries to check interface status (optional, + default 15). :type nodes: dict - :type timeout: int + :type retries: int :returns: Nothing. """ for node in nodes.values(): if node['type'] == NodeType.DUT: - InterfaceUtil.vpp_node_interfaces_ready_wait(node, timeout) + InterfaceUtil.vpp_node_interfaces_ready_wait(node, retries) @staticmethod def vpp_get_interface_data(node, interface=None): """Get all interface data from a VPP node. If a name or sw_interface_index is provided, return only data for the matching - interface. + interface(s). :param node: VPP node to get interface data from. :param interface: Numeric index or name string of a specific interface. @@ -252,23 +307,53 @@ class InterfaceUtil(object): :raises TypeError: if the data type of interface is neither basestring nor int. """ - with VatTerminal(node) as vat: - response = vat.vat_terminal_exec_cmd_from_template( - "interface_dump.vat") - - data = response[0] - if interface is not None: if isinstance(interface, basestring): - param = "interface_name" + param = 'interface_name' elif isinstance(interface, int): - param = "sw_if_index" + param = 'sw_if_index' else: - raise TypeError - for data_if in data: - if data_if[param] == interface: - return data_if - return dict() + raise TypeError('Wrong interface format {ifc}'.format( + ifc=interface)) + else: + param = '' + + cmd = 'sw_interface_dump' + cmd_reply = 'sw_interface_details' + args = dict(name_filter_valid=0, + name_filter='') + err_msg = 'Failed to get interface dump on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_dump(err_msg) + + papi_if_dump = papi_resp.reply[0]['api_reply'] + + def process_if_dump(if_dump): + """Process interface dump. + + :param if_dump: Interface dump. + :type if_dump: dict + :returns: Processed interface dump. + :rtype: dict + """ + if_dump['interface_name'] = if_dump['interface_name'].rstrip('\x00') + if_dump['tag'] = if_dump['tag'].rstrip('\x00') + if_dump['l2_address'] = L2Util.bin_to_mac(if_dump['l2_address']) + if_dump['b_dmac'] = L2Util.bin_to_mac(if_dump['b_dmac']) + if_dump['b_smac'] = L2Util.bin_to_mac(if_dump['b_smac']) + return if_dump + + data = list() if interface is None else dict() + for item in papi_if_dump: + if interface is None: + data.append(process_if_dump(item[cmd_reply])) + elif str(item[cmd_reply].get(param)).rstrip('\x00') == \ + str(interface): + data = process_if_dump(item[cmd_reply]) + break + + logger.debug('Interface data:\n{if_data}'.format(if_data=data)) return data @staticmethod @@ -283,19 +368,31 @@ class InterfaceUtil(object): :returns: Name of the given interface. :rtype: str """ - if_data = InterfaceUtil.vpp_get_interface_data(node, sw_if_index) if if_data['sup_sw_if_index'] != if_data['sw_if_index']: if_data = InterfaceUtil.vpp_get_interface_data( node, if_data['sup_sw_if_index']) - try: - if_name = if_data["interface_name"] - except KeyError: - if_name = None - return if_name + + return if_data.get('interface_name') @staticmethod - def vpp_get_interface_mac(node, interface=None): + def vpp_get_interface_sw_index(node, interface_name): + """Get interface name for the given SW interface index from actual + interface dump. + + :param node: VPP node to get interface data from. + :param interface_name: Interface name. + :type node: dict + :type interface_name: str + :returns: Name of the given interface. + :rtype: str + """ + if_data = InterfaceUtil.vpp_get_interface_data(node, interface_name) + + return if_data.get('sw_if_index') + + @staticmethod + def vpp_get_interface_mac(node, interface): """Get MAC address for the given interface from actual interface dump. :param node: VPP node to get interface data from. @@ -305,57 +402,12 @@ class InterfaceUtil(object): :returns: MAC address. :rtype: str """ - if_data = InterfaceUtil.vpp_get_interface_data(node, interface) if if_data['sup_sw_if_index'] != if_data['sw_if_index']: if_data = InterfaceUtil.vpp_get_interface_data( node, if_data['sup_sw_if_index']) - mac_data = [str(hex(item))[2:] for item in if_data['l2_address'][:6]] - mac_data_nice = [] - for item in mac_data: - if len(item) == 1: - item = '0' + item - mac_data_nice.append(item) - mac = ":".join(mac_data_nice) - return mac - - @staticmethod - def vpp_get_interface_ip_addresses(node, interface, ip_version): - """Get list of IP addresses from an interface on a VPP node. - - :param node: VPP node to get data from. - :param interface: Name of an interface on the VPP node. - :param ip_version: IP protocol version (ipv4 or ipv6). - :type node: dict - :type interface: str - :type ip_version: str - :returns: List of dictionaries, each containing IP address, subnet - prefix length and also the subnet mask for ipv4 addresses. - Note: A single interface may have multiple IP addresses assigned. - :rtype: list - """ - - try: - sw_if_index = Topology.convert_interface_reference( - node, interface, "sw_if_index") - except RuntimeError: - if isinstance(interface, basestring): - sw_if_index = InterfaceUtil.get_sw_if_index(node, interface) - else: - raise - with VatTerminal(node) as vat: - response = vat.vat_terminal_exec_cmd_from_template( - "ip_address_dump.vat", ip_version=ip_version, - sw_if_index=sw_if_index) - - data = response[0] - - if ip_version == "ipv4": - for item in data: - item["netmask"] = convert_ipv4_netmask_prefix( - item["prefix_length"]) - return data + return if_data.get('l2_address') @staticmethod def tg_set_interface_driver(node, pci_addr, driver): @@ -466,18 +518,30 @@ class InterfaceUtil(object): Updates interface names, software if index numbers and any other details generated specifically by vpp that are unknown before testcase run. - It does this by dumping interface list to JSON output from all - devices using vpp_api_test, and pairing known information from topology - (mac address/pci address of interface) to state from VPP. + It does this by dumping interface list from all devices using python + api, and pairing known information from topology (mac address) to state + from VPP. :param node: Node selected from DICT__nodes. :type node: dict """ - vat_executor = VatExecutor() - vat_executor.execute_script_json_out("dump_interfaces.vat", node) - interface_dump_json = vat_executor.get_script_stdout() - VatJsonUtil.update_vpp_interface_data_from_json(node, - interface_dump_json) + interface_list = InterfaceUtil.vpp_get_interface_data(node) + interface_dict = dict() + for ifc in interface_list: + interface_dict[ifc['l2_address']] = ifc + + for if_name, if_data in node['interfaces'].items(): + ifc_dict = interface_dict.get(if_data['mac_address']) + if ifc_dict is not None: + if_data['name'] = ifc_dict['interface_name'] + if_data['vpp_sw_index'] = ifc_dict['sw_if_index'] + if_data['mtu'] = ifc_dict['mtu'][0] + logger.trace('Interface {ifc} found by MAC {mac}'.format( + ifc=if_name, mac=if_data['mac_address'])) + else: + logger.trace('Interface {ifc} not found by MAC {mac}'.format( + ifc=if_name, mac=if_data['mac_address'])) + if_data['vpp_sw_index'] = None @staticmethod def update_nic_interface_names(node): @@ -650,7 +714,7 @@ class InterfaceUtil(object): @staticmethod def create_vlan_subinterface(node, interface, vlan): - """Create VLAN subinterface on node. + """Create VLAN sub-interface on node. :param node: Node to add VLAN subinterface on. :param interface: Interface name on which create VLAN subinterface. @@ -666,34 +730,27 @@ class InterfaceUtil(object): iface_key = Topology.get_interface_by_name(node, interface) sw_if_index = Topology.get_interface_sw_index(node, iface_key) - output = VatExecutor.cmd_from_template(node, "create_vlan_subif.vat", - sw_if_index=sw_if_index, - vlan=vlan) - if output[0]["retval"] == 0: - sw_vlan_idx = output[0]["sw_if_index"] - logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} ' - 'created on node {}'.format(sw_vlan_idx, - vlan, node['host'])) - if_key = Topology.add_new_port(node, "vlan_subif") - Topology.update_interface_sw_if_index(node, if_key, sw_vlan_idx) - ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_vlan_idx) - Topology.update_interface_name(node, if_key, ifc_name) - else: - raise RuntimeError('Unable to create VLAN subinterface on node {}' - .format(node['host'])) + cmd = 'create_vlan_subif' + args = dict(sw_if_index=sw_if_index, + vlan_id=int(vlan)) + err_msg = 'Failed to create VLAN sub-interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) - with VatTerminal(node, False) as vat: - vat.vat_terminal_exec_cmd('exec show interfaces') + sw_if_idx = papi_resp['sw_if_index'] + if_key = Topology.add_new_port(node, 'vlan_subif') + Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) + ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) + Topology.update_interface_name(node, if_key, ifc_name) - return '{}.{}'.format(interface, vlan), sw_vlan_idx + return '{ifc}.{vlan}'.format(ifc=interface, vlan=vlan), sw_if_idx @staticmethod def create_vxlan_interface(node, vni, source_ip, destination_ip): """Create VXLAN interface and return sw if index of created interface. - Executes "vxlan_add_del_tunnel src {src} dst {dst} vni {vni}" VAT - command on the node. - :param node: Node where to create VXLAN interface. :param vni: VXLAN Network Identifier. :param source_ip: Source IP of a VXLAN Tunnel End Point. @@ -707,22 +764,32 @@ class InterfaceUtil(object): :raises RuntimeError: if it is unable to create VxLAN interface on the node. """ - output = VatExecutor.cmd_from_template(node, "vxlan_create.vat", - src=source_ip, - dst=destination_ip, - vni=vni) - output = output[0] - - if output["retval"] == 0: - sw_if_idx = output["sw_if_index"] - if_key = Topology.add_new_port(node, "vxlan_tunnel") - Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) - ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) - Topology.update_interface_name(node, if_key, ifc_name) - return sw_if_idx - else: - raise RuntimeError("Unable to create VXLAN interface on node {0}" - .format(node)) + src_address = ip_address(unicode(source_ip)) + dst_address = ip_address(unicode(destination_ip)) + + cmd = 'vxlan_add_del_tunnel' + args = dict(is_add=1, + is_ipv6=1 if src_address.version == 6 else 0, + instance=Constants.BITWISE_NON_ZERO, + src_address=src_address.packed, + dst_address=dst_address.packed, + mcast_sw_if_index=Constants.BITWISE_NON_ZERO, + encap_vrf_id=0, + decap_next_index=Constants.BITWISE_NON_ZERO, + vni=int(vni)) + err_msg = 'Failed to create VXLAN tunnel interface on host {host}'.\ + format(host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) + + sw_if_idx = papi_resp['sw_if_index'] + if_key = Topology.add_new_port(node, 'vxlan_tunnel') + Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) + ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) + Topology.update_interface_name(node, if_key, ifc_name) + + return sw_if_idx @staticmethod def vxlan_dump(node, interface=None): @@ -739,48 +806,100 @@ class InterfaceUtil(object): :raises TypeError: if the data type of interface is neither basestring nor int. """ - param = "sw_if_index" - if interface is None: - param = '' - sw_if_index = '' - elif isinstance(interface, basestring): - sw_if_index = Topology.get_interface_sw_index(node, interface) - elif isinstance(interface, int): - sw_if_index = interface + if interface is not None: + sw_if_index = InterfaceUtil.get_interface_index(node, interface) else: - raise TypeError("Wrong interface format {0}".format(interface)) + sw_if_index = int(Constants.BITWISE_NON_ZERO) - with VatTerminal(node) as vat: - response = vat.vat_terminal_exec_cmd_from_template( - "vxlan_dump.vat", param=param, sw_if_index=sw_if_index) + cmd = 'vxlan_tunnel_dump' + cmd_reply = 'vxlan_tunnel_details' + args = dict(sw_if_index=sw_if_index) + err_msg = 'Failed to get VXLAN dump on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_dump(err_msg) + + papi_vxlan_dump = papi_resp.reply[0]['api_reply'] + + def process_vxlan_dump(vxlan_dump): + """Process vxlan dump. + + :param vxlan_dump: Vxlan interface dump. + :type vxlan_dump: dict + :returns: Processed vxlan interface dump. + :rtype: dict + """ + if vxlan_dump['is_ipv6']: + vxlan_dump['src_address'] = \ + inet_ntop(AF_INET6, vxlan_dump['src_address']) + vxlan_dump['dst_address'] = \ + inet_ntop(AF_INET6, vxlan_dump['dst_address']) + else: + vxlan_dump['src_address'] = \ + inet_ntop(AF_INET, vxlan_dump['src_address'][0:4]) + vxlan_dump['dst_address'] = \ + inet_ntop(AF_INET, vxlan_dump['dst_address'][0:4]) + return vxlan_dump + + data = list() if interface is None else dict() + for item in papi_vxlan_dump: + if interface is None: + data.append(process_vxlan_dump(item[cmd_reply])) + elif item[cmd_reply]['sw_if_index'] == sw_if_index: + data = process_vxlan_dump(item[cmd_reply]) + break - if sw_if_index: - for vxlan in response[0]: - if vxlan["sw_if_index"] == sw_if_index: - return vxlan - return {} - return response[0] + logger.debug('VXLAN data:\n{vxlan_data}'.format(vxlan_data=data)) + return data @staticmethod def vhost_user_dump(node): """Get vhost-user data for the given node. + TODO: Move to VhostUser.py + :param node: VPP node to get interface data from. :type node: dict :returns: List of dictionaries with all vhost-user interfaces. :rtype: list """ - with VatTerminal(node) as vat: - response = vat.vat_terminal_exec_cmd_from_template( - "vhost_user_dump.vat") + cmd = 'sw_interface_vhost_user_dump' + cmd_reply = 'sw_interface_vhost_user_details' + err_msg = 'Failed to get vhost-user dump on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd).get_dump(err_msg) + + papi_vxlan_dump = papi_resp.reply[0]['api_reply'] + + def process_vhost_dump(vhost_dump): + """Process vhost dump. - return response[0] + :param vhost_dump: Vhost interface dump. + :type vhost_dump: dict + :returns: Processed vhost interface dump. + :rtype: dict + """ + vhost_dump['interface_name'] = \ + vhost_dump['interface_name'].rstrip('\x00') + vhost_dump['sock_filename'] = \ + vhost_dump['sock_filename'].rstrip('\x00') + return vhost_dump + + data = list() + for item in papi_vxlan_dump: + data.append(process_vhost_dump(item[cmd_reply])) + + logger.debug('Vhost-user data:\n{vhost_data}'.format(vhost_data=data)) + return data @staticmethod def tap_dump(node, name=None): """Get all TAP interface data from the given node, or data about a specific TAP interface. + TODO: Move to Tap.py + :param node: VPP node to get data from. :param name: Optional name of a specific TAP interface. :type node: dict @@ -789,15 +908,45 @@ class InterfaceUtil(object): a List of dictionaries containing all TAP data for the given node. :rtype: dict or list """ - with VatTerminal(node) as vat: - response = vat.vat_terminal_exec_cmd_from_template( - "tap_dump.vat") - if name is None: - return response[0] - for item in response[0]: - if name == item['dev_name']: - return item - return {} + cmd = 'sw_interface_tap_v2_dump' + cmd_reply = 'sw_interface_tap_v2_details' + err_msg = 'Failed to get TAP dump on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd).get_dump(err_msg) + + papi_tap_dump = papi_resp.reply[0]['api_reply'] + + def process_tap_dump(tap_dump): + """Process tap dump. + + :param tap_dump: Tap interface dump. + :type tap_dump: dict + :returns: Processed tap interface dump. + :rtype: dict + """ + tap_dump['dev_name'] = tap_dump['dev_name'].rstrip('\x00') + tap_dump['host_if_name'] = tap_dump['host_if_name'].rstrip('\x00') + tap_dump['host_namespace'] = \ + tap_dump['host_namespace'].rstrip('\x00') + tap_dump['host_mac_addr'] = \ + L2Util.bin_to_mac(tap_dump['host_mac_addr']) + tap_dump['host_ip4_addr'] = \ + inet_ntop(AF_INET, tap_dump['host_ip4_addr']) + tap_dump['host_ip6_addr'] = \ + inet_ntop(AF_INET6, tap_dump['host_ip6_addr']) + return tap_dump + + data = list() if name is None else dict() + for item in papi_tap_dump: + if name is None: + data.append(process_tap_dump(item[cmd_reply])) + elif item[cmd_reply].get('dev_name').rstrip('\x00') == name: + data = process_tap_dump(item[cmd_reply]) + break + + logger.debug('TAP data:\n{tap_data}'.format(tap_data=data)) + return data @staticmethod def create_subinterface(node, interface, sub_id, outer_vlan_id=None, @@ -823,46 +972,35 @@ class InterfaceUtil(object): :rtype: tuple :raises RuntimeError: If it is not possible to create sub-interface. """ + subif_types = type_subif.split() + + cmd = 'create_subif' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index(node, interface), + sub_id=int(sub_id), + no_tags=1 if 'no_tags' in subif_types else 0, + one_tag=1 if 'one_tag' in subif_types else 0, + two_tags=1 if 'two_tags' in subif_types else 0, + dot1ad=1 if 'dot1ad' in subif_types else 0, + exact_match=1 if 'exact_match' in subif_types else 0, + default_sub=1 if 'default_sub' in subif_types else 0, + outer_vlan_id_any=1 if type_subif == 'default_sub' else 0, + inner_vlan_id_any=1 if type_subif == 'default_sub' else 0, + outer_vlan_id=int(outer_vlan_id) if outer_vlan_id else 0, + inner_vlan_id=int(inner_vlan_id) if inner_vlan_id else 0) + err_msg = 'Failed to create sub-interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) - outer_vlan_id = 'outer_vlan_id {0}'.format(outer_vlan_id)\ - if outer_vlan_id else '' - - inner_vlan_id = 'inner_vlan_id {0}'.format(inner_vlan_id)\ - if inner_vlan_id else '' - - if type_subif is None: - type_subif = '' - - if isinstance(interface, basestring): - iface_key = Topology.get_interface_by_name(node, interface) - sw_if_index = Topology.get_interface_sw_index(node, iface_key) - else: - sw_if_index = interface - - output = VatExecutor.cmd_from_template(node, "create_sub_interface.vat", - sw_if_index=sw_if_index, - sub_id=sub_id, - outer_vlan_id=outer_vlan_id, - inner_vlan_id=inner_vlan_id, - type_subif=type_subif) - - if output[0]["retval"] == 0: - sw_vlan_idx = output[0]["sw_if_index"] - logger.trace('Created subinterface with index {}' - .format(sw_vlan_idx)) - if_key = Topology.add_new_port(node, "subinterface") - Topology.update_interface_sw_if_index(node, if_key, sw_vlan_idx) - ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_vlan_idx) - Topology.update_interface_name(node, if_key, ifc_name) - else: - raise RuntimeError('Unable to create sub-interface on node {}' - .format(node['host'])) - - with VatTerminal(node, json_param=False) as vat: - vat.vat_terminal_exec_cmd('exec show interfaces') + sw_subif_idx = papi_resp['sw_if_index'] + if_key = Topology.add_new_port(node, 'subinterface') + Topology.update_interface_sw_if_index(node, if_key, sw_subif_idx) + ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_subif_idx) + Topology.update_interface_name(node, if_key, ifc_name) - name = '{}.{}'.format(interface, sub_id) - return name, sw_vlan_idx + return '{ifc}.{s_id}'.format(ifc=interface, s_id=sub_id), sw_subif_idx @staticmethod def create_gre_tunnel_interface(node, source_ip, destination_ip): @@ -878,28 +1016,28 @@ class InterfaceUtil(object): :rtype: tuple :raises RuntimeError: If unable to create GRE tunnel interface. """ - output = VatExecutor.cmd_from_template(node, "create_gre.vat", - src=source_ip, - dst=destination_ip) - output = output[0] - - if output["retval"] == 0: - sw_if_idx = output["sw_if_index"] - - vat_executor = VatExecutor() - vat_executor.execute_script_json_out("dump_interfaces.vat", node) - interface_dump_json = vat_executor.get_script_stdout() - name = VatJsonUtil.get_interface_name_from_json( - interface_dump_json, sw_if_idx) + cmd = 'gre_tunnel_add_del' + tunnel = dict(type=0, + instance=Constants.BITWISE_NON_ZERO, + src=str(source_ip), + dst=str(destination_ip), + outer_fib_id=0, + session_id=0) + args = dict(is_add=1, + tunnel=tunnel) + err_msg = 'Failed to create GRE tunnel interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) - if_key = Topology.add_new_port(node, "gre_tunnel") - Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) - Topology.update_interface_name(node, if_key, name) + sw_if_idx = papi_resp['sw_if_index'] + if_key = Topology.add_new_port(node, 'gre_tunnel') + Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) + ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) + Topology.update_interface_name(node, if_key, ifc_name) - return name, sw_if_idx - else: - raise RuntimeError('Unable to create GRE tunnel on node {}.' - .format(node)) + return ifc_name, sw_if_idx @staticmethod def vpp_create_loopback(node): @@ -912,17 +1050,21 @@ class InterfaceUtil(object): :raises RuntimeError: If it is not possible to create loopback on the node. """ - out = VatExecutor.cmd_from_template(node, "create_loopback.vat") - if out[0].get('retval') == 0: - sw_if_idx = out[0].get('sw_if_index') - if_key = Topology.add_new_port(node, "loopback") - Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) - ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) - Topology.update_interface_name(node, if_key, ifc_name) - return sw_if_idx - else: - raise RuntimeError('Create loopback failed on node "{}"' - .format(node['host'])) + cmd = 'create_loopback' + args = dict(mac_address=0) + err_msg = 'Failed to create loopback interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) + + sw_if_idx = papi_resp['sw_if_index'] + if_key = Topology.add_new_port(node, 'loopback') + Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) + ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) + Topology.update_interface_name(node, if_key, ifc_name) + + return sw_if_idx @staticmethod def vpp_create_bond_interface(node, mode, load_balance=None, mac=None): @@ -942,22 +1084,27 @@ class InterfaceUtil(object): :raises RuntimeError: If it is not possible to create bond interface on the node. """ - hw_addr = '' if mac is None else 'hw-addr {mac}'.format(mac=mac) - ldb = '' if load_balance is None \ - else 'lb {ldb}'.format(ldb=load_balance) - - output = VatExecutor.cmd_from_template( - node, 'create_bond_interface.vat', mode=mode, lb=ldb, mac=hw_addr) - - if output[0].get('retval') == 0: - sw_if_idx = output[0].get('sw_if_index') - InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx, - ifc_pfx='eth_bond') - if_key = Topology.get_interface_by_sw_index(node, sw_if_idx) - return if_key - else: - raise RuntimeError('Create bond interface failed on "{host}"'. - format(host=node['host'])) + cmd = 'bond_create' + args = dict(id=int(Constants.BITWISE_NON_ZERO), + use_custom_mac=0 if mac is None else 1, + mac_address=0 if mac is None else L2Util.mac_to_bin(mac), + mode=getattr(LinkBondMode, '{md}'.format( + md=mode.replace('-', '_').upper())).value, + lb=0 if load_balance is None else getattr( + LinkBondLoadBalance, '{lb}'.format( + lb=load_balance.upper())).value) + err_msg = 'Failed to create bond interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) + + sw_if_idx = papi_resp['sw_if_index'] + InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx, + ifc_pfx='eth_bond') + if_key = Topology.get_interface_by_sw_index(node, sw_if_idx) + + return if_key @staticmethod def add_eth_interface(node, ifc_name=None, sw_if_idx=None, ifc_pfx=None): @@ -974,19 +1121,13 @@ class InterfaceUtil(object): """ if_key = Topology.add_new_port(node, ifc_pfx) - vat_executor = VatExecutor() - vat_executor.execute_script_json_out("dump_interfaces.vat", node) - interface_dump_json = vat_executor.get_script_stdout() - if ifc_name and sw_if_idx is None: - sw_if_idx = VatJsonUtil.get_interface_sw_index_from_json( - interface_dump_json, ifc_name) + sw_if_idx = InterfaceUtil.vpp_get_interface_sw_index(node, ifc_name) Topology.update_interface_sw_if_index(node, if_key, sw_if_idx) if sw_if_idx and ifc_name is None: ifc_name = InterfaceUtil.vpp_get_interface_name(node, sw_if_idx) Topology.update_interface_name(node, if_key, ifc_name) - ifc_mac = VatJsonUtil.get_interface_mac_from_json( - interface_dump_json, sw_if_idx) + ifc_mac = InterfaceUtil.vpp_get_interface_mac(node, sw_if_idx) Topology.update_interface_mac_address(node, if_key, ifc_mac) @staticmethod @@ -1004,50 +1145,51 @@ class InterfaceUtil(object): :raises RuntimeError: If it is not possible to create AVF interface on the node. """ - num_rx_queues = 'num-rx-queues {num_rx_queues}'\ - .format(num_rx_queues=num_rx_queues) if num_rx_queues else '' - - with VatTerminal(node, json_param=False) as vat: - vat.vat_terminal_exec_cmd_from_template('create_avf_interface.vat', - vf_pci_addr=vf_pci_addr, - num_rx_queues=num_rx_queues) - output = vat.vat_stdout - - if output is not None: - sw_if_idx = int(output.split()[4]) - InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx, - ifc_pfx='eth_avf') - if_key = Topology.get_interface_by_sw_index(node, sw_if_idx) - return if_key - else: - raise RuntimeError('Create AVF interface failed on {host}'. - format(host=node['host'])) + cmd = 'avf_create' + args = dict(pci_addr=InterfaceUtil.pci_to_int(vf_pci_addr), + enable_elog=0, + rxq_num=int(num_rx_queues) if num_rx_queues else 0, + rxq_size=0, + txq_size=0) + err_msg = 'Failed to create AVF interface on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) + + sw_if_idx = papi_resp['sw_if_index'] + InterfaceUtil.add_eth_interface(node, sw_if_idx=sw_if_idx, + ifc_pfx='eth_avf') + if_key = Topology.get_interface_by_sw_index(node, sw_if_idx) + + return if_key @staticmethod - def vpp_enslave_physical_interface(node, interface, bond_interface): + def vpp_enslave_physical_interface(node, interface, bond_if): """Enslave physical interface to bond interface on VPP node. :param node: DUT node from topology. :param interface: Physical interface key from topology file. - :param bond_interface: Load balance + :param bond_if: Load balance :type node: dict :type interface: str - :type bond_interface: str + :type bond_if: str :raises RuntimeError: If it is not possible to enslave physical interface to bond interface on the node. """ - ifc = Topology.get_interface_sw_index(node, interface) - bond_ifc = Topology.get_interface_sw_index(node, bond_interface) - - output = VatExecutor.cmd_from_template( - node, 'enslave_physical_interface.vat', p_int=ifc, b_int=bond_ifc) - - retval = output[0].get('retval', None) - if retval is None or int(retval) != 0: - raise RuntimeError('Enslave physical interface {ifc} to bond ' - 'interface {bond} failed on node "{n}"' - .format(ifc=interface, bond=bond_interface, - n=node['host'])) + cmd = 'bond_enslave' + args = dict( + sw_if_index=Topology.get_interface_sw_index(node, interface), + bond_sw_if_index=Topology.get_interface_sw_index(node, bond_if), + is_passive=0, + is_long_timeout=0) + err_msg = 'Failed to enslave physical interface {ifc} to bond ' \ + 'interface {bond} on host {host}'.format(ifc=interface, + bond=bond_if, + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) @staticmethod def vpp_show_bond_data_on_node(node, details=False): @@ -1058,9 +1200,86 @@ class InterfaceUtil(object): :type node: dict :type details: bool """ - cmd = 'exec show bond details' if details else 'exec show bond' - with VatTerminal(node, json_param=False) as vat: - vat.vat_terminal_exec_cmd(cmd) + cmd = 'sw_interface_bond_dump' + cmd_reply = 'sw_interface_bond_details' + err_msg = 'Failed to get bond interface dump on host {host}'.format( + host=node['host']) + + data = ('Bond data on node {host}:\n'.format(host=node['host'])) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd).get_dump(err_msg) + + papi_dump = papi_resp.reply[0]['api_reply'] + for item in papi_dump: + data += ('{b}\n'.format(b=item[cmd_reply]['interface_name']. + rstrip('\x00'))) + data += (' mode: {m}\n'. + format(m=LinkBondMode(item[cmd_reply]['mode']).name. + lower())) + data += (' load balance: {lb}\n'. + format(lb=LinkBondLoadBalance(item[cmd_reply]['lb']).name. + lower())) + data += (' number of active slaves: {n}\n'. + format(n=item[cmd_reply]['active_slaves'])) + if details: + slave_data = InterfaceUtil.vpp_bond_slave_dump( + node, Topology.get_interface_by_sw_index( + node, item[cmd_reply]['sw_if_index'])) + for slave in slave_data: + if not slave['is_passive']: + data += (' {s}\n'.format(s=slave['interface_name'])) + data += (' number of slaves: {n}\n'. + format(n=item[cmd_reply]['slaves'])) + if details: + for slave in slave_data: + data += (' {s}\n'.format(s=slave['interface_name'])) + data += (' interface id: {i}\n'. + format(i=item[cmd_reply]['id'])) + data += (' sw_if_index: {i}\n'. + format(i=item[cmd_reply]['sw_if_index'])) + logger.info(data) + + @staticmethod + def vpp_bond_slave_dump(node, interface): + """Get bond interface slave(s) data on VPP node. + + :param node: DUT node from topology. + :param interface: Physical interface key from topology file. + :type node: dict + :type interface: str + :returns: Bond slave interface data. + :rtype: dict + """ + cmd = 'sw_interface_slave_dump' + cmd_reply = 'sw_interface_slave_details' + args = dict(sw_if_index=Topology.get_interface_sw_index( + node, interface)) + err_msg = 'Failed to get slave dump on host {host}'.format( + host=node['host']) + + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_dump(err_msg) + + papi_dump = papi_resp.reply[0]['api_reply'] + + def process_slave_dump(slave_dump): + """Process slave dump. + + :param slave_dump: Slave interface dump. + :type slave_dump: dict + :returns: Processed slave interface dump. + :rtype: dict + """ + slave_dump['interface_name'] = slave_dump['interface_name'].\ + rstrip('\x00') + return slave_dump + + data = list() + for item in papi_dump: + data.append(process_slave_dump(item[cmd_reply])) + + logger.debug('Slave data:\n{slave_data}'.format(slave_data=data)) + return data @staticmethod def vpp_show_bond_data_on_all_nodes(nodes, details=False): @@ -1089,21 +1308,28 @@ class InterfaceUtil(object): :type ip_version: str :type table_index: int """ - if isinstance(interface, basestring): - sw_if_index = Topology.get_interface_sw_index(node, interface) - else: - sw_if_index = interface - - with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template("input_acl_int.vat", - sw_if_index=sw_if_index, - ip_version=ip_version, - table_index=table_index) + cmd = 'input_acl_set_interface' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index(node, interface), + ip4_table_index=table_index if ip_version == 'ip4' + else Constants.BITWISE_NON_ZERO, + ip6_table_index=table_index if ip_version == 'ip6' + else Constants.BITWISE_NON_ZERO, + l2_table_index=table_index if ip_version == 'l2' + else Constants.BITWISE_NON_ZERO, + is_add=1) + err_msg = 'Failed to enable input acl on interface {ifc}'.format( + ifc=interface) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) @staticmethod def get_interface_classify_table(node, interface): """Get name of classify table for the given interface. + TODO: Move to Classify.py. + :param node: VPP node to get data from. :param interface: Name or sw_if_index of a specific interface. :type node: dict @@ -1116,34 +1342,15 @@ class InterfaceUtil(object): else: sw_if_index = interface - with VatTerminal(node) as vat: - data = vat.vat_terminal_exec_cmd_from_template( - "classify_interface_table.vat", - sw_if_index=sw_if_index) - return data[0] - - @staticmethod - def get_interface_vrf_table(node, interface): - """Get vrf ID for the given interface. - - :param node: VPP node. - :param interface: Name or sw_if_index of a specific interface. - :type node: dict - :type interface: str or int - :returns: vrf ID of the specified interface. - :rtype: int - """ - - if isinstance(interface, basestring): - sw_if_index = InterfaceUtil.get_sw_if_index(node, interface) - else: - sw_if_index = interface + cmd = 'classify_table_by_interface' + args = dict(sw_if_index=sw_if_index) + err_msg = 'Failed to get classify table name by interface {ifc}'.format( + ifc=interface) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_replies(err_msg). \ + verify_reply(err_msg=err_msg) - with VatTerminal(node) as vat: - data = vat.vat_terminal_exec_cmd_from_template( - "interface_vrf_dump.vat", - sw_if_index=sw_if_index) - return data[0]["vrf_id"] + return papi_resp @staticmethod def get_sw_if_index(node, interface_name): @@ -1156,15 +1363,9 @@ class InterfaceUtil(object): :returns: sw_if_index of the given interface. :rtype: str """ - - with VatTerminal(node) as vat: - if_data = vat.vat_terminal_exec_cmd_from_template( - "interface_dump.vat") - for interface in if_data[0]: - if interface["interface_name"] == interface_name: - return interface["sw_if_index"] - - return None + interface_data = InterfaceUtil.vpp_get_interface_data( + node, interface=interface_name) + return interface_data.get('sw_if_index') @staticmethod def vxlan_gpe_dump(node, interface_name=None): @@ -1180,52 +1381,53 @@ class InterfaceUtil(object): interfaces. :rtype: dict or list """ - - with VatTerminal(node) as vat: - vxlan_gpe_data = vat.vat_terminal_exec_cmd_from_template( - "vxlan_gpe_dump.vat") - - if interface_name: - sw_if_index = InterfaceUtil.get_sw_if_index(node, interface_name) - if sw_if_index: - for vxlan_gpe in vxlan_gpe_data[0]: - if vxlan_gpe["sw_if_index"] == sw_if_index: - return vxlan_gpe - return {} - - return vxlan_gpe_data[0] - - @staticmethod - def vpp_proxy_arp_interface_enable(node, interface): - """Enable proxy ARP on interface. - - :param node: VPP node to enable proxy ARP on interface. - :param interface: Interface to enable proxy ARP. - :type node: dict - :type interface: str or int - """ - if isinstance(interface, basestring): - sw_if_index = InterfaceUtil.get_sw_if_index(node, interface) + if interface_name is not None: + sw_if_index = InterfaceUtil.get_interface_index( + node, interface_name) else: - sw_if_index = interface + sw_if_index = int(Constants.BITWISE_NON_ZERO) - with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template( - "proxy_arp_intfc_enable.vat", - sw_if_index=sw_if_index) - - @staticmethod - def vpp_ip_source_check_setup(node, interface): - """Setup Reverse Path Forwarding source check on interface. + cmd = 'vxlan_gpe_tunnel_dump' + cmd_reply = 'vxlan_gpe_tunnel_details' + args = dict(sw_if_index=sw_if_index) + err_msg = 'Failed to get VXLAN-GPE dump on host {host}'.format( + host=node['host']) + with PapiExecutor(node) as papi_exec: + papi_resp = papi_exec.add(cmd, **args).get_dump(err_msg) + + papi_vxlan_dump = papi_resp.reply[0]['api_reply'] + + def process_vxlan_gpe_dump(vxlan_dump): + """Process vxlan_gpe dump. + + :param vxlan_dump: Vxlan_gpe nterface dump. + :type vxlan_dump: dict + :returns: Processed vxlan_gpe interface dump. + :rtype: dict + """ + if vxlan_dump['is_ipv6']: + vxlan_dump['local'] = \ + inet_ntop(AF_INET6, vxlan_dump['local']) + vxlan_dump['remote'] = \ + inet_ntop(AF_INET6, vxlan_dump['remote']) + else: + vxlan_dump['local'] = \ + inet_ntop(AF_INET, vxlan_dump['local'][0:4]) + vxlan_dump['remote'] = \ + inet_ntop(AF_INET, vxlan_dump['remote'][0:4]) + return vxlan_dump + + data = list() if interface_name is None else dict() + for item in papi_vxlan_dump: + if interface_name is None: + data.append(process_vxlan_gpe_dump(item[cmd_reply])) + elif item[cmd_reply]['sw_if_index'] == sw_if_index: + data = process_vxlan_gpe_dump(item[cmd_reply]) + break - :param node: Node to setup RPF source check. - :param interface: Interface name to setup RPF source check. - :type node: dict - :type interface: str - """ - with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat", - interface_name=interface) + logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format( + vxlan_gpe_data=data)) + return data @staticmethod def assign_interface_to_fib_table(node, interface, table_id, ipv6=False): @@ -1240,21 +1442,16 @@ class InterfaceUtil(object): :type table_id: int :type ipv6: bool """ - if isinstance(interface, basestring): - sw_if_index = Topology.get_interface_sw_index(node, interface) - else: - sw_if_index = interface - - ipv6 = 'ipv6' if ipv6 else '' - - with VatTerminal(node) as vat: - ret = vat.vat_terminal_exec_cmd_from_template( - "set_fib_to_interface.vat", - sw_index=sw_if_index, vrf=table_id, ipv6=ipv6) - - if ret[0]["retval"] != 0: - raise RuntimeError('Unable to assign interface to FIB node {}.' - .format(node)) + cmd = 'sw_interface_set_table' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index(node, interface), + is_ipv6=1 if ipv6 else 0, + vrf_id=int(table_id)) + err_msg = 'Failed to assign interface {ifc} to FIB table'.format( + ifc=interface) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg). \ + verify_reply(err_msg=err_msg) @staticmethod def set_linux_interface_mac(node, interface, mac, namespace=None, @@ -1325,31 +1522,32 @@ class InterfaceUtil(object): exec_cmd_no_error(node, cmd, sudo=True) @staticmethod - def init_avf_interface(node, ifc_key, numvfs=1, traffic_type='L2'): + def init_avf_interface(node, ifc_key, numvfs=1, osi_layer='L2'): """Init PCI device by creating VFs and bind them to vfio-pci for AVF driver testing on DUT. :param node: DUT node. :param ifc_key: Interface key from topology file. :param numvfs: Number of VFs to initialize, 0 - disable the VFs. - :param traffic_type: Expected type of traffic, affects spoofing. + :param osi_layer: OSI Layer type to initialize TG with. Default value "L2" sets linux interface spoof off. - Other values do not do that. :type node: dict :type ifc_key: str :type numvfs: int - :type traffic_type: str + :type osi_layer: str :returns: Virtual Function topology interface keys. :rtype: list + :raises RuntimeError: If a reason preventing initialization is found. """ - ssh = SSH() - ssh.connect(node) - # Read PCI address and driver. pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key) pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":") uio_driver = Topology.get_uio_driver(node) kernel_driver = Topology.get_interface_driver(node, ifc_key) + if kernel_driver != "i40e": + raise RuntimeError( + "AVF needs i40e driver, not {driver} at node {host} ifc {ifc}"\ + .format(driver=kernel_driver, host=node["host"], ifc=ifc_key)) current_driver = DUTSetup.get_pci_dev_driver( node, pf_pci_addr.replace(':', r'\:')) @@ -1377,7 +1575,7 @@ class InterfaceUtil(object): format(pci=pf_pci_addr) InterfaceUtil.set_linux_interface_trust_on(node, pf_dev, vf_id=vf_id) - if traffic_type == 'L2': + if osi_layer == 'L2': InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev, vf_id=vf_id) InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr, @@ -1398,238 +1596,6 @@ class InterfaceUtil(object): return vf_ifc_keys - @staticmethod - def vpp_create_multiple_vxlan_ipv4_tunnels( - node, node_vxlan_if, node_vlan_if, op_node, op_node_if, - n_tunnels, vni_start, src_ip_start, dst_ip_start, ip_step, ip_limit, - bd_id_start): - """Create multiple VXLAN tunnel interfaces and VLAN sub-interfaces on - VPP node. - - Put each pair of VXLAN tunnel interface and VLAN sub-interface to - separate bridge-domain. - - :param node: VPP node to create VXLAN tunnel interfaces. - :param node_vxlan_if: VPP node interface key to create VXLAN tunnel - interfaces. - :param node_vlan_if: VPP node interface key to create VLAN - sub-interface. - :param op_node: Opposite VPP node for VXLAN tunnel interfaces. - :param op_node_if: Opposite VPP node interface key for VXLAN tunnel - interfaces. - :param n_tunnels: Number of tunnel interfaces to create. - :param vni_start: VNI start ID. - :param src_ip_start: VXLAN tunnel source IP address start. - :param dst_ip_start: VXLAN tunnel destination IP address start. - :param ip_step: IP address incremental step. - :param ip_limit: IP address limit. - :param bd_id_start: Bridge-domain ID start. - :type node: dict - :type node_vxlan_if: str - :type node_vlan_if: str - :type op_node: dict - :type op_node_if: str - :type n_tunnels: int - :type vni_start: int - :type src_ip_start: str - :type dst_ip_start: str - :type ip_step: int - :type ip_limit: str - :type bd_id_start: int - """ - # configure IPs, create VXLAN interfaces and VLAN sub-interfaces - vxlan_count = InterfaceUtil.vpp_create_vxlan_and_vlan_interfaces( - node, node_vxlan_if, node_vlan_if, n_tunnels, vni_start, - src_ip_start, dst_ip_start, ip_step, ip_limit) - - # update topology with VXLAN interfaces and VLAN sub-interfaces data - # and put interfaces up - InterfaceUtil.vpp_put_vxlan_and_vlan_interfaces_up( - node, vxlan_count, node_vlan_if) - - # configure bridge domains, ARPs and routes - InterfaceUtil.vpp_put_vxlan_and_vlan_interfaces_to_bridge_domain( - node, node_vxlan_if, vxlan_count, op_node, op_node_if, dst_ip_start, - ip_step, bd_id_start) - - @staticmethod - def vpp_create_vxlan_and_vlan_interfaces( - node, node_vxlan_if, node_vlan_if, vxlan_count, vni_start, - src_ip_start, dst_ip_start, ip_step, ip_limit): - """ - Configure IPs, create VXLAN interfaces and VLAN sub-interfaces on VPP - node. - - :param node: VPP node. - :param node_vxlan_if: VPP node interface key to create VXLAN tunnel - interfaces. - :param node_vlan_if: VPP node interface key to create VLAN - sub-interface. - :param vxlan_count: Number of tunnel interfaces to create. - :param vni_start: VNI start ID. - :param src_ip_start: VXLAN tunnel source IP address start. - :param dst_ip_start: VXLAN tunnel destination IP address start. - :param ip_step: IP address incremental step. - :param ip_limit: IP address limit. - :type node: dict - :type node_vxlan_if: str - :type node_vlan_if: str - :type vxlan_count: int - :type vni_start: int - :type src_ip_start: str - :type dst_ip_start: str - :type ip_step: int - :type ip_limit: str - :returns: Number of created VXLAN interfaces. - :rtype: int - """ - commands = list() - - src_ip_start_int = IPUtil.ip_to_int(src_ip_start) - dst_ip_start_int = IPUtil.ip_to_int(dst_ip_start) - ip_limit_int = IPUtil.ip_to_int(ip_limit) - - tmp_fn = '/tmp/create_vxlan_interfaces.config' - for i in range(0, vxlan_count): - src_ip_int = src_ip_start_int + i * ip_step - dst_ip_int = dst_ip_start_int + i * ip_step - if src_ip_int > ip_limit_int or dst_ip_int > ip_limit_int: - logger.warn("Can't do more iterations - IPv4 address limit " - "has been reached.") - vxlan_count = i - break - src_ip = IPUtil.int_to_ip(src_ip_int) - dst_ip = IPUtil.int_to_ip(dst_ip_int) - commands.append( - 'sw_interface_add_del_address sw_if_index {sw_idx} {ip}/32\n' - .format(sw_idx=Topology.get_interface_sw_index( - node, node_vxlan_if), ip=src_ip)) - commands.append( - 'vxlan_add_del_tunnel src {src_ip} dst {dst_ip} vni {vni}\n' - .format(src_ip=src_ip, dst_ip=dst_ip, vni=vni_start+i)) - commands.append( - 'create_vlan_subif sw_if_index {sw_idx} vlan {vlan}\n' - .format(sw_idx=Topology.get_interface_sw_index( - node, node_vlan_if), vlan=i+1)) - - VatExecutor().write_and_execute_script(node, tmp_fn, commands) - - return vxlan_count - - @staticmethod - def vpp_put_vxlan_and_vlan_interfaces_up(node, vxlan_count, node_vlan_if): - """ - Update topology with VXLAN interfaces and VLAN sub-interfaces data - and put interfaces up. - - :param node: VPP node. - :param vxlan_count: Number of tunnel interfaces. - :param node_vlan_if: VPP node interface key where VLAN sub-interfaces - have been created. - :type node: dict - :type vxlan_count: int - :type node_vlan_if: str - """ - with VatTerminal(node) as vat_ter: - if_data = vat_ter.vat_terminal_exec_cmd_from_template( - 'interface_dump.vat')[0] - - tmp_fn = '/tmp/put_subinterfaces_up.config' - commands = list() - for i in range(0, vxlan_count): - vxlan_subif_key = Topology.add_new_port(node, 'vxlan_tunnel') - vxlan_subif_name = 'vxlan_tunnel{nr}'.format(nr=i) - vxlan_found = False - vxlan_subif_idx = None - vlan_subif_key = Topology.add_new_port(node, 'vlan_subif') - vlan_subif_name = '{if_name}.{vlan}'.format( - if_name=Topology.get_interface_name( - node, node_vlan_if), vlan=i+1) - vlan_found = False - vlan_idx = None - for data in if_data: - if_name = data['interface_name'] - if not vxlan_found and if_name == vxlan_subif_name: - vxlan_subif_idx = data['sw_if_index'] - vxlan_found = True - elif not vlan_found and if_name == vlan_subif_name: - vlan_idx = data['sw_if_index'] - vlan_found = True - if vxlan_found and vlan_found: - break - Topology.update_interface_sw_if_index( - node, vxlan_subif_key, vxlan_subif_idx) - Topology.update_interface_name( - node, vxlan_subif_key, vxlan_subif_name) - commands.append( - 'sw_interface_set_flags sw_if_index {sw_idx} admin-up link-up\n' - .format(sw_idx=vxlan_subif_idx)) - Topology.update_interface_sw_if_index( - node, vlan_subif_key, vlan_idx) - Topology.update_interface_name( - node, vlan_subif_key, vlan_subif_name) - commands.append( - 'sw_interface_set_flags sw_if_index {sw_idx} admin-up link-up\n' - .format(sw_idx=vlan_idx)) - - VatExecutor().write_and_execute_script(node, tmp_fn, commands) - - @staticmethod - def vpp_put_vxlan_and_vlan_interfaces_to_bridge_domain( - node, node_vxlan_if, vxlan_count, op_node, op_node_if, dst_ip_start, - ip_step, bd_id_start): - """ - Configure ARPs and routes for VXLAN interfaces and put each pair of - VXLAN tunnel interface and VLAN sub-interface to separate bridge-domain. - - :param node: VPP node. - :param node_vxlan_if: VPP node interface key where VXLAN tunnel - interfaces have been created. - :param vxlan_count: Number of tunnel interfaces. - :param op_node: Opposite VPP node for VXLAN tunnel interfaces. - :param op_node_if: Opposite VPP node interface key for VXLAN tunnel - interfaces. - :param dst_ip_start: VXLAN tunnel destination IP address start. - :param ip_step: IP address incremental step. - :param bd_id_start: Bridge-domain ID start. - :type node: dict - :type node_vxlan_if: str - :type vxlan_count: int - :type op_node: dict - :type op_node_if: - :type dst_ip_start: str - :type ip_step: int - :type bd_id_start: int - """ - sw_idx_vxlan = Topology.get_interface_sw_index(node, node_vxlan_if) - - dst_ip_start_int = IPUtil.ip_to_int(dst_ip_start) - - tmp_fn = '/tmp/configure_routes_and_bridge_domains.config' - commands = list() - for i in range(0, vxlan_count): - dst_ip = IPUtil.int_to_ip(dst_ip_start_int + i * ip_step) - commands.append( - 'ip_neighbor_add_del sw_if_index {sw_idx} dst {ip} mac {mac}\n' - .format(sw_idx=sw_idx_vxlan, ip=dst_ip, - mac=Topology.get_interface_mac(op_node, op_node_if))) - commands.append( - 'ip_add_del_route {ip}/32 via {ip} sw_if_index {sw_idx}' - ' resolve-attempts 10 count 1\n'.format( - ip=dst_ip, sw_idx=sw_idx_vxlan)) - bd_id = bd_id_start + i - subif_id = i + 1 - commands.append( - 'sw_interface_set_l2_bridge sw_if_index {sw_idx} bd_id {bd_id} ' - 'shg 0 enable\n'.format(sw_idx=Topology.get_interface_sw_index( - node, 'vxlan_tunnel{nr}'.format(nr=subif_id)), bd_id=bd_id)) - commands.append( - 'sw_interface_set_l2_bridge sw_if_index {sw_idx} bd_id {bd_id} ' - 'shg 0 enable\n'.format(sw_idx=Topology.get_interface_sw_index( - node, 'vlan_subif{nr}'.format(nr=subif_id)), bd_id=bd_id)) - - VatExecutor().write_and_execute_script(node, tmp_fn, commands) - @staticmethod def vpp_sw_interface_rx_placement_dump(node): """Dump VPP interface RX placement on node. @@ -1639,7 +1605,6 @@ class InterfaceUtil(object): :returns: Thread mapping information as a list of dictionaries. :rtype: list """ - cmd = 'sw_interface_rx_placement_dump' cmd_reply = 'sw_interface_rx_placement_details' err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format( @@ -1669,20 +1634,14 @@ class InterfaceUtil(object): :raises RuntimeError: If failed to run command on host or if no API reply received. """ - cmd = 'sw_interface_set_rx_placement' - cmd_reply = 'sw_interface_set_rx_placement_reply' - err_msg = "Failed to run '{cmd}' PAPI command on host {host}!".format( - host=node['host'], cmd=cmd) + err_msg = "Failed to set interface RX placement to worker on host " \ + "{host}!".format(host=node['host']) args = dict(sw_if_index=sw_if_index, queue_id=queue_id, worker_id=worker_id) with PapiExecutor(node) as papi_exec: - papi_resp = papi_exec.add(cmd, **args).execute_should_pass(err_msg) - data = papi_resp.reply[0]['api_reply'][cmd_reply] - if data['retval'] != 0: - raise RuntimeError("Failed to set interface RX placement " - "to worker on host {host}". - format(host=node['host'])) + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) @staticmethod def vpp_round_robin_rx_placement(node, prefix): @@ -1696,6 +1655,8 @@ class InterfaceUtil(object): """ worker_id = 0 worker_cnt = len(VPPUtil.vpp_show_threads(node)) - 1 + if not worker_cnt: + return for placement in InterfaceUtil.vpp_sw_interface_rx_placement_dump(node): for interface in node['interfaces'].values(): if placement['sw_if_index'] == interface['vpp_sw_index'] \