From fe1975eb1ac994df1bd759deda7154bb7dd9d7a7 Mon Sep 17 00:00:00 2001 From: Jan Gelety Date: Mon, 18 Mar 2019 13:30:11 +0100 Subject: CSIT-1468: InterfaceUtil migration from VAT to PAPI Change-Id: I0062710d58996be767a852dc00545fedd60a5c72 Signed-off-by: Jan Gelety --- resources/libraries/python/InterfaceUtil.py | 1341 +++++++++++++++++---------- resources/libraries/python/L2Util.py | 17 +- resources/libraries/python/PapiExecutor.py | 19 +- resources/libraries/python/ProxyArp.py | 22 + resources/libraries/python/VPPUtil.py | 31 +- 5 files changed, 925 insertions(+), 505 deletions(-) (limited to 'resources/libraries/python') diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index 3f268092e3..5617c472f4 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -14,33 +14,90 @@ """Interface util library.""" from socket import AF_INET, AF_INET6, inet_ntop, inet_pton -from socket import error as inet_error -from time import time, sleep +from time import sleep +from enum import IntEnum +from ipaddress import IPv4Address, IPv6Address +from ipaddress import AddressValueError, NetmaskValueError 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.PapiExecutor import PapiExecutor from resources.libraries.python.IPUtil import convert_ipv4_netmask_prefix -from resources.libraries.python.IPUtil import IPUtil +from resources.libraries.python.L2Util import L2Util from resources.libraries.python.PapiExecutor import PapiExecutor 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. @@ -59,31 +116,39 @@ class InterfaceUtil(object): :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: "{}"' @@ -105,6 +170,7 @@ class InterfaceUtil(object): :raises ValueError: If the node type is "DUT". :raises ValueError: If the node has an unknown node type. """ + if node['type'] == NodeType.DUT: raise ValueError('Node {}: Setting Ethernet MTU for interface ' 'on DUT nodes not supported', node['host']) @@ -126,6 +192,7 @@ class InterfaceUtil(object): :type node: dict :returns: Nothing. """ + for ifc in node['interfaces']: InterfaceUtil.set_interface_ethernet_mtu(node, ifc, 1500) @@ -140,16 +207,20 @@ class InterfaceUtil(object): :type interface: str or int :type mtu: int """ + if isinstance(interface, basestring): sw_if_index = Topology.get_interface_sw_index(node, interface) 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)) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg).\ + verify_reply(err_msg=err_msg) @staticmethod def vpp_set_interfaces_mtu_on_node(node, mtu=9200): @@ -160,6 +231,7 @@ class InterfaceUtil(object): :type node: dict :type mtu: int """ + for interface in node['interfaces']: InterfaceUtil.vpp_set_interface_mtu(node, interface, mtu) @@ -172,79 +244,65 @@ class InterfaceUtil(object): :type nodes: dict :type mtu: int """ + for node in nodes.values(): if node['type'] == NodeType.DUT: 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. @@ -256,23 +314,54 @@ 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 @@ -292,14 +381,28 @@ class InterfaceUtil(object): 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. @@ -314,51 +417,57 @@ class InterfaceUtil(object): 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 + + return if_data.get('l2_address') @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 + TODO: Move to IPUtils + + :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 + :rtype: list """ try: sw_if_index = Topology.convert_interface_reference( - node, interface, "sw_if_index") + 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) + is_ipv6 = 1 if ip_version == 'ipv6' else 0 - data = response[0] + cmd = 'ip_address_dump' + cmd_reply = 'ip_address_details' + args = dict(sw_if_index=sw_if_index, + is_ipv6=is_ipv6) + err_msg = 'Failed to get L2FIB 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) - if ip_version == "ipv4": + data = list() + for item in papi_resp.reply[0]['api_reply']: + item[cmd_reply]['ip'] = inet_ntop(AF_INET6, item[cmd_reply]['ip']) \ + if is_ipv6 else inet_ntop(AF_INET, item[cmd_reply]['ip'][0:4]) + data.append(item[cmd_reply]) + + if ip_version == 'ipv4': for item in data: - item["netmask"] = convert_ipv4_netmask_prefix( - item["prefix_length"]) + item['netmask'] = convert_ipv4_netmask_prefix( + item['prefix_length']) return data @staticmethod @@ -470,18 +579,31 @@ 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): @@ -654,7 +776,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. @@ -667,37 +789,31 @@ class InterfaceUtil(object): :raises RuntimeError: if it is unable to create VLAN subinterface on the node. """ + 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. @@ -711,22 +827,41 @@ 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)) + + try: + src_address = IPv6Address(unicode(source_ip)) + dst_address = IPv6Address(unicode(destination_ip)) + af_inet = AF_INET6 + is_ipv6 = 1 + except (AddressValueError, NetmaskValueError): + src_address = IPv4Address(unicode(source_ip)) + dst_address = IPv4Address(unicode(destination_ip)) + af_inet = AF_INET + is_ipv6 = 0 + + cmd = 'vxlan_add_del_tunnel' + args = dict(is_add=1, + is_ipv6=is_ipv6, + instance=Constants.BITWISE_NON_ZERO, + src_address=inet_pton(af_inet, str(src_address)), + dst_address=inet_pton(af_inet, str(dst_address)), + 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): @@ -743,48 +878,102 @@ 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") - return response[0] + 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. + + :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 @@ -793,15 +982,46 @@ 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, @@ -828,45 +1048,35 @@ class InterfaceUtil(object): :raises RuntimeError: If it is not possible to create sub-interface. """ - 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'])) + 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) - 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): @@ -883,20 +1093,11 @@ class InterfaceUtil(object): :raises RuntimeError: If unable to create GRE tunnel interface. """ - try: - src_address = inet_pton(AF_INET6, source_ip) - dst_address = inet_pton(AF_INET6, destination_ip) - is_ipv6 = 1 - except inet_error: - src_address = inet_pton(AF_INET, source_ip) - dst_address = inet_pton(AF_INET, destination_ip) - is_ipv6 = 0 - cmd = 'gre_tunnel_add_del' tunnel = dict(type=0, instance=Constants.BITWISE_NON_ZERO, - src=src_address, - dst=dst_address, + src=str(source_ip), + dst=str(destination_ip), outer_fib_id=0, session_id=0) args = dict(is_add=1, @@ -926,17 +1127,22 @@ 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): @@ -956,22 +1162,28 @@ 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): @@ -986,21 +1198,16 @@ class InterfaceUtil(object): :type sw_if_idx: int :type ifc_pfx: str """ - 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_key = Topology.add_new_port(node, ifc_pfx) 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 @@ -1018,50 +1225,53 @@ 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): @@ -1072,9 +1282,87 @@ 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): @@ -1103,21 +1391,29 @@ 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 @@ -1130,34 +1426,48 @@ 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] + 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) + + return papi_resp @staticmethod - def get_interface_vrf_table(node, interface): + def get_interface_vrf_table(node, interface, ip_version='ipv4'): """Get vrf ID for the given interface. + TODO: Move to proper IP library when implementing CSIT-1459. + :param node: VPP node. :param interface: Name or sw_if_index of a specific interface. :type node: dict + :param ip_version: IP protocol version (ipv4 or ipv6). :type interface: str or int + :type ip_version: str :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 - 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"] + is_ipv6 = 1 if ip_version == 'ipv6' else 0 + + cmd = 'sw_interface_get_table' + args = dict(sw_if_index=sw_if_index, + is_ipv6=is_ipv6) + err_msg = 'Failed to get VRF id assigned to 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) + + return papi_resp['vrf_id'] @staticmethod def get_sw_if_index(node, interface_name): @@ -1171,14 +1481,9 @@ class InterfaceUtil(object): :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): @@ -1195,51 +1500,76 @@ class InterfaceUtil(object): :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) + + 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 - with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template( - "proxy_arp_intfc_enable.vat", - sw_if_index=sw_if_index) + logger.debug('VXLAN-GPE data:\n{vxlan_gpe_data}'.format( + vxlan_gpe_data=data)) + return data @staticmethod - def vpp_ip_source_check_setup(node, interface): + def vpp_ip_source_check_setup(node, if_name): """Setup Reverse Path Forwarding source check on interface. + TODO: Move to proper IP library when implementing CSIT-1459. + :param node: Node to setup RPF source check. - :param interface: Interface name to setup RPF source check. + :param if_name: Interface name to setup RPF source check. :type node: dict - :type interface: str + :type if_name: str """ - with VatTerminal(node) as vat: - vat.vat_terminal_exec_cmd_from_template("ip_source_check.vat", - interface_name=interface) + + cmd = 'ip_source_check_interface_add_del' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index(node, if_name), + is_add=1, + loose=0) + err_msg = 'Failed to enable source check on interface {ifc}'.format( + ifc=if_name) + with PapiExecutor(node) as papi_exec: + papi_exec.add(cmd, **args).get_replies(err_msg). \ + verify_reply(err_msg=err_msg) @staticmethod def assign_interface_to_fib_table(node, interface, table_id, ipv6=False): @@ -1254,21 +1584,17 @@ 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, @@ -1496,36 +1822,58 @@ class InterfaceUtil(object): :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) + + try: + src_address_start = IPv6Address(unicode(src_ip_start)) + dst_address_start = IPv6Address(unicode(dst_ip_start)) + ip_address_limit = IPv6Address(unicode(ip_limit)) + af_inet = AF_INET6 + is_ipv6 = 1 + except (AddressValueError, NetmaskValueError): + src_address_start = IPv4Address(unicode(src_ip_start)) + dst_address_start = IPv4Address(unicode(dst_ip_start)) + ip_address_limit = IPv4Address(unicode(ip_limit)) + af_inet = AF_INET + is_ipv6 = 0 + + with PapiExecutor(node) as papi_exec: + for i in xrange(0, vxlan_count): + src_ip = src_address_start + i * ip_step + dst_ip = dst_address_start + i * ip_step + if src_ip > ip_address_limit or dst_ip > ip_address_limit: + logger.warn("Can't do more iterations - IP address limit " + "has been reached.") + vxlan_count = i + break + cmd = 'sw_interface_add_del_address' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index( + node, node_vxlan_if), + is_add=1, + is_ipv6=0, + del_all=0, + address_length=128 if is_ipv6 else 32, + address=inet_pton(af_inet, str(src_ip))) + papi_exec.add(cmd, **args) + cmd = 'vxlan_add_del_tunnel' + args = dict( + is_add=1, + is_ipv6=0, + instance=Constants.BITWISE_NON_ZERO, + src_address=inet_pton(af_inet, str(src_ip)), + dst_address=inet_pton(af_inet, str(dst_ip)), + mcast_sw_if_index=Constants.BITWISE_NON_ZERO, + encap_vrf_id=0, + decap_next_index=Constants.BITWISE_NON_ZERO, + vni=int(vni_start)+i) + papi_exec.add(cmd, **args) + cmd = 'create_vlan_subif' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index( + node, node_vlan_if), + vlan_id=i+1) + papi_exec.add(cmd, **args) + papi_exec.get_replies().verify_replies() return vxlan_count @@ -1543,49 +1891,47 @@ class InterfaceUtil(object): :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) + + if_data = InterfaceUtil.vpp_get_interface_data(node) + + with PapiExecutor(node) as papi_exec: + for i in xrange(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 not vxlan_found \ + and data['interface_name'] == vxlan_subif_name: + vxlan_subif_idx = data['sw_if_index'] + vxlan_found = True + elif not vlan_found \ + and data['interface_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) + cmd = 'sw_interface_set_flags' + args1 = dict(sw_if_index=vxlan_subif_idx, + admin_up_down=1) + Topology.update_interface_sw_if_index( + node, vlan_subif_key, vlan_idx) + Topology.update_interface_name( + node, vlan_subif_key, vlan_subif_name) + args2 = dict(sw_if_index=vlan_idx, + admin_up_down=1) + papi_exec.add(cmd, **args1).add(cmd, **args2) + papi_exec.get_replies().verify_replies() @staticmethod def vpp_put_vxlan_and_vlan_interfaces_to_bridge_domain( @@ -1614,34 +1960,62 @@ class InterfaceUtil(object): :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) + + try: + dst_address_start = IPv6Address(unicode(dst_ip_start)) + af_inet = AF_INET6 + is_ipv6 = 1 + except (AddressValueError, NetmaskValueError): + dst_address_start = IPv4Address(unicode(dst_ip_start)) + af_inet = AF_INET + is_ipv6 = 0 + + with PapiExecutor(node) as papi_exec: + for i in xrange(0, vxlan_count): + dst_ip = dst_address_start + i * ip_step + neighbor = dict( + sw_if_index=Topology.get_interface_sw_index( + node, node_vxlan_if), + flags=0, + mac_address=str( + Topology.get_interface_mac(op_node, op_node_if)), + ip_address=str(dst_ip)) + cmd = 'ip_neighbor_add_del' + args = dict( + is_add=1, + neighbor=neighbor) + papi_exec.add(cmd, **args) + cmd = 'ip_add_del_route' + args = dict( + next_hop_sw_if_index=Topology.get_interface_sw_index( + node, node_vxlan_if), + table_id=0, + is_add=1, + is_ipv6=is_ipv6, + next_hop_weight=1, + next_hop_proto=1 if is_ipv6 else 0, + dst_address_length=128 if is_ipv6 else 32, + dst_address=inet_pton(af_inet, str(dst_ip)), + next_hop_address=inet_pton(af_inet, str(dst_ip))) + papi_exec.add(cmd, **args) + cmd = 'sw_interface_set_l2_bridge' + args = dict( + rx_sw_if_index=Topology.get_interface_sw_index( + node, 'vxlan_tunnel{nr}'.format(nr=i+1)), + bd_id=int(bd_id_start+i), + shg=0, + port_type=0, + enable=1) + papi_exec.add(cmd, **args) + args = dict( + rx_sw_if_index=Topology.get_interface_sw_index( + node, 'vlan_subif{nr}'.format(nr=i+1)), + bd_id=int(bd_id_start+i), + shg=0, + port_type=0, + enable=1) + papi_exec.add(cmd, **args) + papi_exec.get_replies().verify_replies() @staticmethod def vpp_sw_interface_rx_placement_dump(node): @@ -1684,18 +2058,13 @@ class InterfaceUtil(object): """ 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): diff --git a/resources/libraries/python/L2Util.py b/resources/libraries/python/L2Util.py index 03ba5640c9..f1476044a6 100644 --- a/resources/libraries/python/L2Util.py +++ b/resources/libraries/python/L2Util.py @@ -18,6 +18,7 @@ from textwrap import wrap from enum import IntEnum +from resources.libraries.python.Constants import Constants from resources.libraries.python.PapiExecutor import PapiExecutor from resources.libraries.python.topology import Topology from resources.libraries.python.ssh import exec_cmd_no_error @@ -85,9 +86,9 @@ class L2Util(object): :returns: String representation of MAC address. :rtype: str """ - x = ':'.join(binascii.hexlify(mac_bin)[i:i + 2] - for i in range(0, 12, 2)) - return str(x.decode('ascii')) + mac_str = ':'.join(binascii.hexlify(mac_bin)[i:i + 2] + for i in range(0, 12, 2)) + return str(mac_str.decode('ascii')) @staticmethod def vpp_add_l2fib_entry(node, mac, interface, bd_id, static_mac=1, @@ -391,13 +392,9 @@ class L2Util(object): :rtype: list or dict """ - # TODO: set following variable per whole suite when planned FIB API - # changes are merged in VPP - bitwise_non_zero = 0xffffffff # equals to ~0 used in vpp code - cmd = 'bridge_domain_dump' cmd_reply = 'bridge_domain_details' - args = dict(bd_id=bd_id if isinstance(bd_id, int) else int(bd_id)) + args = dict(bd_id=int(bd_id)) err_msg = 'Failed to get L2FIB dump on host {host}'.format( host=node['host']) with PapiExecutor(node) as papi_exec: @@ -405,9 +402,9 @@ class L2Util(object): data = papi_resp.reply[0]['api_reply'] - bd_data = list() if bd_id == bitwise_non_zero else dict() + bd_data = list() if bd_id == Constants.BITWISE_NON_ZERO else dict() for bridge_domain in data: - if bd_id == bitwise_non_zero: + if bd_id == Constants.BITWISE_NON_ZERO: bd_data.append(bridge_domain[cmd_reply]) else: if bridge_domain[cmd_reply]['bd_id'] == bd_id: diff --git a/resources/libraries/python/PapiExecutor.py b/resources/libraries/python/PapiExecutor.py index 09cbf9ac73..8261a5e331 100644 --- a/resources/libraries/python/PapiExecutor.py +++ b/resources/libraries/python/PapiExecutor.py @@ -407,12 +407,27 @@ class PapiExecutor(object): :rtype: list """ + def process_value(val): + """Process value. + + :param val: Value to be processed. + :type val: object + :returns: Processed value. + :rtype: dict or str or int + """ + if isinstance(val, dict): + val_dict = dict() + for val_k, val_v in val.iteritems(): + val_dict[str(val_k)] = process_value(val_v) + return val_dict + else: + return binascii.hexlify(val) if isinstance(val, str) else val + api_data_processed = list() for api in api_d: api_args_processed = dict() for a_k, a_v in api["api_args"].iteritems(): - value = binascii.hexlify(a_v) if isinstance(a_v, str) else a_v - api_args_processed[str(a_k)] = value + api_args_processed[str(a_k)] = process_value(a_v) api_data_processed.append(dict(api_name=api["api_name"], api_args=api_args_processed)) return api_data_processed diff --git a/resources/libraries/python/ProxyArp.py b/resources/libraries/python/ProxyArp.py index 4879fe6bae..6cd723d327 100644 --- a/resources/libraries/python/ProxyArp.py +++ b/resources/libraries/python/ProxyArp.py @@ -13,6 +13,8 @@ """Proxy ARP library""" +from resources.libraries.python.InterfaceUtil import InterfaceUtil +from resources.libraries.python.PapiExecutor import PapiExecutor from resources.libraries.python.VatExecutor import VatTerminal @@ -34,3 +36,23 @@ class ProxyArp(object): vat.vat_terminal_exec_cmd_from_template("add_proxy_arp.vat", lo_ip4_addr=lo_ip4_addr, hi_ip4_addr=hi_ip4_addr) + + @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 + """ + + cmd = 'proxy_arp_intfc_enable_disable' + args = dict( + sw_if_index=InterfaceUtil.get_interface_index(node, interface), + enable_disable=1) + err_msg = 'Failed to enable proxy ARP 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) diff --git a/resources/libraries/python/VPPUtil.py b/resources/libraries/python/VPPUtil.py index 5b7728412e..6268e369be 100644 --- a/resources/libraries/python/VPPUtil.py +++ b/resources/libraries/python/VPPUtil.py @@ -13,6 +13,8 @@ """VPP util library.""" +import binascii + from robot.api import logger from resources.libraries.python.Constants import Constants @@ -191,14 +193,29 @@ class VPPUtil(object): :param node: Node to run command on. :type node: dict """ - vat = VatExecutor() - vat.execute_script("show_interface.vat", node, json_out=False) - try: - vat.script_should_have_passed() - except AssertionError: - raise RuntimeError('Failed to get VPP interfaces on host: {name}'. - format(name=node['host'])) + 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).execute_should_pass(err_msg) + + papi_if_dump = papi_resp.reply[0]['api_reply'] + + if_data = list() + for item in papi_if_dump: + data = item[cmd_reply] + data['interface_name'] = data['interface_name'].rstrip('\x00') + data['tag'] = data['tag'].rstrip('\x00') + data['l2_address'] = str(':'.join(binascii.hexlify( + data['l2_address'])[i:i + 2] for i in range(0, 12, 2)). + decode('ascii')) + if_data.append(data) + # TODO: return only base data + logger.trace('Interface data of host {host}:\n{if_data}'.format( + host=node['host'], if_data=if_data)) @staticmethod def vpp_show_crypto_device_mapping(node): -- cgit 1.2.3-korg