aboutsummaryrefslogtreecommitdiffstats
path: root/resources
diff options
context:
space:
mode:
authorPeter Mikus <pmikus@cisco.com>2018-07-20 13:07:12 +0000
committerPeter Mikus <pmikus@cisco.com>2018-09-05 13:49:38 +0000
commit89620b26d6bbf3dd9d9707851d8d140471d40b56 (patch)
treecd22606feabb7190d6a612b46e8132fcda1d0f04 /resources
parentb456176a07ae1a3721407693d75931db6b1cd14e (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.py324
-rw-r--r--resources/libraries/python/InterfaceUtil.py178
-rw-r--r--resources/libraries/python/VppConfigGenerator.py5
-rw-r--r--resources/libraries/python/ssh.py25
-rw-r--r--resources/libraries/python/topology.py13
-rw-r--r--resources/libraries/robot/performance/performance_configuration.robot22
-rw-r--r--resources/libraries/robot/performance/performance_setup.robot69
-rw-r--r--resources/libraries/robot/shared/default.robot41
-rw-r--r--resources/templates/vat/create_avf_interface.vat1
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}