diff options
author | Peter Mikus <pmikus@cisco.com> | 2018-07-20 13:07:12 +0000 |
---|---|---|
committer | Peter Mikus <pmikus@cisco.com> | 2018-09-05 13:49:38 +0000 |
commit | 89620b26d6bbf3dd9d9707851d8d140471d40b56 (patch) | |
tree | cd22606feabb7190d6a612b46e8132fcda1d0f04 /resources | |
parent | b456176a07ae1a3721407693d75931db6b1cd14e (diff) |
CSIT-1205 Create AVF driver test
- Add L1 KWs for SR-IOV handling (init Vfs, remove Vfs, ...)
- Cleanup L1 KWs for SR-IOV bind/unbind/pci_get/...
- Add L2 KWs for Test Setup/Teardown, L2patch, Create AVF interface
- Add sample L2patch test fox x710, xxv710
Change-Id: If17077877455a14043617d8ea0d06cbe47b469e3
Signed-off-by: Peter Mikus <pmikus@cisco.com>
Diffstat (limited to 'resources')
-rw-r--r-- | resources/libraries/python/DUTSetup.py | 324 | ||||
-rw-r--r-- | resources/libraries/python/InterfaceUtil.py | 178 | ||||
-rw-r--r-- | resources/libraries/python/VppConfigGenerator.py | 5 | ||||
-rw-r--r-- | resources/libraries/python/ssh.py | 25 | ||||
-rw-r--r-- | resources/libraries/python/topology.py | 13 | ||||
-rw-r--r-- | resources/libraries/robot/performance/performance_configuration.robot | 22 | ||||
-rw-r--r-- | resources/libraries/robot/performance/performance_setup.robot | 69 | ||||
-rw-r--r-- | resources/libraries/robot/shared/default.robot | 41 | ||||
-rw-r--r-- | resources/templates/vat/create_avf_interface.vat | 1 |
9 files changed, 546 insertions, 132 deletions
diff --git a/resources/libraries/python/DUTSetup.py b/resources/libraries/python/DUTSetup.py index 8f9e94d560..84862d4167 100644 --- a/resources/libraries/python/DUTSetup.py +++ b/resources/libraries/python/DUTSetup.py @@ -16,7 +16,7 @@ from robot.api import logger from resources.libraries.python.topology import NodeType, Topology -from resources.libraries.python.ssh import SSH +from resources.libraries.python.ssh import SSH, exec_cmd_no_error from resources.libraries.python.constants import Constants from resources.libraries.python.VatExecutor import VatExecutor from resources.libraries.python.VPPUtil import VPPUtil @@ -252,7 +252,7 @@ class DUTSetup(object): def crypto_device_verify(node, force_init=False, numvfs=32): """Verify if Crypto QAT device virtual functions are initialized on all DUTs. If parameter force initialization is set to True, then try to - initialize or disable QAT. + initialize or remove VFs on QAT. :param node: DUT node. :param force_init: If True then try to initialize to specific value. @@ -261,37 +261,19 @@ class DUTSetup(object): :type force_init: bool :type numvfs: int :returns: nothing - :raises RuntimeError: If QAT is not initialized or failed to initialize. + :raises RuntimeError: If QAT VFs are not created and force init is set + to False. """ + pci_addr = Topology.get_cryptodev(node) + sriov_numvfs = DUTSetup.get_sriov_numvfs(node, pci_addr) - ssh = SSH() - ssh.connect(node) - - cryptodev = Topology.get_cryptodev(node) - cmd = 'cat /sys/bus/pci/devices/{0}/sriov_numvfs'.\ - format(cryptodev.replace(':', r'\:')) - - # Try to read number of VFs from PCI address of QAT device - for _ in range(3): - ret_code, stdout, _ = ssh.exec_command(cmd) - if not int(ret_code): - try: - sriov_numvfs = int(stdout) - except ValueError: - logger.trace('Reading sriov_numvfs info failed on {0}'. - format(node['host'])) - else: - if sriov_numvfs != numvfs: - if force_init: - # QAT is not initialized and we want to initialize - # with numvfs - DUTSetup.crypto_device_init(node, numvfs) - else: - raise RuntimeError('QAT device {0} is not ' - 'initialized to {1} on host {2}' - .format(cryptodev, numvfs, - node['host'])) - break + if sriov_numvfs != numvfs: + if force_init: + # QAT is not initialized and we want to initialize with numvfs + DUTSetup.crypto_device_init(node, numvfs) + else: + raise RuntimeError('QAT device failed to create VFs on {host}'. + format(host=node['host'])) @staticmethod def crypto_device_init(node, numvfs): @@ -304,33 +286,98 @@ class DUTSetup(object): :returns: nothing :raises RuntimeError: If failed to stop VPP or QAT failed to initialize. """ - cryptodev = Topology.get_cryptodev(node) + pci_addr = Topology.get_cryptodev(node) - # QAT device must be re-bound to kernel driver before initialization - driver = 'dh895xcc' - kernel_module = 'qat_dh895xcc' - current_driver = DUTSetup.get_pci_dev_driver( - node, cryptodev.replace(':', r'\:')) - - DUTSetup.kernel_module_verify(node, kernel_module, force_load=True) + # QAT device must be re-bound to kernel driver before initialization. + DUTSetup.verify_kernel_module(node, 'qat_dh895xcc', force_load=True) + # Stop VPP to prevent deadlock. VPPUtil.stop_vpp_service(node) + + current_driver = DUTSetup.get_pci_dev_driver( + node, pci_addr.replace(':', r'\:')) if current_driver is not None: - DUTSetup.pci_driver_unbind(node, cryptodev) - DUTSetup.pci_driver_bind(node, cryptodev, driver) + DUTSetup.pci_driver_unbind(node, pci_addr) - ssh = SSH() - ssh.connect(node) + # Bind to kernel driver. + DUTSetup.pci_driver_bind(node, pci_addr, 'dh895xcc') - # Initialize QAT VFs + # Initialize QAT VFs. if numvfs > 0: - cmd = 'echo "{0}" | tee /sys/bus/pci/devices/{1}/sriov_numvfs'.\ - format(numvfs, cryptodev.replace(':', r'\:'), timeout=180) - ret_code, _, _ = ssh.exec_command_sudo("sh -c '{0}'".format(cmd)) + DUTSetup.set_sriov_numvfs(node, pci_addr, numvfs) - if int(ret_code): - raise RuntimeError('Failed to initialize {0} VFs on QAT device ' - ' on host {1}'.format(numvfs, node['host'])) + @staticmethod + def get_virtfn_pci_addr(node, pf_pci_addr, vf_id): + """Get PCI address of Virtual Function. + + :param node: DUT node. + :param pf_pci_addr: Physical Function PCI address. + :param vf_id: Virtual Function number. + :type node: dict + :type pf_pci_addr: str + :type vf_id: int + :returns: Virtual Function PCI address. + :rtype: int + :raises RuntimeError: If failed to get Virtual Function PCI address. + """ + command = "sh -c "\ + "'basename $(readlink /sys/bus/pci/devices/{pci}/virtfn{vf_id})'".\ + format(pci=pf_pci_addr, vf_id=vf_id) + message = 'Failed to get virtual function PCI address.' + + stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True, + message=message) + + return stdout.strip() + + @staticmethod + def get_sriov_numvfs(node, pf_pci_addr): + """Get number of SR-IOV VFs. + + :param node: DUT node. + :param pf_pci_addr: Physical Function PCI device address. + :type node: dict + :type pf_pci_addr: str + :returns: Number of VFs. + :rtype: int + :raises RuntimeError: If PCI device is not SR-IOV capable. + """ + command = 'cat /sys/bus/pci/devices/{pci}/sriov_numvfs'.\ + format(pci=pf_pci_addr.replace(':', r'\:')) + message = 'PCI device {pci} is not a SR-IOV device.'.\ + format(pci=pf_pci_addr) + + for _ in range(3): + stdout, _ = exec_cmd_no_error(node, command, timeout=30, sudo=True, + message=message) + try: + sriov_numvfs = int(stdout) + except ValueError: + logger.trace('Reading sriov_numvfs info failed on {host}'. + format(host=node['host'])) + else: + return sriov_numvfs + + @staticmethod + def set_sriov_numvfs(node, pf_pci_addr, numvfs=0): + """Init or reset SR-IOV virtual functions by setting its number on PCI + device on DUT. Setting to zero removes all VFs. + + :param node: DUT node. + :param pf_pci_addr: Physical Function PCI device address. + :param numvfs: Number of VFs to initialize, 0 - removes the VFs. + :type node: dict + :type pf_pci_addr: str + :type numvfs: int + :raises RuntimeError: Failed to create VFs on PCI. + """ + command = "sh -c "\ + "'echo {num} | tee /sys/bus/pci/devices/{pci}/sriov_numvfs'".\ + format(num=numvfs, pci=pf_pci_addr.replace(':', r'\:')) + message = 'Failed to create {num} VFs on {pci} device on {host}'.\ + format(num=numvfs, pci=pf_pci_addr, host=node['host']) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) @staticmethod def pci_driver_unbind(node, pci_addr): @@ -340,20 +387,15 @@ class DUTSetup(object): :param pci_addr: PCI device address. :type node: dict :type pci_addr: str - :returns: nothing :raises RuntimeError: If PCI device unbind failed. """ + command = "sh -c "\ + "'echo {pci} | tee /sys/bus/pci/devices/{pcie}/driver/unbind'".\ + format(pci=pci_addr, pcie=pci_addr.replace(':', r'\:')) + message = 'Failed to unbind PCI device {pci} on {host}'.\ + format(pci=pci_addr, host=node['host']) - ssh = SSH() - ssh.connect(node) - - ret_code, _, _ = ssh.exec_command_sudo( - "sh -c 'echo {0} | tee /sys/bus/pci/devices/{1}/driver/unbind'" - .format(pci_addr, pci_addr.replace(':', r'\:')), timeout=180) - - if int(ret_code): - raise RuntimeError('Failed to unbind PCI device {0} from driver on ' - 'host {1}'.format(pci_addr, node['host'])) + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) @staticmethod def pci_driver_bind(node, pci_addr, driver): @@ -365,21 +407,92 @@ class DUTSetup(object): :type node: dict :type pci_addr: str :type driver: str - :returns: nothing :raises RuntimeError: If PCI device bind failed. """ + message = 'Failed to bind PCI device {pci} to {driver} on host {host}'.\ + format(pci=pci_addr, driver=driver, host=node['host']) - ssh = SSH() - ssh.connect(node) + command = "sh -c "\ + "'echo {driver} | tee /sys/bus/pci/devices/{pci}/driver_override'".\ + format(driver=driver, pci=pci_addr.replace(':', r'\:')) - ret_code, _, _ = ssh.exec_command_sudo( - "sh -c 'echo {0} | tee /sys/bus/pci/drivers/{1}/bind'".format( - pci_addr, driver), timeout=180) + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) - if int(ret_code): - raise RuntimeError('Failed to bind PCI device {0} to {1} driver on ' - 'host {2}'.format(pci_addr, driver, - node['host'])) + command = "sh -c "\ + "'echo {pci} | tee /sys/bus/pci/drivers/{driver}/bind'".\ + format(pci=pci_addr, driver=driver) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) + + command = "sh -c "\ + "'echo | tee /sys/bus/pci/devices/{pci}/driver_override'".\ + format(pci=pci_addr.replace(':', r'\:')) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) + + @staticmethod + def pci_vf_driver_unbind(node, pf_pci_addr, vf_id): + """Unbind Virtual Function from driver on node. + + :param node: DUT node. + :param pf_pci_addr: PCI device address. + :param vf_id: Virtual Function ID. + :type node: dict + :type pf_pci_addr: str + :type vf_id: int + :raises RuntimeError: If Virtual Function unbind failed. + """ + vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id) + vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\ + format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id) + + command = "sh -c "\ + "'echo {vf_pci_addr} | tee {vf_path}/driver/unbind'".\ + format(vf_pci_addr=vf_pci_addr, vf_path=vf_path) + + message = 'Failed to unbind VF {vf_pci_addr} to on {host}'.\ + format(vf_pci_addr=vf_pci_addr, host=node['host']) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) + + @staticmethod + def pci_vf_driver_bind(node, pf_pci_addr, vf_id, driver): + """Bind Virtual Function to driver on node. + + :param node: DUT node. + :param pf_pci_addr: PCI device address. + :param vf_id: Virtual Function ID. + :param driver: Driver to bind. + :type node: dict + :type pf_pci_addr: str + :type vf_id: int + :type driver: str + :raises RuntimeError: If PCI device bind failed. + """ + vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id) + vf_path = "/sys/bus/pci/devices/{pf_pci_addr}/virtfn{vf_id}".\ + format(pf_pci_addr=pf_pci_addr.replace(':', r'\:'), vf_id=vf_id) + + message = 'Failed to bind VF {vf_pci_addr} to {driver} on {host}'.\ + format(vf_pci_addr=vf_pci_addr, driver=driver, host=node['host']) + + command = "sh -c "\ + "'echo {driver} | tee {vf_path}/driver_override'".\ + format(driver=driver, vf_path=vf_path) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) + + command = "sh -c "\ + "'echo {vf_pci_addr} | tee /sys/bus/pci/drivers/{driver}/bind'".\ + format(vf_pci_addr=vf_pci_addr, driver=driver) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) + + command = "sh -c "\ + "'echo | tee {vf_path}/driver_override'".\ + format(vf_path=vf_path) + + exec_cmd_no_error(node, command, timeout=60, sudo=True, message=message) @staticmethod def get_pci_dev_driver(node, pci_addr): @@ -442,7 +555,7 @@ class DUTSetup(object): return None @staticmethod - def kernel_module_verify(node, module, force_load=False): + def verify_kernel_module(node, module, force_load=False): """Verify if kernel module is loaded on node. If parameter force load is set to True, then try to load the modules. @@ -454,22 +567,22 @@ class DUTSetup(object): :type force_load: bool :raises RuntimeError: If module is not loaded or failed to load. """ - ssh = SSH() - ssh.connect(node) + command = 'grep -w {module} /proc/modules'.format(module=module) + message = 'Kernel module {module} is not loaded on host {host}'.\ + format(module=module, host=node['host']) - cmd = 'grep -w {0} /proc/modules'.format(module) - ret_code, _, _ = ssh.exec_command(cmd) - - if int(ret_code): + try: + exec_cmd_no_error(node, command, timeout=30, sudo=False, + message=message) + except RuntimeError: if force_load: # Module is not loaded and we want to load it - DUTSetup.kernel_module_load(node, module) + DUTSetup.load_kernel_module(node, module) else: - raise RuntimeError('Kernel module {0} is not loaded on host ' - '{1}'.format(module, node['host'])) + raise @staticmethod - def kernel_module_verify_on_all_duts(nodes, module, force_load=False): + def verify_kernel_module_on_all_duts(nodes, module, force_load=False): """Verify if kernel module is loaded on all DUTs. If parameter force load is set to True, then try to load the modules. @@ -482,7 +595,7 @@ class DUTSetup(object): """ for node in nodes.values(): if node['type'] == NodeType.DUT: - DUTSetup.kernel_module_verify(node, module, force_load) + DUTSetup.verify_kernel_module(node, module, force_load) @staticmethod def verify_uio_driver_on_all_duts(nodes): @@ -495,10 +608,10 @@ class DUTSetup(object): for node in nodes.values(): if node['type'] == NodeType.DUT: uio_driver = Topology.get_uio_driver(node) - DUTSetup.kernel_module_verify(node, uio_driver, force_load=True) + DUTSetup.verify_kernel_module(node, uio_driver, force_load=True) @staticmethod - def kernel_module_load(node, module): + def load_kernel_module(node, module): """Load kernel module on node. :param node: DUT node. @@ -508,15 +621,11 @@ class DUTSetup(object): :returns: nothing :raises RuntimeError: If loading failed. """ + command = 'modprobe {module}'.format(module=module) + message = 'Failed to load {module} on host {host}'.\ + format(module=module, host=node['host']) - ssh = SSH() - ssh.connect(node) - - ret_code, _, _ = ssh.exec_command_sudo("modprobe {0}".format(module)) - - if int(ret_code): - raise RuntimeError('Failed to load {0} kernel module on host {1}'. - format(module, node['host'])) + exec_cmd_no_error(node, command, timeout=30, sudo=True, message=message) @staticmethod def vpp_enable_traces_on_all_duts(nodes): @@ -615,6 +724,21 @@ class DUTSetup(object): ssh.disconnect(node) @staticmethod + def verify_vpp_on_dut(node): + """Verify that VPP is installed on DUT node. + + :param node: DUT node. + :type node: dict + :raises RuntimeError: If failed to restart VPP, get VPP version + or get VPP interfaces. + """ + + logger.debug("Verify VPP on node {0}".format(node['host'])) + + DUTSetup.vpp_show_version_verbose(node) + DUTSetup.vpp_show_interfaces(node) + + @staticmethod def verify_vpp_on_all_duts(nodes): """Verify that VPP is installed on all DUT nodes. @@ -630,20 +754,6 @@ class DUTSetup(object): if node['type'] == NodeType.DUT: DUTSetup.verify_vpp_on_dut(node) - @staticmethod - def verify_vpp_on_dut(node): - """Verify that VPP is installed on DUT node. - - :param node: DUT node. - :type node: dict - :raises RuntimeError: If failed to restart VPP, get VPP version - or get VPP interfaces. - """ - - logger.debug("Verify VPP on node {0}".format(node['host'])) - - DUTSetup.vpp_show_version_verbose(node) - DUTSetup.vpp_show_interfaces(node) @staticmethod def get_huge_page_size(node): diff --git a/resources/libraries/python/InterfaceUtil.py b/resources/libraries/python/InterfaceUtil.py index e43935eb7b..878edd6fc2 100644 --- a/resources/libraries/python/InterfaceUtil.py +++ b/resources/libraries/python/InterfaceUtil.py @@ -24,6 +24,7 @@ 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.VPPUtil import VPPUtil from resources.libraries.python.parsers.JsonParser import JsonParser @@ -940,25 +941,28 @@ class InterfaceUtil(object): 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) + 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'])) + raise RuntimeError('Create bond interface failed on "{host}"'. + format(host=node['host'])) @staticmethod - def add_bond_eth_interface(node, ifc_name=None, sw_if_idx=None): - """Add BondEthernet interface to current topology. + def add_eth_interface(node, ifc_name=None, sw_if_idx=None, ifc_pfx=None): + """Add ethernet interface to current topology. :param node: DUT node from topology. - :param ifc_name: Name of the BondEthernet interface. + :param ifc_name: Name of the interface. :param sw_if_idx: SW interface index. + :param ifc_pfx: Interface key prefix. :type node: dict :type ifc_name: str :type sw_if_idx: int + :type ifc_pfx: str """ - if_key = Topology.add_new_port(node, 'eth_bond') + if_key = Topology.add_new_port(node, ifc_pfx) vat_executor = VatExecutor() vat_executor.execute_script_json_out("dump_interfaces.vat", node) @@ -976,6 +980,34 @@ class InterfaceUtil(object): Topology.update_interface_mac_address(node, if_key, ifc_mac) @staticmethod + def vpp_create_avf_interface(node, vf_pci_addr): + """Create AVF interface on VPP node. + + :param node: DUT node from topology. + :param vf_pci_addr: Virtual Function PCI address. + :type node: dict + :type vf_pci_addr: str + :returns: Interface key (name) in topology. + :rtype: str + :raises RuntimeError: If it is not possible to create AVF interface on + the node. + """ + 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) + 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'])) + + @staticmethod def vpp_enslave_physical_interface(node, interface, bond_interface): """Enslave physical interface to bond interface on VPP node. @@ -1209,21 +1241,141 @@ class InterfaceUtil(object): .format(node)) @staticmethod - def set_linux_interface_mac(node, interface, mac, namespace=None): + def set_linux_interface_mac(node, interface, mac, namespace=None, + vf_id=None): """Set MAC address for interface in linux. :param node: Node where to execute command. :param interface: Interface in namespace. :param mac: MAC to be assigned to interface. :param namespace: Execute command in namespace. Optional + :param vf_id: Virtual Function id. Optional :type node: dict :type interface: str :type mac: str :type namespace: str + :type vf_id: int """ - if namespace is not None: - cmd = 'ip netns exec {} ip link set {} address {}'.format( - namespace, interface, mac) - else: - cmd = 'ip link set {} address {}'.format(interface, mac) + mac_str = 'vf {vf_id} mac {mac}'.format(vf_id=vf_id, mac=mac) \ + if vf_id is not None else 'address {mac}'.format(mac=mac) + ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else '' + + cmd = ('{ns} ip link set {interface} {mac}'. + format(ns=ns_str, interface=interface, mac=mac_str)) exec_cmd_no_error(node, cmd, sudo=True) + + @staticmethod + def set_linux_interface_trust_on(node, interface, namespace=None, + vf_id=None): + """Set trust on (promisc) for interface in linux. + + :param node: Node where to execute command. + :param interface: Interface in namespace. + :param namespace: Execute command in namespace. Optional + :param vf_id: Virtual Function id. Optional + :type node: dict + :type interface: str + :type namespace: str + :type vf_id: int + """ + trust_str = 'vf {vf_id} trust on'.format(vf_id=vf_id) \ + if vf_id is not None else 'trust on' + ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else '' + + cmd = ('{ns} ip link set dev {interface} {trust}'. + format(ns=ns_str, interface=interface, trust=trust_str)) + exec_cmd_no_error(node, cmd, sudo=True) + + @staticmethod + def set_linux_interface_spoof_off(node, interface, namespace=None, + vf_id=None): + """Set spoof off for interface in linux. + + :param node: Node where to execute command. + :param interface: Interface in namespace. + :param namespace: Execute command in namespace. Optional + :param vf_id: Virtual Function id. Optional + :type node: dict + :type interface: str + :type namespace: str + :type vf_id: int + """ + spoof_str = 'vf {vf_id} spoof off'.format(vf_id=vf_id) \ + if vf_id is not None else 'spoof off' + ns_str = 'ip netns exec {ns}'.format(ns=namespace) if namespace else '' + + cmd = ('{ns} ip link set dev {interface} {spoof}'. + format(ns=ns_str, interface=interface, spoof=spoof_str)) + exec_cmd_no_error(node, cmd, sudo=True) + + @staticmethod + def init_avf_interface(node, ifc_key, numvfs=1, topology_type='L2'): + """Init PCI device by creating VFs and bind them to vfio-pci for AVF + driver testing on DUT. + + :param node: DUT node. + :param iface_key: Interface key from topology file. + :param numvfs: Number of VFs to initialize, 0 - disable the VFs. + :param topology_type: Topology type. + :type node: dict + :iface_key: str + :type numvfs: int + :typ topology_type: str + :returns: Virtual Function topology interface keys. + :rtype: list + """ + ssh = SSH() + ssh.connect(node) + + # Read PCI address and driver. + pf_pci_addr = Topology.get_interface_pci_addr(node, ifc_key) + pf_mac_addr = Topology.get_interface_mac(node, ifc_key).split(":") + uio_driver = Topology.get_uio_driver(node) + kernel_driver = Topology.get_interface_driver(node, ifc_key) + current_driver = DUTSetup.get_pci_dev_driver(node,\ + pf_pci_addr.replace(':', r'\:')) + + if current_driver != kernel_driver: + # PCI device must be re-bound to kernel driver before creating VFs. + DUTSetup.verify_kernel_module(node, kernel_driver, force_load=True) + # Stop VPP to prevent deadlock. + VPPUtil.stop_vpp_service(node) + # Unbind from current driver. + DUTSetup.pci_driver_unbind(node, pf_pci_addr) + # Bind to kernel driver. + DUTSetup.pci_driver_bind(node, pf_pci_addr, kernel_driver) + + # Initialize PCI VFs + DUTSetup.set_sriov_numvfs(node, pf_pci_addr, numvfs) + + vf_ifc_keys = [] + # Set MAC address and bind each virtual function to uio driver. + for vf_id in range(numvfs): + vf_mac_addr = ":".join([pf_mac_addr[0], pf_mac_addr[2], + pf_mac_addr[3], pf_mac_addr[4], + pf_mac_addr[5], "{:02x}".format(vf_id)]) + + pf_dev = '`basename /sys/bus/pci/devices/{pci}/net/*`'.\ + format(pci=pf_pci_addr) + InterfaceUtil.set_linux_interface_trust_on(node, pf_dev, + vf_id=vf_id) + if topology_type == 'L2': + InterfaceUtil.set_linux_interface_spoof_off(node, pf_dev, + vf_id=vf_id) + InterfaceUtil.set_linux_interface_mac(node, pf_dev, vf_mac_addr, + vf_id=vf_id) + + DUTSetup.pci_vf_driver_unbind(node, pf_pci_addr, vf_id) + DUTSetup.pci_vf_driver_bind(node, pf_pci_addr, vf_id, uio_driver) + + # Add newly created ports into topology file + vf_ifc_name = '{pf_if_key}_vf'.format(pf_if_key=ifc_key) + vf_pci_addr = DUTSetup.get_virtfn_pci_addr(node, pf_pci_addr, vf_id) + vf_ifc_key = Topology.add_new_port(node, vf_ifc_name) + Topology.update_interface_name(node, vf_ifc_key, + vf_ifc_name+str(vf_id+1)) + Topology.update_interface_mac_address(node, vf_ifc_key, vf_mac_addr) + Topology.update_interface_pci_address(node, vf_ifc_key, vf_pci_addr) + vf_ifc_keys.append(vf_ifc_key) + + return vf_ifc_keys diff --git a/resources/libraries/python/VppConfigGenerator.py b/resources/libraries/python/VppConfigGenerator.py index 8611219eed..822a4fdfdb 100644 --- a/resources/libraries/python/VppConfigGenerator.py +++ b/resources/libraries/python/VppConfigGenerator.py @@ -361,6 +361,11 @@ class VppConfigGenerator(object): path = ['dpdk', 'num-mbufs'] self.add_config_item(self._nodeconfig, value, path) + def add_dpdk_no_pci(self): + """Add DPDK no-pci.""" + path = ['dpdk', 'no-pci'] + self.add_config_item(self._nodeconfig, '', path) + def add_dpdk_uio_driver(self, value=None): """Add DPDK uio-driver configuration. diff --git a/resources/libraries/python/ssh.py b/resources/libraries/python/ssh.py index 06cd96010d..5e33a7cf9c 100644 --- a/resources/libraries/python/ssh.py +++ b/resources/libraries/python/ssh.py @@ -393,14 +393,29 @@ def exec_cmd(node, cmd, timeout=600, sudo=False): return ret_code, stdout, stderr -def exec_cmd_no_error(node, cmd, timeout=600, sudo=False): +def exec_cmd_no_error(node, cmd, timeout=600, sudo=False, message=None): """Convenience function to ssh/exec/return out & err. Verifies that return code is zero. - Returns (stdout, stderr). + :param node: DUT node. + :param cmd: Command to be executed. + :param timeout: Timeout value in seconds. Default: 600. + :param sudo: Sudo privilege execution flag. Default: False. + :param message: Error message in case of failure. Default: None. + :type node: dict + :type cmd: str + :type timeout: int + :type sudo: bool + :type message: str + :returns: Stdout, Stderr. + :rtype: tuple(str, str) + :raise RuntimeError: If bash return code is not 0. """ - (ret_code, stdout, stderr) = exec_cmd(node, cmd, timeout=timeout, sudo=sudo) - assert_equal(ret_code, 0, 'Command execution failed: "{}"\n{}'. - format(cmd, stderr)) + ret_code, stdout, stderr = exec_cmd(node, cmd, timeout=timeout, sudo=sudo) + msg = ('Command execution failed: "{cmd}"\n{stderr}'. + format(cmd=cmd, stderr=stderr) if message is None else message) + if ret_code != 0: + raise RuntimeError(msg) + return stdout, stderr diff --git a/resources/libraries/python/topology.py b/resources/libraries/python/topology.py index 82516beb6b..d31d17830c 100644 --- a/resources/libraries/python/topology.py +++ b/resources/libraries/python/topology.py @@ -185,6 +185,19 @@ class Topology(object): node['interfaces'][iface_key]['mac_address'] = str(mac_address) @staticmethod + def update_interface_pci_address(node, iface_key, pci_address): + """Update pci_address on the interface from the node. + + :param node: Node to update PCI on. + :param iface_key: Topology key of the interface. + :param pci_address: PCI address. + :type node: dict + :type iface_key: str + :type pci_address: str + """ + node['interfaces'][iface_key]['pci_address'] = str(pci_address) + + @staticmethod def update_interface_vhost_socket(node, iface_key, vhost_socket): """Update vhost socket name on the interface from the node. diff --git a/resources/libraries/robot/performance/performance_configuration.robot b/resources/libraries/robot/performance/performance_configuration.robot index cd522e753c..e6014b38f7 100644 --- a/resources/libraries/robot/performance/performance_configuration.robot +++ b/resources/libraries/robot/performance/performance_configuration.robot @@ -86,6 +86,24 @@ | | | ... | VPP Set Interface MTU | ${nodes['${dut}']} | ${${dut}_if2_2} | | All VPP Interfaces Ready Wait | ${nodes} +| Initialize AVF interfaces +| | [Documentation] +| | ... | Initialize AVF interfaces on each DUT. Interfaces are brought up. +| | ... +| | ${duts}= | Get Matches | ${nodes} | DUT* +| | :FOR | ${dut} | IN | @{duts} +| | | ${if1_pci}= | Get Interface PCI Addr | ${nodes['${dut}']} +| | | ... | ${${dut}_if1_vf0} +| | | ${if2_pci}= | Get Interface PCI Addr | ${nodes['${dut}']} +| | | ... | ${${dut}_if2_vf0} +| | | ${dut_eth_vf_if1}= | VPP Create AVF Interface | ${nodes['${dut}']} +| | | ... | ${if1_pci} +| | | ${dut_eth_vf_if2}= | VPP Create AVF Interface | ${nodes['${dut}']} +| | | ... | ${if2_pci} +| | | Set Test Variable | ${${dut}_if1} | ${dut_eth_vf_if1} +| | | Set Test Variable | ${${dut}_if2} | ${dut_eth_vf_if2} +| | Set interfaces in path up + | Initialize IPSec in 3-node circular topology | | [Documentation] | | ... | Set UP state on VPP interfaces in path on nodes in 3-node circular @@ -1929,8 +1947,8 @@ | | Run keyword | DUT2.Add DPDK Eth Bond Dev | 0 | 2 | l34 | ${dut2_if1_pci} | Add DPDK bonded ethernet interfaces to topology file in 3-node single link topology -| | Add Bond Eth Interface | ${dut1} | ${dut1_eth_bond_if1_name} -| | Add Bond Eth Interface | ${dut2} | ${dut2_eth_bond_if1_name} +| | Add Eth Interface | ${dut1} | ${dut1_eth_bond_if1_name} | ifc_pfx=eth_bond +| | Add Eth Interface | ${dut2} | ${dut2_eth_bond_if1_name} | ifc_pfx=eth_bond | Configure guest VM with dpdk-testpmd connected via vhost-user | | [Documentation] diff --git a/resources/libraries/robot/performance/performance_setup.robot b/resources/libraries/robot/performance/performance_setup.robot index 0a1eeec955..e9f155533e 100644 --- a/resources/libraries/robot/performance/performance_setup.robot +++ b/resources/libraries/robot/performance/performance_setup.robot @@ -437,6 +437,68 @@ | | Initialize DPDK Environment | ${dut1} | ${dut1_if1} | ${dut1_if2} | | Initialize DPDK Environment | ${dut2} | ${dut2_if1} | ${dut2_if2} +| Set up SRIOV 2-node performance topology with DUT's NIC model +| | [Documentation] +| | ... | Suite preparation phase that sets default startup configuration of +| | ... | VPP on all DUTs. Updates interfaces on all nodes and sets global +| | ... | variables used in test cases based on interface model provided as an +| | ... | argument. Initializes traffic generator. +| | ... | It configures PCI device with VFs on all DUTs. +| | ... +| | ... | *Arguments:* +| | ... | - topology_type - Topology type. Type: string +| | ... | - nic_model - Interface model. Type: string +| | ... | - vf_driver - Virtual function driver. Type: string +| | ... | - numvfs - Number of VFs. Type: integer +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Set up SRIOV 2-node performance topology with DUT's NIC model \ +| | ... | \| L2 \| Intel-X520-DA2 \| AVF \| +| | ... +| | [Arguments] | ${topology_type} | ${nic_model} | ${vf_driver} +| | ... | ${numvfs}=${1} +| | ... +| | Set variables in 2-node circular topology with DUT interface model +| | ... | ${nic_model} +| | Run Keyword If | '${vf_driver}' == 'AVF' +| | ... | Configure AVF interfaces on all DUTs | numvfs=${numvfs} +| | ... | topology_type=${topology_type} +| | Initialize traffic generator | ${tg} | ${tg_if1} | ${tg_if2} +| | ... | ${dut1} | ${dut1_if1_vf0} | ${dut1} | ${dut1_if2_vf0} +| | ... | ${topology_type} + +| Set up SRIOV 3-node performance topology with DUT's NIC model +| | [Documentation] +| | ... | Suite preparation phase that sets default startup configuration of +| | ... | VPP on all DUTs. Updates interfaces on all nodes and sets global +| | ... | variables used in test cases based on interface model provided as an +| | ... | argument. Initializes traffic generator. +| | ... | It configures PCI device with VFs on all DUTs. +| | ... +| | ... | *Arguments:* +| | ... | - topology_type - Topology type. Type: string +| | ... | - nic_model - Interface model. Type: string +| | ... | - vf_driver - Virtual function driver. Type: string +| | ... | - numvfs - Number of VFs. Type: integer +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Set up SRIOV 3-node performance topology with DUT's NIC model \ +| | ... | \| L2 \| Intel-X520-DA2 \| AVF \| +| | ... +| | [Arguments] | ${topology_type} | ${nic_model} | ${vf_driver} +| | ... | ${numvfs}=${1} +| | ... +| | Set variables in 3-node circular topology with DUT interface model +| | ... | ${nic_model} +| | Run Keyword If | '${vf_driver}' == 'AVF' +| | ... | Configure AVF interfaces on all DUTs | numvfs=${numvfs} +| | ... | topology_type=${topology_type} +| | Initialize traffic generator | ${tg} | ${tg_if1} | ${tg_if2} +| | ... | ${dut1} | ${dut1_if1_vf0} | ${dut2} | ${dut2_if2_vf0} +| | ... | ${topology_type} + | Set up IPSec performance test suite | | [Documentation] | | ... | Suite preparation phase that sets default startup configuration of @@ -499,6 +561,13 @@ | | Set Suite Variable | @{plugins_to_enable} | | Append To List | ${plugins_to_enable} | acl_plugin.so +| Set up performance test suite with AVF driver +| | [Documentation] +| | ... | Append avf_plugin.so to the list of enabled plugins. +| | ... +| | Set Suite Variable | @{plugins_to_enable} +| | Append To List | ${plugins_to_enable} | avf_plugin.so + | Set up performance test suite with Static SRv6 proxy | | [Documentation] | | ... | Append srv6as_plugin.so to the list of enabled plugins. diff --git a/resources/libraries/robot/shared/default.robot b/resources/libraries/robot/shared/default.robot index ecce5762b8..f734516005 100644 --- a/resources/libraries/robot/shared/default.robot +++ b/resources/libraries/robot/shared/default.robot @@ -91,9 +91,35 @@ | | | Crypto Device Verify | ${nodes['${dut}']} | force_init=${force_init} | | | ... | numvfs=${numvfs} +| Configure AVF interfaces on all DUTs +| | [Documentation] | Configure virtual functions for AVF interfaces on PCI +| | ... | interface on all DUTs. +| | ... +| | ... | *Arguments:* +| | ... | - numvfs - Number of VFs to initialize, 0 - disable the VFs +| | ... | (Optional). Type: integer, default value: ${1} +| | ... | - topology_type - Topology type. +| | ... | (Optional). Type: string, default value: L2 +| | ... +| | ... | *Example:* +| | ... +| | ... | \| Configure AVF device on all DUTs \| ${1} \| L2 \| +| | ... +| | [Arguments] | ${numvfs}=${1} | ${topology_type}=L2 +| | ... +| | ${duts}= | Get Matches | ${nodes} | DUT* +| | :FOR | ${dut} | IN | @{duts} +| | | ${if1_avf_arr}= | Init AVF interface | ${nodes['${dut}']} | ${${dut}_if1} +| | | ... | numvfs=${numvfs} | topology_type=${topology_type} +| | | ${if2_avf_arr}= | Init AVF interface | ${nodes['${dut}']} | ${${dut}_if2} +| | | ... | numvfs=${numvfs} | topology_type=${topology_type} +# Currently only one AVF is supported. +| | | Set Suite Variable | ${${dut}_if1_vf0} | ${if1_avf_arr[0]} +| | | Set Suite Variable | ${${dut}_if2_vf0} | ${if2_avf_arr[0]} + | Configure kernel module on all DUTs | | [Documentation] | Verify if specific kernel module is loaded on all DUTs. -| | ... | If parameter force_load is set to True, then try to initialize. +| | ... | If parameter force_load is set to True, then try to load. | | ... | | ... | *Arguments:* | | ... | - module - Module to verify. Type: string @@ -105,10 +131,8 @@ | | ... | | [Arguments] | ${module} | ${force_load}=${False} | | ... -| | ${duts}= | Get Matches | ${nodes} | DUT* -| | :FOR | ${dut} | IN | @{duts} -| | | Kernel Module Verify | ${nodes['${dut}']} | ${module} -| | | ... | force_load=${force_load} +| | Verify Kernel Module on All DUTs | ${nodes} | ${module} +| | ... | force_load=${force_load} | Create base startup configuration of VPP on all DUTs | | [Documentation] | Create base startup configuration of VPP to all DUTs. @@ -270,6 +294,13 @@ | | :FOR | ${dut} | IN | @{duts} | | | Run keyword | ${dut}.Add DPDK No Multi Seg +| Add DPDK no PCI to all DUTs +| | [Documentation] | Add DPDK no-pci to VPP startup configuration to all DUTs. +| | ... +| | ${duts}= | Get Matches | ${nodes} | DUT* +| | :FOR | ${dut} | IN | @{duts} +| | | Run keyword | ${dut}.Add DPDK no PCI + | Add DPDK dev default RXD to all DUTs | | [Documentation] | Add DPDK num-rx-desc to VPP startup configuration to all | | ... | DUTs. diff --git a/resources/templates/vat/create_avf_interface.vat b/resources/templates/vat/create_avf_interface.vat new file mode 100644 index 0000000000..b7ce79a98f --- /dev/null +++ b/resources/templates/vat/create_avf_interface.vat @@ -0,0 +1 @@ +avf_create {vf_pci_addr} |