aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/InterfaceUtil.py
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libraries/python/InterfaceUtil.py')
-rw-r--r--resources/libraries/python/InterfaceUtil.py237
1 files changed, 237 insertions, 0 deletions
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))