CSIT-1205 Create AVF driver test 87/13587/47
authorPeter Mikus <pmikus@cisco.com>
Fri, 20 Jul 2018 13:07:12 +0000 (13:07 +0000)
committerPeter Mikus <pmikus@cisco.com>
Wed, 5 Sep 2018 13:49:38 +0000 (13:49 +0000)
- 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>
13 files changed:
resources/libraries/python/DUTSetup.py
resources/libraries/python/InterfaceUtil.py
resources/libraries/python/VppConfigGenerator.py
resources/libraries/python/ssh.py
resources/libraries/python/topology.py
resources/libraries/robot/performance/performance_configuration.robot
resources/libraries/robot/performance/performance_setup.robot
resources/libraries/robot/shared/default.robot
resources/templates/vat/create_avf_interface.vat [new file with mode: 0644]
tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-mrr.robot [new file with mode: 0644]
tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-ndrpdr.robot [new file with mode: 0644]
tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-mrr.robot [new file with mode: 0644]
tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-ndrpdr.robot [new file with mode: 0644]

index 8f9e94d..84862d4 100644 (file)
@@ -16,7 +16,7 @@
 from robot.api import logger
 
 from resources.libraries.python.topology import NodeType, Topology
 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
 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
     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.
 
         :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
         :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):
 
     @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.
         """
         :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)
         VPPUtil.stop_vpp_service(node)
+
+        current_driver = DUTSetup.get_pci_dev_driver(
+            node, pci_addr.replace(':', r'\:'))
         if current_driver is not None:
         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:
         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):
 
     @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
         :param pci_addr: PCI device address.
         :type node: dict
         :type pci_addr: str
-        :returns: nothing
         :raises RuntimeError: If PCI device unbind failed.
         """
         :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):
 
     @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
         :type node: dict
         :type pci_addr: str
         :type driver: str
-        :returns: nothing
         :raises RuntimeError: If PCI device bind failed.
         """
         :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):
 
     @staticmethod
     def get_pci_dev_driver(node, pci_addr):
@@ -442,7 +555,7 @@ class DUTSetup(object):
         return None
 
     @staticmethod
         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.
 
         """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.
         """
         :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
             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:
             else:
-                raise RuntimeError('Kernel module {0} is not loaded on host '
-                                   '{1}'.format(module, node['host']))
+                raise
 
     @staticmethod
 
     @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.
 
         """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:
         """
         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):
 
     @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)
         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
 
     @staticmethod
-    def kernel_module_load(node, module):
+    def load_kernel_module(node, module):
         """Load kernel module on node.
 
         :param node: DUT node.
         """Load kernel module on node.
 
         :param node: DUT node.
@@ -508,15 +621,11 @@ class DUTSetup(object):
         :returns: nothing
         :raises RuntimeError: If loading failed.
         """
         :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):
 
     @staticmethod
     def vpp_enable_traces_on_all_duts(nodes):
@@ -614,6 +723,21 @@ class DUTSetup(object):
 
                 ssh.disconnect(node)
 
 
                 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.
     @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)
 
             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):
 
     @staticmethod
     def get_huge_page_size(node):
index e43935e..878edd6 100644 (file)
@@ -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.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
 
 
 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')
 
         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:
             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
 
     @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 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 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 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)
 
         vat_executor = VatExecutor()
         vat_executor.execute_script_json_out("dump_interfaces.vat", node)
@@ -975,6 +979,34 @@ class InterfaceUtil(object):
             interface_dump_json, sw_if_idx)
         Topology.update_interface_mac_address(node, if_key, ifc_mac)
 
             interface_dump_json, sw_if_idx)
         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.
     @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
                                .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
         """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 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)
         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
index 8611219..822a4fd 100644 (file)
@@ -361,6 +361,11 @@ class VppConfigGenerator(object):
         path = ['dpdk', 'num-mbufs']
         self.add_config_item(self._nodeconfig, value, path)
 
         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.
 
     def add_dpdk_uio_driver(self, value=None):
         """Add DPDK uio-driver configuration.
 
index 06cd960..5e33a7c 100644 (file)
@@ -393,14 +393,29 @@ def exec_cmd(node, cmd, timeout=600, sudo=False):
     return ret_code, stdout, stderr
 
 
     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.
 
     """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
     return stdout, stderr
index 82516be..d31d178 100644 (file)
@@ -184,6 +184,19 @@ class Topology(object):
         """
         node['interfaces'][iface_key]['mac_address'] = str(mac_address)
 
         """
         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.
     @staticmethod
     def update_interface_vhost_socket(node, iface_key, vhost_socket):
         """Update vhost socket name on the interface from the node.
index cd522e7..e6014b3 100644 (file)
 | | | ... | VPP Set Interface MTU | ${nodes['${dut}']} | ${${dut}_if2_2}
 | | All VPP Interfaces Ready Wait | ${nodes}
 
 | | | ... | 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
 | Initialize IPSec in 3-node circular topology
 | | [Documentation]
 | | ... | Set UP state on VPP interfaces in path on nodes in 3-node circular
 | | 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
 | | 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]
 
 | Configure guest VM with dpdk-testpmd connected via vhost-user
 | | [Documentation]
index 0a1eeec..e9f1555 100644 (file)
 | | Initialize DPDK Environment | ${dut1} | ${dut1_if1} | ${dut1_if2}
 | | Initialize DPDK Environment | ${dut2} | ${dut2_if1} | ${dut2_if2}
 
 | | 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
 | Set up IPSec performance test suite
 | | [Documentation]
 | | ... | Suite preparation phase that sets default startup configuration of
 | | Set Suite Variable | @{plugins_to_enable}
 | | Append To List | ${plugins_to_enable} | acl_plugin.so
 
 | | 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.
 | Set up performance test suite with Static SRv6 proxy
 | | [Documentation]
 | | ... | Append srv6as_plugin.so to the list of enabled plugins.
index ecce576..f734516 100644 (file)
 | | | Crypto Device Verify | ${nodes['${dut}']} | force_init=${force_init}
 | | | ... | numvfs=${numvfs}
 
 | | | 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.
 | 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
 | | ...
 | | ... | *Arguments:*
 | | ... | - module - Module to verify. Type: string
 | | ...
 | | [Arguments] | ${module} | ${force_load}=${False}
 | | ...
 | | ...
 | | [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.
 
 | Create base startup configuration of VPP on all DUTs
 | | [Documentation] | Create base startup configuration of VPP to all DUTs.
 | | :FOR | ${dut} | IN | @{duts}
 | | | Run keyword | ${dut}.Add DPDK No Multi Seg
 
 | | :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.
 | 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 (file)
index 0000000..b7ce79a
--- /dev/null
@@ -0,0 +1 @@
+avf_create {vf_pci_addr}
diff --git a/tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-mrr.robot b/tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-mrr.robot
new file mode 100644 (file)
index 0000000..8d1ee9a
--- /dev/null
@@ -0,0 +1,116 @@
+# 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:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Resource | resources/libraries/robot/performance/performance_setup.robot
+| ...
+| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | MRR
+| ... | NIC_Intel-X710 | ETH | L2PATCH | BASE | DRV_AVF
+| ...
+| Suite Setup | Run Keywords
+| ... | Set up SRIOV 3-node performance topology with DUT's NIC model
+| ... | L2 | Intel-X710 | AVF
+| ... | AND | Set up performance test suite with AVF driver
+| ...
+| Suite Teardown | Tear down 3-node performance topology
+| ...
+| Test Setup | Set up performance test
+| ...
+| Test Teardown | Tear down performance mrr test
+| ...
+| Test Template | Local template
+| ...
+| Documentation | *Raw results L2patch test cases*
+| ...
+| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
+| ... | with single links between nodes.
+| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 patch.
+| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with L2 patch\
+| ... | DUT1 and DUT2 tested with 2p10GE NIC X710 by Intel with VF enabled.
+| ... | *[Ver] TG verification:* In MaxReceivedRate tests TG sends traffic\
+| ... | at line rate and reports total received/sent packets over trial period.
+| ... | Test packets are generated by TG on
+| ... | links to DUTs. TG traffic profile contains two L3 flow-groups
+| ... | (flow-group per direction, 254 flows per flow-group) with all packets
+| ... | containing Ethernet header, IPv4 header with IP protocol=61 and static
+| ... | payload. MAC addresses are matching MAC addresses of the TG node
+| ... | interfaces.
+
+*** Variables ***
+# X710-DA2 bandwidth limit
+| ${s_limit}= | ${10000000000}
+# Traffic profile:
+| ${traffic_profile}= | trex-sl-3n-ethip4-ip4src254
+
+*** Keywords ***
+| Local template
+| | [Documentation]
+| | ... | [Cfg] DUT runs L2 patch config with ${phy_cores} phy
+| | ... | core(s).
+| | ... | [Ver] Measure MaxReceivedRate for ${framesize}B frames using single\
+| | ... | trial throughput test.
+| | ...
+| | ... | *Arguments:*
+| | ... | - framesize - Framesize in Bytes in integer or string (IMIX_v4_1).
+| | ... | Type: integer, string
+| | ... | - phy_cores - Number of physical cores. Type: integer
+| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
+| | ...
+| | [Arguments] | ${framesize} | ${phy_cores} | ${rxq}=${None}
+| | ...
+| | Given Add worker threads and rxqueues to all DUTs | ${phy_cores} | ${rxq}
+| | And Add DPDK no PCI to all DUTs
+| | ${max_rate} | ${jumbo} = | Get Max Rate And Jumbo
+| | ... | ${s_limit} | ${framesize}
+| | And Apply startup configuration on all VPP DUTs
+| | When Initialize AVF interfaces
+| | And Initialize L2 patch
+| | Then Traffic should pass with maximum rate
+| | ... | ${max_rate}pps | ${framesize} | ${traffic_profile}
+
+*** Test Cases ***
+| tc01-64B-1c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 1C
+| | framesize=${64} | phy_cores=${1}
+
+| tc02-1518B-1c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 1C
+| | framesize=${1518} | phy_cores=${1}
+
+| tc04-IMIX-1c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 1C
+| | framesize=IMIX_v4_1 | phy_cores=${1}
+
+| tc05-64B-2c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 2C
+| | framesize=${64} | phy_cores=${2}
+
+| tc06-1518B-2c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 2C
+| | framesize=${1518} | phy_cores=${2}
+
+| tc08-IMIX-2c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 2C
+| | framesize=IMIX_v4_1 | phy_cores=${2}
+
+| tc09-64B-4c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 4C
+| | framesize=${64} | phy_cores=${4}
+
+| tc10-1518B-4c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 4C
+| | framesize=${1518} | phy_cores=${4}
+
+| tc12-IMIX-4c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 4C
+| | framesize=IMIX_v4_1 | phy_cores=${4}
diff --git a/tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-ndrpdr.robot b/tests/vpp/perf/l2/10ge2p1x710-avf-eth-l2patch-ndrpdr.robot
new file mode 100644 (file)
index 0000000..7fc0ec9
--- /dev/null
@@ -0,0 +1,123 @@
+# 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:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Resource | resources/libraries/robot/performance/performance_setup.robot
+| ...
+| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | NDRPDR
+| ... | NIC_Intel-X710 | ETH | L2PATCH | BASE | DRV_AVF
+| ...
+| Suite Setup | Run Keywords
+| ... | Set up SRIOV 3-node performance topology with DUT's NIC model
+| ... | L2 | Intel-X710 | AVF
+| ... | AND | Set up performance test suite with AVF driver
+| ...
+| Suite Teardown | Tear down 3-node performance topology
+| ...
+| Test Setup | Set up performance test
+| ...
+| Test Teardown | Tear down performance discovery test | ${min_rate}pps
+| ... | ${framesize} | ${traffic_profile}
+| ...
+| Test Template | Local template
+| ...
+| Documentation | *RFC2544: Pkt throughput L2patch test cases*
+| ...
+| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
+| ... | with single links between nodes.
+| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 patch.
+| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with L2 patch\
+| ... | DUT1 and DUT2 tested with 2p10GE NIC X710 by Intel with VF enabled.
+| ... | *[Ver] TG verification:* TG finds and reports throughput NDR (Non Drop\
+| ... | Rate) with zero packet loss tolerance or throughput PDR (Partial Drop\
+| ... | Rate) with non-zero packet loss tolerance (LT) expressed in percentage\
+| ... | of packets transmitted. NDR and PDR are discovered for different\
+| ... | Ethernet L2 frame sizes using MLRsearch library\
+| ... | Test packets are generated by TG on
+| ... | links to DUTs. TG traffic profile contains two L3 flow-groups
+| ... | (flow-group per direction, 254 flows per flow-group) with all packets
+| ... | containing Ethernet header, IPv4 header with IP protocol=61 and static
+| ... | payload. MAC addresses are matching MAC addresses of the TG node
+| ... | interfaces.
+| ... | *[Ref] Applicable standard specifications:* RFC2544.
+
+*** Variables ***
+# X710-DA2 bandwidth limit
+| ${s_limit}= | ${10000000000}
+# Traffic profile:
+| ${traffic_profile}= | trex-sl-3n-ethip4-ip4src254
+
+*** Keywords ***
+| Local template
+| | [Documentation]
+| | ... | [Cfg] DUT runs L2 patch config with ${phy_cores} phy
+| | ... | core(s).
+| | ... | [Ver] Measure NDR and PDR values using MLRsearch algorithm.
+| | ...
+| | ... | *Arguments:*
+| | ... | - framesize - Framesize in Bytes in integer or string (IMIX_v4_1).
+| | ... | Type: integer, string
+| | ... | - phy_cores - Number of physical cores. Type: integer
+| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
+| | ...
+| | [Arguments] | ${framesize} | ${phy_cores} | ${rxq}=${None}
+| | ...
+| | Set Test Variable | ${framesize}
+| | Set Test Variable | ${min_rate} | ${20000}
+| | ...
+| | Given Add worker threads and rxqueues to all DUTs | ${phy_cores} | ${rxq}
+| | And Add DPDK no PCI to all DUTs
+| | ${max_rate} | ${jumbo} = | Get Max Rate And Jumbo
+| | ... | ${s_limit} | ${framesize}
+| | And Apply startup configuration on all VPP DUTs
+| | When Initialize AVF interfaces
+| | And Initialize L2 patch
+| | Then Find NDR and PDR intervals using optimized search
+| | ... | ${framesize} | ${traffic_profile} | ${min_rate} | ${max_rate}
+
+*** Test Cases ***
+| tc01-64B-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 1C
+| | framesize=${64} | phy_cores=${1}
+
+| tc02-1518B-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 1C
+| | framesize=${1518} | phy_cores=${1}
+
+| tc04-IMIX-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 1C
+| | framesize=IMIX_v4_1 | phy_cores=${1}
+
+| tc05-64B-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 2C
+| | framesize=${64} | phy_cores=${2}
+
+| tc06-1518B-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 2C
+| | framesize=${1518} | phy_cores=${2}
+
+| tc08-IMIX-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 2C
+| | framesize=IMIX_v4_1 | phy_cores=${2}
+
+| tc09-64B-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 4C
+| | framesize=${64} | phy_cores=${4}
+
+| tc10-1518B-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 4C
+| | framesize=${1518} | phy_cores=${4}
+
+| tc12-IMIX-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 4C
+| | framesize=IMIX_v4_1 | phy_cores=${4}
diff --git a/tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-mrr.robot b/tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-mrr.robot
new file mode 100644 (file)
index 0000000..73eb12f
--- /dev/null
@@ -0,0 +1,118 @@
+# 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:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Resource | resources/libraries/robot/performance/performance_setup.robot
+| ...
+| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | MRR
+| ... | NIC_Intel-XXV710 | ETH | L2PATCH | BASE | DRV_AVF
+| ...
+| Suite Setup | Run Keywords
+| ... | Set up SRIOV 3-node performance topology with DUT's NIC model
+| ... | L2 | Intel-XXV710 | AVF
+| ... | AND | Set up performance test suite with AVF driver
+| ...
+| Suite Teardown | Tear down 3-node performance topology
+| ...
+| Test Setup | Set up performance test
+| ...
+| Test Teardown | Tear down performance mrr test
+| ...
+| Test Template | Local template
+| ...
+| Documentation | *Raw results L2patch test cases*
+| ...
+| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
+| ... | with single links between nodes.
+| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 patch.
+| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with L2 patch\
+| ... | DUT1 and DUT2 tested with 2p25GE NIC XXV710 by Intel with VF enabled.
+| ... | *[Ver] TG verification:* In MaxReceivedRate tests TG sends traffic\
+| ... | at line rate and reports total received/sent packets over trial period.
+| ... | Test packets are generated by TG on
+| ... | links to DUTs. TG traffic profile contains two L3 flow-groups
+| ... | (flow-group per direction, 254 flows per flow-group) with all packets
+| ... | containing Ethernet header, IPv4 header with IP protocol=61 and static
+| ... | payload. MAC addresses are matching MAC addresses of the TG node
+| ... | interfaces.
+
+*** Variables ***
+# XXV710-DA2 bandwidth limit ~50Gbps/2=25Gbps
+| ${s_25G}= | ${25000000000}
+# XXV710-DA2 Mpps limit 37.5Mpps/2=18.75Mpps
+| ${s_18.75Mpps}= | ${18750000}
+# Traffic profile:
+| ${traffic_profile}= | trex-sl-3n-ethip4-ip4src254
+
+*** Keywords ***
+| Local template
+| | [Documentation]
+| | ... | [Cfg] DUT runs L2 patch config with ${phy_cores} phy
+| | ... | core(s).
+| | ... | [Ver] Measure MaxReceivedRate for ${framesize}B frames using single\
+| | ... | trial throughput test.
+| | ...
+| | ... | *Arguments:*
+| | ... | - framesize - Framesize in Bytes in integer or string (IMIX_v4_1).
+| | ... | Type: integer, string
+| | ... | - phy_cores - Number of physical cores. Type: integer
+| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
+| | ...
+| | [Arguments] | ${framesize} | ${phy_cores} | ${rxq}=${None}
+| | ...
+| | Given Add worker threads and rxqueues to all DUTs | ${phy_cores} | ${rxq}
+| | And Add DPDK no PCI to all DUTs
+| | ${max_rate} | ${jumbo} = | Get Max Rate And Jumbo
+| | ... | ${s_25G} | ${framesize} | pps_limit=${s_18.75Mpps}
+| | And Apply startup configuration on all VPP DUTs
+| | When Initialize AVF interfaces
+| | And Initialize L2 patch
+| | Then Traffic should pass with maximum rate
+| | ... | ${max_rate}pps | ${framesize} | ${traffic_profile}
+
+*** Test Cases ***
+| tc01-64B-1c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 1C
+| | framesize=${64} | phy_cores=${1}
+
+| tc02-1518B-1c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 1C
+| | framesize=${1518} | phy_cores=${1}
+
+| tc04-IMIX-1c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 1C
+| | framesize=IMIX_v4_1 | phy_cores=${1}
+
+| tc05-64B-2c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 2C
+| | framesize=${64} | phy_cores=${2}
+
+| tc06-1518B-2c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 2C
+| | framesize=${1518} | phy_cores=${2}
+
+| tc08-IMIX-2c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 2C
+| | framesize=IMIX_v4_1 | phy_cores=${2}
+
+| tc09-64B-4c-avf-eth-l2patch-mrr
+| | [Tags] | 64B | 4C
+| | framesize=${64} | phy_cores=${4}
+
+| tc10-1518B-4c-avf-eth-l2patch-mrr
+| | [Tags] | 1518B | 4C
+| | framesize=${1518} | phy_cores=${4}
+
+| tc12-IMIX-4c-avf-eth-l2patch-mrr
+| | [Tags] | IMIX | 4C
+| | framesize=IMIX_v4_1 | phy_cores=${4}
diff --git a/tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-ndrpdr.robot b/tests/vpp/perf/l2/25ge2p1xxv710-avf-eth-l2patch-ndrpdr.robot
new file mode 100644 (file)
index 0000000..f03a24d
--- /dev/null
@@ -0,0 +1,125 @@
+# 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:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+*** Settings ***
+| Resource | resources/libraries/robot/performance/performance_setup.robot
+| ...
+| Force Tags | 3_NODE_SINGLE_LINK_TOPO | PERFTEST | HW_ENV | NDRPDR
+| ... | NIC_Intel-XXV710 | ETH | L2PATCH | BASE | DRV_AVF
+| ...
+| Suite Setup | Run Keywords
+| ... | Set up SRIOV 3-node performance topology with DUT's NIC model
+| ... | L2 | Intel-XXV710 | AVF
+| ... | AND | Set up performance test suite with AVF driver
+| ...
+| Suite Teardown | Tear down 3-node performance topology
+| ...
+| Test Setup | Set up performance test
+| ...
+| Test Teardown | Tear down performance discovery test | ${min_rate}pps
+| ... | ${framesize} | ${traffic_profile}
+| ...
+| Test Template | Local template
+| ...
+| Documentation | *RFC2544: Pkt throughput L2patch test cases*
+| ...
+| ... | *[Top] Network Topologies:* TG-DUT1-DUT2-TG 3-node circular topology
+| ... | with single links between nodes.
+| ... | *[Enc] Packet Encapsulations:* Eth-IPv4 for L2 patch.
+| ... | *[Cfg] DUT configuration:* DUT1 and DUT2 are configured with L2 patch\
+| ... | DUT1 and DUT2 tested with 2p25GE NIC XXV710 by Intel with VF enabled.
+| ... | *[Ver] TG verification:* TG finds and reports throughput NDR (Non Drop\
+| ... | Rate) with zero packet loss tolerance or throughput PDR (Partial Drop\
+| ... | Rate) with non-zero packet loss tolerance (LT) expressed in percentage\
+| ... | of packets transmitted. NDR and PDR are discovered for different\
+| ... | Ethernet L2 frame sizes using MLRsearch library\
+| ... | Test packets are generated by TG on
+| ... | links to DUTs. TG traffic profile contains two L3 flow-groups
+| ... | (flow-group per direction, 254 flows per flow-group) with all packets
+| ... | containing Ethernet header, IPv4 header with IP protocol=61 and static
+| ... | payload. MAC addresses are matching MAC addresses of the TG node
+| ... | interfaces.
+| ... | *[Ref] Applicable standard specifications:* RFC2544.
+
+*** Variables ***
+# XXV710-DA2 bandwidth limit ~50Gbps/2=25Gbps
+| ${s_25G}= | ${25000000000}
+# XXV710-DA2 Mpps limit 37.5Mpps/2=18.75Mpps
+| ${s_18.75Mpps}= | ${18750000}
+# Traffic profile:
+| ${traffic_profile}= | trex-sl-3n-ethip4-ip4src254
+
+*** Keywords ***
+| Local template
+| | [Documentation]
+| | ... | [Cfg] DUT runs L2 patch config with ${phy_cores} phy
+| | ... | core(s).
+| | ... | [Ver] Measure NDR and PDR values using MLRsearch algorithm.
+| | ...
+| | ... | *Arguments:*
+| | ... | - framesize - Framesize in Bytes in integer or string (IMIX_v4_1).
+| | ... | Type: integer, string
+| | ... | - phy_cores - Number of physical cores. Type: integer
+| | ... | - rxq - Number of RX queues, default value: ${None}. Type: integer
+| | ...
+| | [Arguments] | ${framesize} | ${phy_cores} | ${rxq}=${None}
+| | ...
+| | Set Test Variable | ${framesize}
+| | Set Test Variable | ${min_rate} | ${20000}
+| | ...
+| | Given Add worker threads and rxqueues to all DUTs | ${phy_cores} | ${rxq}
+| | And Add DPDK no PCI to all DUTs
+| | ${max_rate} | ${jumbo} = | Get Max Rate And Jumbo
+| | ... | ${s_25G} | ${framesize} | pps_limit=${s_18.75Mpps}
+| | And Apply startup configuration on all VPP DUTs
+| | When Initialize AVF interfaces
+| | And Initialize L2 patch
+| | Then Find NDR and PDR intervals using optimized search
+| | ... | ${framesize} | ${traffic_profile} | ${min_rate} | ${max_rate}
+
+*** Test Cases ***
+| tc01-64B-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 1C
+| | framesize=${64} | phy_cores=${1}
+
+| tc02-1518B-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 1C
+| | framesize=${1518} | phy_cores=${1}
+
+| tc04-IMIX-1c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 1C
+| | framesize=IMIX_v4_1 | phy_cores=${1}
+
+| tc05-64B-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 2C
+| | framesize=${64} | phy_cores=${2}
+
+| tc06-1518B-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 2C
+| | framesize=${1518} | phy_cores=${2}
+
+| tc08-IMIX-2c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 2C
+| | framesize=IMIX_v4_1 | phy_cores=${2}
+
+| tc09-64B-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 64B | 4C
+| | framesize=${64} | phy_cores=${4}
+
+| tc10-1518B-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | 1518B | 4C
+| | framesize=${1518} | phy_cores=${4}
+
+| tc12-IMIX-4c-avf-eth-l2patch-ndrpdr
+| | [Tags] | IMIX | 4C
+| | framesize=IMIX_v4_1 | phy_cores=${4}