X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FInterfaceUtil.py;h=25540dc0db8dfde8c68e0ac1747d861e6943ab51;hp=558e99b4e730c8a7d7188ce7f647011524546f3e;hb=44e82a32bb10129d1f85d12e5141de2a17d5ff1a;hpb=9aab55627669dd002fed544fc1358760793c77c5 diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index 558e99b4e7..25540dc0db 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -130,7 +130,7 @@ class InterfaceUtil(object): :type node: dict :type timeout: int :returns: Nothing. - :raises: RuntimeError if the timeout period value has elapsed. + :raises RuntimeError: If the timeout period value has elapsed. """ if_ready = False not_ready = [] @@ -196,10 +196,10 @@ class InterfaceUtil(object): :type node: dict :type interface: int or str :returns: List of dictionaries containing data for each interface, or a - single dictionary for the specified interface. + single dictionary for the specified interface. :rtype: list or dict :raises TypeError: if the data type of interface is neither basestring - nor int. + nor int. """ with VatTerminal(node) as vat: response = vat.vat_terminal_exec_cmd_from_template( @@ -220,6 +220,29 @@ class InterfaceUtil(object): return dict() return data + @staticmethod + def vpp_get_interface_name(node, sw_if_index): + """Get interface name for the given SW interface index from actual + interface dump. + + :param node: VPP node to get interface data from. + :param sw_if_index: SW interface index of the specific interface. + :type node: dict + :type sw_if_index: int + :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 + @staticmethod def vpp_get_interface_mac(node, interface=None): """Get MAC address for the given interface from actual interface dump. @@ -256,11 +279,19 @@ class InterfaceUtil(object): :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. + prefix length and also the subnet mask for ipv4 addresses. + Note: A single interface may have multiple IP addresses assigned. :rtype: list """ - sw_if_index = InterfaceUtil.get_sw_if_index(node, interface) + + 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( @@ -285,7 +316,6 @@ class InterfaceUtil(object): :type node: dict :type pci_addr: str :type driver: str - :returns: None. :raises RuntimeError: If unbinding from the current driver fails. :raises RuntimeError: If binding to the new driver fails. """ @@ -324,7 +354,7 @@ class InterfaceUtil(object): :returns: Interface driver or None if not found. :rtype: str :raises RuntimeError: If it is not possible to get the interface driver - information from the node. + information from the node. .. note:: # lspci -vmmks 0000:00:05.0 @@ -340,21 +370,34 @@ class InterfaceUtil(object): ssh = SSH() ssh.connect(node) - cmd = 'lspci -vmmks {0}'.format(pci_addr) - - (ret_code, stdout, _) = ssh.exec_command(cmd) - if int(ret_code) != 0: - raise RuntimeError("'{0}' failed on '{1}'" - .format(cmd, node['host'])) + for i in range(3): + logger.trace('Try {}: Get interface driver'.format(i)) + cmd = 'sh -c "echo 1 > /sys/bus/pci/rescan"' + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise RuntimeError("'{0}' failed on '{1}'" + .format(cmd, node['host'])) - for line in stdout.splitlines(): - if len(line) == 0: - continue - (name, value) = line.split("\t", 1) - if name == 'Driver:': - return value + cmd = 'lspci -vmmks {0}'.format(pci_addr) + (ret_code, stdout, _) = ssh.exec_command(cmd) + if int(ret_code) != 0: + raise RuntimeError("'{0}' failed on '{1}'" + .format(cmd, node['host'])) - return None + for line in stdout.splitlines(): + if len(line) == 0: + continue + try: + (name, value) = line.split("\t", 1) + except ValueError: + if name != "Driver:": + pass + else: + return None + if name == 'Driver:': + return value if value else None + raise RuntimeError('Get interface driver for: {0}' + .format(pci_addr)) @staticmethod def tg_set_interfaces_udev_rules(node): @@ -428,12 +471,49 @@ class InterfaceUtil(object): interface_dump_json) @staticmethod - def update_tg_interface_data_on_node(node): - """Update interface name for TG/linux node in DICT__nodes. + def update_nic_interface_names(node): + """Update interface names based on nic type and PCI address. - :param node: Node selected from DICT__nodes. + This method updates interface names in the same format as VPP does. + + :param node: Node dictionary. :type node: dict - :raises RuntimeError: If getting of interface name and MAC fails. + """ + for ifc in node['interfaces'].values(): + if_pci = ifc['pci_address'].replace('.', ':').split(':') + bus = '{:x}'.format(int(if_pci[1], 16)) + dev = '{:x}'.format(int(if_pci[2], 16)) + fun = '{:x}'.format(int(if_pci[3], 16)) + loc = '{bus}/{dev}/{fun}'.format(bus=bus, dev=dev, fun=fun) + if ifc['model'] == 'Intel-XL710': + ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc) + elif ifc['model'] == 'Intel-X710': + ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc) + elif ifc['model'] == 'Intel-X520-DA2': + ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc) + elif ifc['model'] == 'Cisco-VIC-1385': + ifc['name'] = 'FortyGigabitEthernet{loc}'.format(loc=loc) + elif ifc['model'] == 'Cisco-VIC-1227': + ifc['name'] = 'TenGigabitEthernet{loc}'.format(loc=loc) + else: + ifc['name'] = 'UnknownEthernet{loc}'.format(loc=loc) + + @staticmethod + def update_nic_interface_names_on_all_duts(nodes): + """Update interface names based on nic type and PCI address on all DUTs. + + This method updates interface names in the same format as VPP does. + + :param nodes: Topology nodes. + :type nodes: dict + """ + for node in nodes.values(): + if node['type'] == NodeType.DUT: + InterfaceUtil.update_nic_interface_names(node) + + @staticmethod + def update_tg_interface_data_on_node(node): + """Update interface name for TG/linux node in DICT__nodes. .. note:: # for dev in `ls /sys/class/net/`; @@ -443,7 +523,11 @@ class InterfaceUtil(object): "52:54:00:e1:8a:0f": "eth2" "00:00:00:00:00:00": "lo" - .. todo:: parse lshw -json instead + .. note:: TODO: parse lshw -json instead + + :param node: Node selected from DICT__nodes. + :type node: dict + :raises RuntimeError: If getting of interface name and MAC fails. """ # First setup interface driver specified in yaml file InterfaceUtil.tg_set_interfaces_default_driver(node) @@ -560,7 +644,7 @@ class InterfaceUtil(object): :returns: Name and index of created subinterface. :rtype: tuple :raises RuntimeError: if it is unable to create VLAN subinterface on the - node. + node. """ iface_key = Topology.get_interface_by_name(node, interface) sw_if_index = Topology.get_interface_sw_index(node, iface_key) @@ -569,10 +653,14 @@ class InterfaceUtil(object): sw_if_index=sw_if_index, vlan=vlan) if output[0]["retval"] == 0: - sw_subif_index = output[0]["sw_if_index"] + sw_subif_idx = output[0]["sw_if_index"] logger.trace('VLAN subinterface with sw_if_index {} and VLAN ID {} ' - 'created on node {}'.format(sw_subif_index, + 'created on node {}'.format(sw_subif_idx, vlan, node['host'])) + if_key = Topology.add_new_port(node, "vlan_subif") + 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) else: raise RuntimeError('Unable to create VLAN subinterface on node {}' .format(node['host'])) @@ -580,7 +668,7 @@ class InterfaceUtil(object): with VatTerminal(node, False) as vat: vat.vat_terminal_exec_cmd('exec show interfaces') - return '{}.{}'.format(interface, vlan), sw_subif_index + return '{}.{}'.format(interface, vlan), sw_subif_idx @staticmethod def create_vxlan_interface(node, vni, source_ip, destination_ip): @@ -600,7 +688,7 @@ class InterfaceUtil(object): :returns: SW IF INDEX of created interface. :rtype: int :raises RuntimeError: if it is unable to create VxLAN interface on the - node. + node. """ output = VatExecutor.cmd_from_template(node, "vxlan_create.vat", src=source_ip, @@ -609,9 +697,14 @@ class InterfaceUtil(object): output = output[0] if output["retval"] == 0: - return output["sw_if_index"] + 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}' + raise RuntimeError("Unable to create VXLAN interface on node {0}" .format(node)) @staticmethod @@ -620,14 +713,14 @@ class InterfaceUtil(object): :param node: VPP node to get interface data from. :param interface: Numeric index or name string of a specific interface. - If None, information about all VxLAN interfaces is returned. + If None, information about all VxLAN interfaces is returned. :type node: dict :type interface: int or str :returns: Dictionary containing data for the given VxLAN interface or if - interface=None, the list of dictionaries with all VxLAN interfaces. + interface=None, the list of dictionaries with all VxLAN interfaces. :rtype: dict or list :raises TypeError: if the data type of interface is neither basestring - nor int. + nor int. """ param = "sw_if_index" if interface is None: @@ -676,7 +769,7 @@ class InterfaceUtil(object): :type node: dict :type name: str :returns: Dictionary of information about a specific TAP interface, or - a List of dictionaries containing all TAP data for the given node. + a List of dictionaries containing all TAP data for the given node. :rtype: dict or list """ with VatTerminal(node) as vat: @@ -702,7 +795,8 @@ class InterfaceUtil(object): :param outer_vlan_id: Optional outer VLAN ID. :param inner_vlan_id: Optional inner VLAN ID. :param type_subif: Optional type of sub-interface. Values supported by - VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match] [default_sub] + VPP: [no_tags] [one_tag] [two_tags] [dot1ad] [exact_match] + [default_sub] :type node: dict :type interface: str or int :type sub_id: int @@ -737,9 +831,13 @@ class InterfaceUtil(object): type_subif=type_subif) if output[0]["retval"] == 0: - sw_subif_index = output[0]["sw_if_index"] + sw_subif_idx = output[0]["sw_if_index"] logger.trace('Created subinterface with index {}' - .format(sw_subif_index)) + .format(sw_subif_idx)) + 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) else: raise RuntimeError('Unable to create sub-interface on node {}' .format(node['host'])) @@ -748,7 +846,7 @@ class InterfaceUtil(object): vat.vat_terminal_exec_cmd('exec show interfaces') name = '{}.{}'.format(interface, sub_id) - return name, sw_subif_index + return name, sw_subif_idx @staticmethod def create_gre_tunnel_interface(node, source_ip, destination_ip): @@ -770,14 +868,19 @@ class InterfaceUtil(object): output = output[0] if output["retval"] == 0: - sw_if_index = output["sw_if_index"] + 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_index) - return name, sw_if_index + interface_dump_json, sw_if_idx) + + 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) + + return name, sw_if_idx else: raise RuntimeError('Unable to create GRE tunnel on node {}.' .format(node)) @@ -791,15 +894,134 @@ class InterfaceUtil(object): :returns: SW interface index. :rtype: int :raises RuntimeError: If it is not possible to create loopback on the - node. + node. """ out = VatExecutor.cmd_from_template(node, "create_loopback.vat") if out[0].get('retval') == 0: - return out[0].get('sw_if_index') + 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'])) + @staticmethod + def vpp_create_bond_interface(node, mode, load_balance=None, mac=None): + """Create bond interface on VPP node. + + :param node: DUT node from topology. + :param mode: Link bonding mode. + :param load_balance: Load balance (optional, valid for xor and lacp + modes, otherwise ignored). + :param mac: MAC address to assign to the bond interface (optional). + :type node: dict + :type mode: str + :type load_balance: str + :type mac: str + :returns: Interface key (name) in topology. + :rtype: str + :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) + lb = '' if load_balance is None \ + else 'lb {lb}'.format(lb=load_balance) + + output = VatExecutor.cmd_from_template( + node, 'create_bond_interface.vat', mode=mode, lb=lb, mac=hw_addr) + + if output[0].get('retval') == 0: + sw_if_idx = output[0].get('sw_if_index') + InterfaceUtil.add_bond_eth_interface(node, sw_if_idx=sw_if_idx) + if_key = Topology.get_interface_by_sw_index(node, sw_if_idx) + return if_key + else: + raise RuntimeError('Create bond interface failed on node "{n}"' + .format(n=node['host'])) + + @staticmethod + def add_bond_eth_interface(node, ifc_name=None, sw_if_idx=None): + """Add BondEthernet interface to current topology. + + :param node: DUT node from topology. + :param ifc_name: Name of the BondEthernet interface. + :param sw_if_idx: SW interface index. + :type node: dict + :type ifc_name: str + :type sw_if_idx: int + """ + if_key = Topology.add_new_port(node, 'eth_bond') + + 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) + 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) + Topology.update_interface_mac_address(node, if_key, ifc_mac) + + @staticmethod + def vpp_enslave_physical_interface(node, interface, bond_interface): + """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 + :type node: dict + :type interface: str + :type bond_interface: 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'])) + + @staticmethod + def vpp_show_bond_data_on_node(node, details=False): + """Show (detailed) bond information on VPP node. + + :param node: DUT node from topology. + :param details: If detailed information is required or not. + :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) + + @staticmethod + def vpp_show_bond_data_on_all_nodes(nodes, details=False): + """Show (detailed) bond information on all VPP nodes in DICT__nodes. + + :param nodes: Nodes in the topology. + :param details: If detailed information is required or not. + :type nodes: dict + :type details: bool + """ + for node_data in nodes.values(): + if node_data['type'] == NodeType.DUT: + InterfaceUtil.vpp_show_bond_data_on_node(node_data, details) + @staticmethod def vpp_enable_input_acl_interface(node, interface, ip_version, table_index): @@ -897,12 +1119,12 @@ class InterfaceUtil(object): :param node: VPP node to get interface data from. :param interface_name: Name of the specific interface. If None, - information about all VxLAN GPE interfaces is returned. + information about all VxLAN GPE interfaces is returned. :type node: dict :type interface_name: str :returns: Dictionary containing data for the given VxLAN GPE interface - or if interface=None, the list of dictionaries with all VxLAN GPE - interfaces. + or if interface=None, the list of dictionaries with all VxLAN GPE + interfaces. :rtype: dict or list """ @@ -981,7 +1203,6 @@ class InterfaceUtil(object): raise RuntimeError('Unable to assign interface to FIB node {}.' .format(node)) - @staticmethod def set_linux_interface_mac(node, interface, mac, namespace=None): """Set MAC address for interface in linux.