X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Flibraries%2Fpython%2FInterfaceUtil.py;h=4631ccce7a5fc260e9510f5a25f0ed3a8bed4ee9;hp=25503c08df66600046f0c16c8716e057b7708bd5;hb=5a2fd159dce96a70f2e5157314391aceb6d80197;hpb=3121b691debad27fcea1c6e2031e4a2544e42fbf diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index 25503c08df..4631ccce7a 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -14,15 +14,22 @@ """Interface util library""" from time import time, sleep + from robot.api import logger + +from resources.libraries.python.ssh import SSH from resources.libraries.python.ssh import 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.parsers.JsonParser import JsonParser 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): """Set interface state on a node. @@ -191,3 +198,233 @@ class InterfaceUtil(object): return data_if return data + + @staticmethod + def tg_set_interface_driver(node, pci_addr, driver): + """Set interface driver on the TG node. + + :param node: Node to set interface driver on (must be TG node). + :param pci_addr: PCI address of the interface. + :param driver: Driver name. + :type node: dict + :type pci_addr: str + :type driver: str + """ + old_driver = InterfaceUtil.tg_get_interface_driver(node, pci_addr) + if old_driver == driver: + return + + ssh = SSH() + ssh.connect(node) + + # Unbind from current driver + if old_driver is not None: + cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/unbind"'.format( + pci_addr, old_driver) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise Exception("'{0}' failed on '{1}'".format(cmd, + node['host'])) + + # Bind to the new driver + cmd = 'sh -c "echo {0} > /sys/bus/pci/drivers/{1}/bind"'.format( + pci_addr, driver) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise Exception("'{0}' failed on '{1}'".format(cmd, node['host'])) + + @staticmethod + def tg_get_interface_driver(node, pci_addr): + """Get interface driver from the TG node. + + :param node: Node to get interface driver on (must be TG node). + :param pci_addr: PCI address of the interface. + :type node: dict + :type pci_addr: str + :return: Interface driver or None if not found. + :rtype: str + + .. note:: + # lspci -vmmks 0000:00:05.0 + Slot: 00:05.0 + Class: Ethernet controller + Vendor: Red Hat, Inc + Device: Virtio network device + SVendor: Red Hat, Inc + SDevice: Device 0001 + PhySlot: 5 + Driver: virtio-pci + """ + 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 Exception("'{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 + + return None + + @staticmethod + def tg_set_interfaces_udev_rules(node): + """Set udev rules for interfaces. + + Create udev rules file in /etc/udev/rules.d where are rules for each + interface used by TG node, based on MAC interface has specific name. + So after unbind and bind again to kernel driver interface has same + name as before. This must be called after TG has set name for each + port in topology dictionary. + udev rule example + SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="52:54:00:e1:8a:0f", + NAME="eth1" + + :param node: Node to set udev rules on (must be TG node). + :type node: dict + """ + ssh = SSH() + ssh.connect(node) + + cmd = 'rm -f {0}'.format(InterfaceUtil.__UDEV_IF_RULES_FILE) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise Exception("'{0}' failed on '{1}'".format(cmd, node['host'])) + + for interface in node['interfaces'].values(): + rule = 'SUBSYSTEM==\\"net\\", ACTION==\\"add\\", ATTR{address}' + \ + '==\\"' + interface['mac_address'] + '\\", NAME=\\"' + \ + interface['name'] + '\\"' + cmd = 'sh -c "echo \'{0}\' >> {1}"'.format( + rule, InterfaceUtil.__UDEV_IF_RULES_FILE) + (ret_code, _, _) = ssh.exec_command_sudo(cmd) + if int(ret_code) != 0: + raise Exception("'{0}' failed on '{1}'".format(cmd, + node['host'])) + + cmd = '/etc/init.d/udev restart' + ssh.exec_command_sudo(cmd) + + @staticmethod + def tg_set_interfaces_default_driver(node): + """Set interfaces default driver specified in topology yaml file. + + :param node: Node to setup interfaces driver on (must be TG node). + :type node: dict + """ + for interface in node['interfaces'].values(): + InterfaceUtil.tg_set_interface_driver(node, + interface['pci_address'], + interface['driver']) + + @staticmethod + def update_vpp_interface_data_on_node(node): + """Update vpp generated interface data for a given node in DICT__nodes + + 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. + + :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) + + @staticmethod + def update_tg_interface_data_on_node(node): + """Update interface name for TG/linux node in DICT__nodes. + + :param node: Node selected from DICT__nodes. + :type node: dict + + .. note:: + # for dev in `ls /sys/class/net/`; + > do echo "\"`cat /sys/class/net/$dev/address`\": \"$dev\""; done + "52:54:00:9f:82:63": "eth0" + "52:54:00:77:ae:a9": "eth1" + "52:54:00:e1:8a:0f": "eth2" + "00:00:00:00:00:00": "lo" + + .. todo:: parse lshw -json instead + """ + # First setup interface driver specified in yaml file + InterfaceUtil.tg_set_interfaces_default_driver(node) + + # Get interface names + ssh = SSH() + ssh.connect(node) + + cmd = ('for dev in `ls /sys/class/net/`; do echo "\\"`cat ' + '/sys/class/net/$dev/address`\\": \\"$dev\\""; done;') + + (ret_code, stdout, _) = ssh.exec_command(cmd) + if int(ret_code) != 0: + raise Exception('Get interface name and MAC failed') + tmp = "{" + stdout.rstrip().replace('\n', ',') + "}" + interfaces = JsonParser().parse_data(tmp) + for interface in node['interfaces'].values(): + name = interfaces.get(interface['mac_address']) + if name is None: + continue + interface['name'] = name + + # Set udev rules for interfaces + InterfaceUtil.tg_set_interfaces_udev_rules(node) + + @staticmethod + def update_all_interface_data_on_all_nodes(nodes): + """Update interface names on all nodes in DICT__nodes. + + This method updates the topology dictionary by querying interface lists + of all nodes mentioned in the topology dictionary. + + :param nodes: Nodes in the topology. + :type nodes: dict + """ + for node_data in nodes.values(): + if node_data['type'] == NodeType.DUT: + InterfaceUtil.update_vpp_interface_data_on_node(node_data) + elif node_data['type'] == NodeType.TG: + InterfaceUtil.update_tg_interface_data_on_node(node_data) + + @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. + :param destination_ip: Destination IP of a VXLAN Tunnel End Point. + :type node: dict + :type vni: int + :type source_ip: str + :type destination_ip: str + :return: SW IF INDEX of created interface. + :rtype: int + """ + output = VatExecutor.cmd_from_template(node, "vxlan_create.vat", + src=source_ip, + dst=destination_ip, + vni=vni) + output = output[0] + + if output["retval"] == 0: + return output["sw_if_index"] + else: + raise RuntimeError('Unable to create VXLAN interface on node {}' + .format(node))