aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Klozik <martinx.klozik@intel.com>2016-03-18 10:40:42 +0000
committerMartin Klozik <martinx.klozik@intel.com>2016-04-14 08:23:50 +0100
commit55db32610210f3163971557382e653be6667e333 (patch)
tree139c4a7d631e34b05e89c88ac446dc51c33fd613
parent0c0d7c2fa564bd9ab2e7da40e7bd009b1e7a8650 (diff)
sriov: Support of SRIOV and Qemu PCI passthrough
Generic support of SRIOV has been added. Virtual interfaces can be used in multiplei scenarios instead of physical NICs. Virtual functions can be directly accessed from VM by PCI passthrough method. Another option is to use VFs with vSwtich to evaluate impact on performance. Additonal modifications: * Automatic detection of NIC details has been added to simplify configuration. * Obsoleted configuration options have been removed. * Logging usage within vsperf script was fixed. * Vsperf main was refactored and final cleanup function added. * Configurable forwarding mode of TestPMD executed inside VM. JIRA: VSPERF-198 Change-Id: I4a0d5d262b245d433b12419de79399fb5825a623 Signed-off-by: Martin Klozik <martinx.klozik@intel.com> Reviewed-by: Maryam Tahhan <maryam.tahhan@intel.com> Reviewed-by: Al Morton <acmorton@att.com> Reviewed-by: Christian Trautman <ctrautma@redhat.com> Reviewed-by: Brian Castelli <brian.castelli@spirent.com>
-rw-r--r--conf/02_vswitch.conf15
-rw-r--r--conf/04_vnf.conf3
-rw-r--r--core/component_factory.py5
-rw-r--r--core/loader/loader_servant.py4
-rw-r--r--core/pktfwd_controller.py9
-rwxr-xr-xdocs/userguide/integration.rst1
-rwxr-xr-xdocs/userguide/testusage.rst62
-rw-r--r--src/dpdk/dpdk.py56
-rw-r--r--testcases/integration.py4
-rw-r--r--testcases/performance.py4
-rw-r--r--testcases/testcase.py13
-rw-r--r--tools/networkcard.py266
-rw-r--r--tools/pkt_fwd/testpmd.py2
-rw-r--r--tools/systeminfo.py5
-rw-r--r--vnfs/qemu/qemu.py22
-rw-r--r--vnfs/qemu/qemu_pci_passthrough.py87
-rwxr-xr-xvsperf273
-rw-r--r--vswitches/ovs_vanilla.py9
18 files changed, 701 insertions, 139 deletions
diff --git a/conf/02_vswitch.conf b/conf/02_vswitch.conf
index f0475313..d36d1786 100644
--- a/conf/02_vswitch.conf
+++ b/conf/02_vswitch.conf
@@ -31,14 +31,12 @@ VSWITCH_DIR = os.path.join(ROOT_DIR, 'vswitches')
# DPDK target used when builing DPDK
RTE_TARGET = 'x86_64-native-linuxapp-gcc'
-# list of NIC HWIDs which will be bound to the 'igb_uio' driver on
-# system init
-WHITELIST_NICS = ['05:00.0', '05:00.1']
-
-# list of NIC HWIDs which will be ignored by the 'igb_uio' driver on
-# system init
-BLACKLIST_NICS = ['0000:09:00.0', '0000:09:00.1', '0000:09:00.2',
- '0000:09:00.3']
+# list of NIC HWIDs to which traffic generator is connected
+# In case of NIC with SRIOV suport, it is possible to define,
+# which virtual function should be used
+# e.g. value '0000:05:00.0|vf1' will configure two VFs and second VF
+# will be used for testing
+WHITELIST_NICS = ['0000:05:00.0', '0000:05:00.1']
# for DPDK_MODULES the path is in reference to the build directory
# To use vfio set
@@ -74,7 +72,6 @@ VHOST_USER_SOCKS = ['/tmp/dpdkvhostuser0', '/tmp/dpdkvhostuser1',
VSWITCHD_DPDK_ARGS = ['-c', '0x4', '-n', '4', '--socket-mem 1024,0']
VSWITCHD_VANILLA_ARGS = ['--pidfile']
-VSWITCH_VANILLA_PHY_PORT_NAMES = ['', '']
# use full module path to load module matching OVS version built from the source
VSWITCH_VANILLA_KERNEL_MODULES = ['libcrc32c', 'ip_tunnel', 'vxlan', 'gre', 'nf_conntrack', 'nf_defrag_ipv4', 'nf_defrag_ipv6', os.path.join(OVS_DIR_VANILLA, 'datapath/linux/openvswitch.ko')]
diff --git a/conf/04_vnf.conf b/conf/04_vnf.conf
index a78564c3..926ea50a 100644
--- a/conf/04_vnf.conf
+++ b/conf/04_vnf.conf
@@ -35,6 +35,9 @@ GUEST_IMAGE = ['', '']
# For 2 VNFs you may use [180, 180]
GUEST_TIMEOUT = [180, 180]
+# packet forwarding mode: io|mac|mac_retry|macswap|flowgen|rxonly|txonly|csum|icmpecho
+GUEST_TESTPMD_FWD_MODE = 'csum'
+
# guest loopback application method; supported options are:
# 'testpmd' - testpmd from dpdk will be built and used
# 'l2fwd' - l2fwd module provided by Huawei will be built and used
diff --git a/core/component_factory.py b/core/component_factory.py
index 9c58fc5c..a91872e2 100644
--- a/core/component_factory.py
+++ b/core/component_factory.py
@@ -118,13 +118,14 @@ def create_loadgen(loadgen_type, loadgen_cfg):
elif loadgen_type.find("stress") >= 0:
return Stress(loadgen_cfg)
-def create_pktfwd(pktfwd_class):
+def create_pktfwd(deployment, pktfwd_class):
"""Return a new packet forwarder controller
The returned controller is configured with the given
packet forwarder class.
:param pktfwd_class: Reference to packet forwarder class to be used.
+ :param deployment: The deployment scenario name
:return: packet forwarder controller
"""
- return PktFwdController(pktfwd_class)
+ return PktFwdController(deployment, pktfwd_class)
diff --git a/core/loader/loader_servant.py b/core/loader/loader_servant.py
index dc6353ff..226b0931 100644
--- a/core/loader/loader_servant.py
+++ b/core/loader/loader_servant.py
@@ -90,8 +90,8 @@ class LoaderServant(object):
desc = (mod.__doc__ or 'No description').strip().split('\n')[0]
results.append((name, desc))
- output = [
- 'Classes derived from: ' + self._interface.__name__ + '\n======\n']
+ header = 'Classes derived from: ' + self._interface.__name__
+ output = [header + '\n' + '=' * len(header) + '\n']
for (name, desc) in results:
output.append('* %-18s%s' % ('%s:' % name, desc))
diff --git a/core/pktfwd_controller.py b/core/pktfwd_controller.py
index 40565504..b1e37f2e 100644
--- a/core/pktfwd_controller.py
+++ b/core/pktfwd_controller.py
@@ -24,11 +24,12 @@ class PktFwdController(object):
_pktfwd_class: The packet forwarder class to be used.
_pktfwd: The packet forwarder object controlled by this controller
"""
- def __init__(self, pktfwd_class):
+ def __init__(self, deployment, pktfwd_class):
"""Initializes up the prerequisites for the P2P deployment scenario.
:vswitch_class: the vSwitch class to be used.
"""
+ self._deployment = deployment
self._logger = logging.getLogger(__name__)
self._pktfwd_class = pktfwd_class
self._pktfwd = pktfwd_class()
@@ -52,10 +53,12 @@ class PktFwdController(object):
self._pktfwd.stop()
def __enter__(self):
- self.setup()
+ if self._deployment.find("p2p") == 0:
+ self.setup()
def __exit__(self, type_, value, traceback):
- self.stop()
+ if self._deployment.find("p2p") == 0:
+ self.stop()
def get_pktfwd(self):
"""Get the controlled packet forwarder
diff --git a/docs/userguide/integration.rst b/docs/userguide/integration.rst
index 27bf2cd0..db9fdd0b 100755
--- a/docs/userguide/integration.rst
+++ b/docs/userguide/integration.rst
@@ -90,7 +90,6 @@ To run OVS NATIVE tunnel tests (VXLAN/GRE/GENEVE):
.. code-block:: python
VSWITCH = 'OvsVanilla'
- VSWITCH_VANILLA_PHY_PORT_NAMES = ['nic1name', 'nic2name']
# Specify vport_* kernel module to test.
VSWITCH_VANILLA_KERNEL_MODULES = ['vport_vxlan',
'vport_gre',
diff --git a/docs/userguide/testusage.rst b/docs/userguide/testusage.rst
index ac46ba3f..233f7015 100755
--- a/docs/userguide/testusage.rst
+++ b/docs/userguide/testusage.rst
@@ -199,7 +199,6 @@ for Vanilla OVS:
.. code-block:: console
VSWITCH = 'OvsVanilla'
- VSWITCH_VANILLA_PHY_PORT_NAMES = ['$PORT1', '$PORT2']
Where $PORT1 and $PORT2 are the Linux interfaces you'd like to bind
to the vswitch.
@@ -309,6 +308,8 @@ To run tests using Vanilla OVS:
$ ./vsperf --conf-file<path_to_custom_conf>/10_custom.conf
+.. _vfio-pci:
+
Using vfio_pci with DPDK
^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -346,6 +347,65 @@ To check that IOMMU is enabled on your platform:
[ 3.335746] IOMMU: dmar1 using Queued invalidation
....
+.. _SRIOV-support:
+
+Using SRIOV support
+^^^^^^^^^^^^^^^^^^^
+
+To use virtual functions of NIC with SRIOV support, use extended form
+of NIC PCI slot definition:
+
+.. code-block:: python
+
+ WHITELIST_NICS = ['0000:05:00.0|vf0', '0000:05:00.1|vf3']
+
+Where 'vf' is an indication of virtual function usage and following
+number defines a VF to be used. In case that VF usage is detected,
+then vswitchperf will enable SRIOV support for given card and it will
+detect PCI slot numbers of selected VFs.
+
+So in example above, one VF will be configured for NIC '0000:05:00.0'
+and four VFs will be configured for NIC '0000:05:00.1'. Vswitchperf
+will detect PCI addresses of selected VFs and it will use them during
+test execution.
+
+At the end of vswitchperf execution, SRIOV support will be disabled.
+
+SRIOV support is generic and it can be used in different testing scenarios.
+For example:
+
+* vSwitch tests with DPDK or without DPDK support to verify impact
+ of VF usage on vSwitch performance
+* tests without vSwitch, where traffic is forwared directly
+ between VF interfaces by packet forwarder (e.g. testpmd application)
+* tests without vSwitch, where VM accesses VF interfaces directly
+ by PCI-passthrough_ to measure raw VM throughput performance.
+
+.. _PCI-passthrough:
+
+Using QEMU with PCI passthrough support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Raw virtual machine throughput performance can be measured by execution of PVP
+test with direct access to NICs by PCI passthrough. To execute VM with direct
+access to PCI devices, enable vfio-pci_. In order to use virtual functions,
+SRIOV-support_ must be enabled.
+
+Execution of test with PCI passthrough with vswitch disabled:
+
+.. code-block:: console
+
+ $ ./vsperf --conf-file=<path_to_custom_conf>/10_custom.conf
+ --vswtich none --vnf QemuPciPassthrough pvp_tput
+
+Any of supported guest-loopback-application_ can be used inside VM with
+PCI passthrough support.
+
+Note: Qemu with PCI passthrough support can be used only with PVP test
+deployment.
+
+.. _guest-loopback-application:
+
Selection of loopback application for PVP and PVVP tests
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/dpdk/dpdk.py b/src/dpdk/dpdk.py
index f8cbbd81..36f1d055 100644
--- a/src/dpdk/dpdk.py
+++ b/src/dpdk/dpdk.py
@@ -23,7 +23,6 @@ from sys import platform as _platform
import os
import subprocess
import logging
-import locale
from tools import tasks
from conf import settings
@@ -34,14 +33,22 @@ RTE_PCI_TOOL = os.path.join(
settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
_DPDK_MODULE_MANAGER = ModuleManager()
+
+# declare global NIC variables only as their content might not be known yet
+_NICS = []
+_NICS_PCI = []
+
#
# system management
#
-
def init():
"""Setup system for DPDK.
"""
+ global _NICS
+ global _NICS_PCI
+ _NICS = settings.getValue('NICS')
+ _NICS_PCI = list(nic['pci'] for nic in _NICS)
if not _is_linux():
_LOGGER.error('Not running on a compatible Linux version. Exiting...')
return
@@ -175,54 +182,35 @@ def _bind_nics():
True)
tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind=' + _driver] +
- settings.getValue('WHITELIST_NICS'), _LOGGER,
- 'Binding NICs %s...' %
- settings.getValue('WHITELIST_NICS'),
+ _NICS_PCI, _LOGGER,
+ 'Binding NICs %s...' % _NICS_PCI,
True)
except subprocess.CalledProcessError:
- _LOGGER.error('Unable to bind NICs %s',
- str(settings.getValue('WHITELIST_NICS')))
-
-def _unbind_nics_get_driver():
- """Check what driver the NICs should be bound to
- after unbinding them from DPDK.
- """
- _driver_list = []
- _output = subprocess.check_output([os.path.expanduser(RTE_PCI_TOOL), '--status'])
- _my_encoding = locale.getdefaultlocale()[1]
- for line in _output.decode(_my_encoding).split('\n'):
- for nic in settings.getValue('WHITELIST_NICS'):
- if nic in line:
- _driver_list.append((line.split("unused=", 1)[1]))
- return _driver_list
+ _LOGGER.error('Unable to bind NICs %s', str(_NICS_PCI))
def _unbind_nics():
"""Unbind NICs using the Intel DPDK ``dpdk_nic_bind.py`` tool.
"""
- nic_drivers = _unbind_nics_get_driver()
try:
tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] +
- settings.getValue('WHITELIST_NICS'), _LOGGER,
- 'Unbinding NICs %s...' %
- str(settings.getValue('WHITELIST_NICS')),
+ _NICS_PCI, _LOGGER,
+ 'Unbinding NICs %s...' % str(_NICS_PCI),
True)
except subprocess.CalledProcessError:
- _LOGGER.error('Unable to unbind NICs %s',
- str(settings.getValue('WHITELIST_NICS')))
+ _LOGGER.error('Unable to unbind NICs %s', str(_NICS_PCI))
# Rebind NICs to their original drivers
# using the Intel DPDK ``dpdk_nic_bind.py`` tool.
- for i, nic in enumerate(settings.getValue('WHITELIST_NICS')):
+ for nic in _NICS:
try:
- if nic_drivers[i] != '':
+ if nic['driver']:
tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind',
- nic_drivers[i], nic],
- _LOGGER, 'Binding NIC %s...' %
- nic,
+ nic['driver'], nic['pci']],
+ _LOGGER, 'Binding NIC %s to %s...' %
+ (nic['pci'], nic['driver']),
True)
except subprocess.CalledProcessError:
- _LOGGER.error('Unable to bind NICs %s to drivers %s',
- str(settings.getValue('WHITELIST_NICS')),
- nic_drivers)
+ _LOGGER.error('Unable to bind NIC %s to driver %s',
+ nic['pci'], nic['driver'])
class Dpdk(object):
"""A context manager for the system init/cleanup.
diff --git a/testcases/integration.py b/testcases/integration.py
index ecaed14f..53ba17f4 100644
--- a/testcases/integration.py
+++ b/testcases/integration.py
@@ -28,11 +28,11 @@ class IntegrationTestCase(TestCase):
"""IntegrationTestCase class
"""
- def __init__(self, cfg, results_dir):
+ def __init__(self, cfg):
""" Testcase initialization
"""
self._type = 'integration'
- super(IntegrationTestCase, self).__init__(cfg, results_dir)
+ super(IntegrationTestCase, self).__init__(cfg)
self._logger = logging.getLogger(__name__)
self._inttest = None
diff --git a/testcases/performance.py b/testcases/performance.py
index 0ae3ea77..a4769a28 100644
--- a/testcases/performance.py
+++ b/testcases/performance.py
@@ -25,11 +25,11 @@ class PerformanceTestCase(TestCase):
In this basic form runs RFC2544 throughput test
"""
- def __init__(self, cfg, results_dir):
+ def __init__(self, cfg):
""" Testcase initialization
"""
self._type = 'performance'
- super(PerformanceTestCase, self).__init__(cfg, results_dir)
+ super(PerformanceTestCase, self).__init__(cfg)
self._logger = logging.getLogger(__name__)
def run_report(self):
diff --git a/testcases/testcase.py b/testcases/testcase.py
index 0effce75..ff1247fc 100644
--- a/testcases/testcase.py
+++ b/testcases/testcase.py
@@ -36,7 +36,7 @@ class TestCase(object):
In this basic form runs RFC2544 throughput test
"""
- def __init__(self, cfg, results_dir):
+ def __init__(self, cfg):
"""Pull out fields from test config
:param cfg: A dictionary of string-value pairs describing the test
@@ -114,7 +114,7 @@ class TestCase(object):
if self._frame_mod:
self._frame_mod = self._frame_mod.lower()
- self._results_dir = results_dir
+ self._results_dir = S.getValue('RESULTS_PATH')
# set traffic details, so they can be passed to vswitch and traffic ctls
self._traffic = copy.deepcopy(TRAFFIC_DEFAULTS)
@@ -163,6 +163,14 @@ class TestCase(object):
self._traffic['l2'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L2')
self._traffic['l3'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L3')
self._traffic['l4'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L4')
+ elif S.getValue('NICS')[0]['type'] == 'vf' or S.getValue('NICS')[1]['type'] == 'vf':
+ mac1 = S.getValue('NICS')[0]['mac']
+ mac2 = S.getValue('NICS')[1]['mac']
+ if mac1 and mac2:
+ self._traffic['l2'].update({'srcmac': mac2, 'dstmac': mac1})
+ else:
+ self._logger.debug("MAC addresses can not be read")
+
self._logger.debug("Controllers:")
@@ -177,6 +185,7 @@ class TestCase(object):
if self._vswitch_none:
self._vswitch_ctl = component_factory.create_pktfwd(
+ self.deployment,
loader.get_pktfwd_class())
else:
self._vswitch_ctl = component_factory.create_vswitch(
diff --git a/tools/networkcard.py b/tools/networkcard.py
new file mode 100644
index 00000000..c31be691
--- /dev/null
+++ b/tools/networkcard.py
@@ -0,0 +1,266 @@
+# Copyright 2016 Intel Corporation.
+#
+# 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.
+
+"""Tools for network card manipulation
+"""
+
+import os
+import subprocess
+import logging
+import glob
+from conf import settings
+
+_LOGGER = logging.getLogger('tools.networkcard')
+
+_PCI_DIR = '/sys/bus/pci/devices/{}/'
+_SRIOV_NUMVFS = os.path.join(_PCI_DIR, 'sriov_numvfs')
+_SRIOV_TOTALVFS = os.path.join(_PCI_DIR, 'sriov_totalvfs')
+_SRIOV_VF_PREFIX = 'virtfn'
+_SRIOV_PF = 'physfn'
+_PCI_NET = 'net'
+_PCI_DRIVER = 'driver'
+
+
+def check_pci(pci_handle):
+ """ Checks if given extended PCI handle has correct length and fixes
+ it if possible.
+
+ :param pci_handle: PCI slot identifier. It can contain vsperf specific
+ suffix after '|' with VF indication. e.g. '0000:05:00.0|vf1'
+
+ :returns: PCI handle
+ """
+ pci = pci_handle.split('|')
+ pci_len = len(pci[0])
+ if pci_len == 12:
+ return pci_handle
+ elif pci_len == 7:
+ pci[0] = '0000:' + pci[0][-7:]
+ _LOGGER.debug('Adding domain part to PCI slot %s', pci[0])
+ return '|'.join(pci)
+ elif pci_len > 12:
+ pci[0] = pci[0][-12:]
+ _LOGGER.warning('PCI slot is too long, it will be shortened to %s', pci[0])
+ return '|'.join(pci)
+ else:
+ # pci_handle has a strange length, but let us try to use it
+ _LOGGER.error('Unknown format of PCI slot %s', pci_handle)
+ return pci_handle
+
+def is_sriov_supported(pci_handle):
+ """ Checks if sriov is supported by given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+
+ :returns: True on success, False otherwise
+ """
+ return os.path.isfile(_SRIOV_TOTALVFS.format(pci_handle))
+
+def is_sriov_nic(pci_handle):
+ """ Checks if given extended PCI ID refers to the VF
+
+ :param pci_handle: PCI slot identifier with domain part. It can contain
+ vsperf specific suffix after '|' with VF indication.
+ e.g. '0000:05:00.0|vf1'
+
+ :returns: True on success, False otherwise
+ """
+ for item in pci_handle.split('|'):
+ if item.lower().startswith('vf'):
+ return True
+ return False
+
+def set_sriov_numvfs(pci_handle, numvfs):
+ """ Checks if sriov is supported and configures given number of VFs
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :param numvfs: Number of VFs to be configured at given NIC.
+
+ :returns: True on success, False otherwise
+ """
+ if not is_sriov_supported(pci_handle):
+ return False
+
+ if get_sriov_numvfs(pci_handle) == numvfs:
+ return True
+
+ if numvfs and get_sriov_numvfs(pci_handle) != 0:
+ if not set_sriov_numvfs(pci_handle, 0):
+ return False
+
+ try:
+ subprocess.call('sudo bash -c "echo {} > {}"'.format(numvfs, _SRIOV_NUMVFS.format(pci_handle)), shell=True)
+ return get_sriov_numvfs(pci_handle) == numvfs
+ except OSError:
+ _LOGGER.debug('Number of VFs cant be changed to %s for PF %s', numvfs, pci_handle)
+ return False
+
+def get_sriov_numvfs(pci_handle):
+ """ Returns the number of configured VFs
+
+ :param pci_handle: PCI slot identifier with domain part
+ :returns: the number of configured VFs
+ """
+ if is_sriov_supported(pci_handle):
+ with open(_SRIOV_NUMVFS.format(pci_handle), 'r') as numvfs:
+ return int(numvfs.readline().rstrip('\n'))
+
+ return None
+
+def get_sriov_totalvfs(pci_handle):
+ """ Checks if sriov is supported and returns max number of supported VFs
+
+ :param pci_handle: PCI slot identifier with domain part
+ :returns: the max number of supported VFs by given NIC
+ """
+ if is_sriov_supported(pci_handle):
+ with open(_SRIOV_TOTALVFS.format(pci_handle), 'r') as total:
+ return int(total.readline().rstrip('\n'))
+
+ return None
+
+def get_sriov_vfs_list(pf_pci_handle):
+ """ Returns list of PCI handles of VFs configured at given NIC/PF
+
+ :param pf_pci_handle: PCI slot identifier of PF with domain part.
+ :returns: list
+ """
+ vfs = []
+ if is_sriov_supported(pf_pci_handle):
+ for vf_name in glob.glob(os.path.join(_PCI_DIR, _SRIOV_VF_PREFIX + '*').format(pf_pci_handle)):
+ vfs.append(os.path.basename(os.path.realpath(vf_name)))
+
+ return vfs
+
+def get_sriov_pf(vf_pci_handle):
+ """ Get PCI handle of PF which belongs to given VF
+
+ :param vf_pci_handle: PCI slot identifier of VF with domain part.
+ :returns: PCI handle of parent PF
+ """
+ pf_path = os.path.join(_PCI_DIR, _SRIOV_PF).format(vf_pci_handle)
+ if os.path.isdir(pf_path):
+ return os.path.basename(os.path.realpath(pf_path))
+
+ return None
+
+def get_driver(pci_handle):
+ """ Returns name of kernel driver assigned to given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned kernel driver, None otherwise
+ """
+ driver_path = os.path.join(_PCI_DIR, _PCI_DRIVER).format(pci_handle)
+ if os.path.isdir(driver_path):
+ return os.path.basename(os.path.realpath(driver_path))
+
+ return None
+
+def get_device_name(pci_handle):
+ """ Returns name of network card device name
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned NIC device name, None otherwise
+ """
+ net_path = os.path.join(_PCI_DIR, _PCI_NET).format(pci_handle)
+ try:
+ return os.listdir(net_path)[0]
+ except FileNotFoundError:
+ return None
+ except IndexError:
+ return None
+
+ return None
+
+def get_mac(pci_handle):
+ """ Returns MAC address of given NIC
+
+ :param pci_handle: PCI slot identifier with domain part.
+ :returns: string with assigned MAC address, None otherwise
+ """
+ mac_path = glob.glob(os.path.join(_PCI_DIR, _PCI_NET, '*', 'address').format(pci_handle))
+ # kernel driver is loaded and MAC can be read
+ if len(mac_path) and os.path.isfile(mac_path[0]):
+ with open(mac_path[0], 'r') as _file:
+ return _file.readline().rstrip('\n')
+
+ # MAC address is unknown, e.g. NIC is assigned to DPDK
+ return None
+
+def get_nic_info(full_pci_handle):
+ """ Parse given pci handle with additional info and returns
+ requested NIC info.
+
+ :param full_pci_handle: A string with extended network card PCI ID.
+ extended PCI ID syntax: PCI_ID[|vfx][|(mac|dev)]
+ examples:
+ 0000:06:00.0 - returns the same value
+ 0000:06:00.0|vf0 - returns PCI ID of 1st virtual function of given NIC
+ 0000:06:00.0|mac - returns MAC address of given NIC
+ 0000:06:00.0|vf0|mac - returns MAC address of 1st virtual function of given NIC
+
+ :returns: A string with requested NIC data or None if data cannot be read.
+ """
+ parsed_handle = full_pci_handle.split('|')
+ if len(parsed_handle) not in (1, 2, 3):
+ _LOGGER.error("Invalid PCI device name: '%s'", full_pci_handle)
+ return None
+
+ pci_handle = parsed_handle[0]
+
+ for action in parsed_handle[1:]:
+ # in case of SRIOV get PCI handle of given virtual function
+ if action.lower().startswith('vf'):
+ try:
+ vf_num = int(action[2:])
+ pci_handle = get_sriov_vfs_list(pci_handle)[vf_num]
+ except ValueError:
+ _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, action[2:])
+ return None
+ except IndexError:
+ _LOGGER.error("Pci device '%s', does not have VF with index '%s'", pci_handle, vf_num)
+ return None
+ continue
+
+ # return requested info for given PCI handle
+ if action.lower() == 'mac':
+ return get_mac(pci_handle)
+ elif action.lower() == 'dev':
+ return get_device_name(pci_handle)
+ else:
+ _LOGGER.error("Invalid item '%s' in PCI handle '%s'", action, full_pci_handle)
+ return None
+
+ return pci_handle
+
+def reinit_vfs(pf_pci_handle):
+ """ Reinitializates all VFs, which belong to given PF
+
+ :param pf_pci_handle: PCI slot identifier of PF with domain part.
+ """
+ rte_pci_tool = os.path.join(settings.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
+
+ for vf_nic in get_sriov_vfs_list(pf_pci_handle):
+ nic_driver = get_driver(vf_nic)
+ if nic_driver:
+ try:
+ subprocess.call(['sudo', rte_pci_tool, '--unbind', vf_nic],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ subprocess.call(['sudo', rte_pci_tool, '--bind=' + nic_driver, vf_nic],
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except subprocess.CalledProcessError:
+ _LOGGER.warning('Error during reinitialization of VF %s', vf_nic)
+ else:
+ _LOGGER.warning("Can't detect driver for VF %s", vf_nic)
+
diff --git a/tools/pkt_fwd/testpmd.py b/tools/pkt_fwd/testpmd.py
index d8ed8905..e1b987bc 100644
--- a/tools/pkt_fwd/testpmd.py
+++ b/tools/pkt_fwd/testpmd.py
@@ -42,7 +42,7 @@ class TestPMD(IPktFwd):
vswitchd_args += _VSWITCHD_CONST_ARGS
vswitchd_args += settings.getValue('TESTPMD_ARGS')
- self._nports = len(settings.getValue('WHITELIST_NICS'))
+ self._nports = len(settings.getValue('NICS'))
self._fwdmode = settings.getValue('TESTPMD_FWD_MODE')
self._csum_layer = settings.getValue('TESTPMD_CSUM_LAYER')
self._csum_calc = settings.getValue('TESTPMD_CSUM_CALC')
diff --git a/tools/systeminfo.py b/tools/systeminfo.py
index 62db852b..531f1c92 100644
--- a/tools/systeminfo.py
+++ b/tools/systeminfo.py
@@ -71,8 +71,9 @@ def get_nic():
output = subprocess.check_output('lspci', shell=True)
output = output.decode(locale.getdefaultlocale()[1])
for line in output.split('\n'):
- for nic_pciid in S.getValue('WHITELIST_NICS'):
- if line.startswith(nic_pciid):
+ for nic in S.getValue('NICS'):
+ # lspci shows PCI addresses without domain part, i.e. last 7 chars
+ if line.startswith(nic['pci'][-7:]):
nics.append(''.join(line.split(':')[2:]).strip())
return nics
diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py
index ec728c5c..9cb23ac6 100644
--- a/vnfs/qemu/qemu.py
+++ b/vnfs/qemu/qemu.py
@@ -66,6 +66,14 @@ class IVnfQemu(IVnf):
# cli option take precedence to config file values
self._guest_loopback = S.getValue('GUEST_LOOPBACK')[self._number]
+ self._testpmd_fwd_mode = S.getValue('GUEST_TESTPMD_FWD_MODE')
+ # in case of SRIOV we must ensure, that MAC addresses are not swapped
+ if S.getValue('SRIOV_ENABLED') and self._testpmd_fwd_mode.startswith('mac') and \
+ not S.getValue('VNF').endswith('PciPassthrough'):
+
+ self._logger.info("SRIOV detected, forwarding mode of testpmd was changed from '%s' to '%s'",
+ self._testpmd_fwd_mode, 'io')
+ self._testpmd_fwd_mode = 'io'
name = 'Client%d' % self._number
vnc = ':%d' % self._number
@@ -306,6 +314,11 @@ class IVnfQemu(IVnf):
# modify makefile if needed
self._modify_dpdk_makefile()
+ # disable network interfaces, so DPDK can take care of them
+ self.execute_and_wait('ifdown ' + self._net1)
+ self.execute_and_wait('ifdown ' + self._net2)
+
+ # build and insert igb_uio and rebind interfaces to it
self.execute_and_wait('make RTE_OUTPUT=$RTE_SDK/$RTE_TARGET -C '
'$RTE_SDK/lib/librte_eal/linuxapp/igb_uio')
self.execute_and_wait('modprobe uio')
@@ -313,9 +326,14 @@ class IVnfQemu(IVnf):
S.getValue('RTE_TARGET'))
self.execute_and_wait('./tools/dpdk_nic_bind.py --status')
self.execute_and_wait(
+ './tools/dpdk_nic_bind.py -u' ' ' +
+ S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' +
+ S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number])
+ self.execute_and_wait(
'./tools/dpdk_nic_bind.py -b igb_uio' ' ' +
S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' +
S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number])
+ self.execute_and_wait('./tools/dpdk_nic_bind.py --status')
# build and run 'test-pmd'
self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') +
@@ -325,9 +343,9 @@ class IVnfQemu(IVnf):
self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
' --burst=64 -i --txqflags=0xf00 ' +
'--disable-hw-vlan', 60, "Done")
- self.execute('set fwd mac_retry', 1)
+ self.execute('set fwd ' + self._testpmd_fwd_mode, 1)
self.execute_and_wait('start', 20,
- 'TX RS bit threshold=0 - TXQ flags=0xf00')
+ 'TX RS bit threshold=.+ - TXQ flags=0xf00')
def _configure_l2fwd(self):
"""
diff --git a/vnfs/qemu/qemu_pci_passthrough.py b/vnfs/qemu/qemu_pci_passthrough.py
new file mode 100644
index 00000000..1b55fdf2
--- /dev/null
+++ b/vnfs/qemu/qemu_pci_passthrough.py
@@ -0,0 +1,87 @@
+# Copyright 2015 Intel Corporation.
+#
+# 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.
+
+"""Automation of QEMU hypervisor with direct access to host NICs via
+ PCI passthrough.
+"""
+
+import logging
+import subprocess
+import os
+
+from conf import settings as S
+from vnfs.qemu.qemu import IVnfQemu
+from tools import tasks
+from tools.module_manager import ModuleManager
+
+_MODULE_MANAGER = ModuleManager()
+_RTE_PCI_TOOL = os.path.join(S.getValue('RTE_SDK'), 'tools', 'dpdk_nic_bind.py')
+
+class QemuPciPassthrough(IVnfQemu):
+ """
+ Control an instance of QEMU with direct access to the host network devices
+ """
+ def __init__(self):
+ """
+ Initialization function.
+ """
+ super(QemuPciPassthrough, self).__init__()
+ self._logger = logging.getLogger(__name__)
+ self._nics = S.getValue('NICS')
+
+ # in case of SRIOV and PCI passthrough we must ensure, that MAC addresses are swapped
+ if S.getValue('SRIOV_ENABLED') and not self._testpmd_fwd_mode.startswith('mac'):
+ self._logger.info("SRIOV detected, forwarding mode of testpmd was changed from '%s' to '%s'",
+ self._testpmd_fwd_mode, 'mac_retry')
+ self._testpmd_fwd_mode = 'mac_retry'
+
+ for nic in self._nics:
+ self._cmd += ['-device', 'vfio-pci,host=' + nic['pci']]
+
+ def start(self):
+ """
+ Start QEMU instance, bind host NICs to vfio-pci driver
+ """
+ # load vfio-pci
+ _MODULE_MANAGER.insert_modules(['vfio-pci'])
+
+ # bind every interface to vfio-pci driver
+ try:
+ nics_list = list(tmp_nic['pci'] for tmp_nic in self._nics)
+ tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=vfio-pci'] + nics_list,
+ self._logger, 'Binding NICs %s...' % nics_list, True)
+
+ except subprocess.CalledProcessError:
+ self._logger.error('Unable to bind NICs %s', self._nics)
+
+ super(QemuPciPassthrough, self).start()
+
+ def stop(self):
+ """
+ Stop QEMU instance, bind host NICs to the original driver
+ """
+ super(QemuPciPassthrough, self).stop()
+
+ # bind original driver to every interface
+ for nic in self._nics:
+ if nic['driver']:
+ try:
+ tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=' + nic['driver'], nic['pci']],
+ self._logger, 'Binding NIC %s...' % nic['pci'], True)
+
+ except subprocess.CalledProcessError:
+ self._logger.error('Unable to bind NIC %s to driver %s', nic['pci'], nic['driver'])
+
+ # unload vfio-pci driver
+ _MODULE_MANAGER.remove_modules()
diff --git a/vsperf b/vsperf
index 35283185..57d68990 100755
--- a/vsperf
+++ b/vsperf
@@ -39,6 +39,7 @@ from core.loader import Loader
from testcases import PerformanceTestCase
from testcases import IntegrationTestCase
from tools import tasks
+from tools import networkcard
from tools.pkt_gen import trafficgen
from tools.opnfvdashboard import opnfvdashboard
from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS
@@ -58,6 +59,8 @@ _TEMPLATE_RST = {'head' : 'tools/report/report_head.rst',
'tmp' : 'tools/report/report_tmp_caption.rst'
}
+_LOGGER = logging.getLogger()
+
def parse_arguments():
"""
Parse command line arguments.
@@ -194,18 +197,17 @@ def configure_logging(level):
settings.getValue('LOG_DIR'),
settings.getValue('LOG_FILE_TRAFFIC_GEN'))
- logger = logging.getLogger()
- logger.setLevel(logging.DEBUG)
+ _LOGGER.setLevel(logging.DEBUG)
stream_logger = logging.StreamHandler(sys.stdout)
stream_logger.setLevel(VERBOSITY_LEVELS[level])
stream_logger.setFormatter(logging.Formatter(
- '[%(levelname)s] %(asctime)s : (%(name)s) - %(message)s'))
- logger.addHandler(stream_logger)
+ '[%(levelname)-5s] %(asctime)s : (%(name)s) - %(message)s'))
+ _LOGGER.addHandler(stream_logger)
file_logger = logging.FileHandler(filename=log_file_default)
file_logger.setLevel(logging.DEBUG)
- logger.addHandler(file_logger)
+ _LOGGER.addHandler(file_logger)
class CommandFilter(logging.Filter):
"""Filter out strings beginning with 'cmd :'"""
@@ -220,12 +222,12 @@ def configure_logging(level):
cmd_logger = logging.FileHandler(filename=log_file_host_cmds)
cmd_logger.setLevel(logging.DEBUG)
cmd_logger.addFilter(CommandFilter())
- logger.addHandler(cmd_logger)
+ _LOGGER.addHandler(cmd_logger)
gen_logger = logging.FileHandler(filename=log_file_traffic_gen)
gen_logger.setLevel(logging.DEBUG)
gen_logger.addFilter(TrafficGenCommandFilter())
- logger.addHandler(gen_logger)
+ _LOGGER.addHandler(gen_logger)
def apply_filter(tests, tc_filter):
@@ -267,26 +269,30 @@ def check_and_set_locale():
system_locale = locale.getdefaultlocale()
if None in system_locale:
os.environ['LC_ALL'] = settings.getValue('DEFAULT_LOCALE')
- logging.warning("Locale was not properly configured. Default values were set. Old locale: %s, New locale: %s",
+ _LOGGER.warning("Locale was not properly configured. Default values were set. Old locale: %s, New locale: %s",
system_locale, locale.getdefaultlocale())
-def generate_final_report(path):
+def generate_final_report():
""" Function will check if partial test results are available
and generates final report in rst format.
"""
+ path = settings.getValue('RESULTS_PATH')
# check if there are any results in rst format
rst_results = glob.glob(os.path.join(path, 'result*rst'))
if len(rst_results):
try:
test_report = os.path.join(path, '{}_{}'.format(settings.getValue('VSWITCH'), _TEMPLATE_RST['final']))
# create report caption directly - it is not worth to execute jinja machinery
+ if settings.getValue('VSWITCH').lower() != 'none':
+ pkt_processor = Loader().get_vswitches()[settings.getValue('VSWITCH')].__doc__.strip().split('\n')[0]
+ else:
+ pkt_processor = Loader().get_pktfwds()[settings.getValue('PKTFWD')].__doc__.strip().split('\n')[0]
report_caption = '{}\n{} {}\n{}\n\n'.format(
'============================================================',
'Performance report for',
- Loader().get_vswitches()[settings.getValue('VSWITCH')].__doc__.strip().split('\n')[0],
-
+ pkt_processor,
'============================================================')
with open(_TEMPLATE_RST['tmp'], 'w') as file_:
@@ -296,15 +302,143 @@ def generate_final_report(path):
' '.join(rst_results), _TEMPLATE_RST['foot'],
test_report), shell=True)
if retval == 0 and os.path.isfile(test_report):
- logging.info('Overall test report written to "%s"', test_report)
+ _LOGGER.info('Overall test report written to "%s"', test_report)
else:
- logging.error('Generatrion of overall test report has failed.')
+ _LOGGER.error('Generatrion of overall test report has failed.')
# remove temporary file
os.remove(_TEMPLATE_RST['tmp'])
except subprocess.CalledProcessError:
- logging.error('Generatrion of overall test report has failed.')
+ _LOGGER.error('Generatrion of overall test report has failed.')
+
+
+def enable_sriov(nic_list):
+ """ Enable SRIOV for given enhanced PCI IDs
+
+ :param nic_list: A list of enhanced PCI IDs
+ """
+ # detect if sriov is required
+ sriov_nic = {}
+ for nic in nic_list:
+ if networkcard.is_sriov_nic(nic):
+ tmp_nic = nic.split('|')
+ if tmp_nic[0] in sriov_nic:
+ if int(tmp_nic[1][2:]) > sriov_nic[tmp_nic[0]]:
+ sriov_nic[tmp_nic[0]] = int(tmp_nic[1][2:])
+ else:
+ sriov_nic.update({tmp_nic[0] : int(tmp_nic[1][2:])})
+
+ # sriov is required for some NICs
+ if len(sriov_nic):
+ for nic in sriov_nic:
+ # check if SRIOV is supported and enough virt interfaces are available
+ if not networkcard.is_sriov_supported(nic) \
+ or networkcard.get_sriov_numvfs(nic) <= sriov_nic[nic]:
+ # if not, enable and set appropriate number of VFs
+ if not networkcard.set_sriov_numvfs(nic, sriov_nic[nic] + 1):
+ _LOGGER.error("SRIOV cannot be enabled for NIC %s", nic)
+ raise
+ else:
+ _LOGGER.debug("SRIOV enabled for NIC %s", nic)
+
+ # WORKAROUND: it has been observed with IXGBE(VF) driver,
+ # that NIC doesn't correclty dispatch traffic to VFs based
+ # on their MAC address. Unbind and bind to the same driver
+ # solves this issue.
+ networkcard.reinit_vfs(nic)
+
+ # After SRIOV is enabled it takes some time until network drivers
+ # properly initialize all cards.
+ # Wait also in case, that SRIOV was already configured as it can be
+ # configured automatically just before vsperf execution.
+ time.sleep(2)
+
+ return True
+
+ return False
+
+
+def disable_sriov(nic_list):
+ """ Disable SRIOV for given PCI IDs
+
+ :param nic_list: A list of enhanced PCI IDs
+ """
+ for nic in nic_list:
+ if networkcard.is_sriov_nic(nic):
+ if not networkcard.set_sriov_numvfs(nic.split('|')[0], 0):
+ _LOGGER.error("SRIOV cannot be disabled for NIC %s", nic)
+ raise
+ else:
+ _LOGGER.debug("SRIOV disabled for NIC %s", nic.split('|')[0])
+
+
+def handle_list_options(args):
+ """ Process --list cli arguments if needed
+
+ :param args: A dictionary with all CLI arguments
+ """
+ if args['list_trafficgens']:
+ print(Loader().get_trafficgens_printable())
+ sys.exit(0)
+
+ if args['list_collectors']:
+ print(Loader().get_collectors_printable())
+ sys.exit(0)
+
+ if args['list_vswitches']:
+ print(Loader().get_vswitches_printable())
+ sys.exit(0)
+
+ if args['list_vnfs']:
+ print(Loader().get_vnfs_printable())
+ sys.exit(0)
+
+ if args['list_fwdapps']:
+ print(Loader().get_pktfwds_printable())
+ sys.exit(0)
+
+ if args['list_settings']:
+ print(str(settings))
+ sys.exit(0)
+
+ if args['list']:
+ # configure tests
+ if args['integration']:
+ testcases = settings.getValue('INTEGRATION_TESTS')
+ else:
+ testcases = settings.getValue('PERFORMANCE_TESTS')
+
+ print("Available Tests:")
+ print("================")
+
+ for test in testcases:
+ print('* %-30s %s' % ('%s:' % test['Name'], test['Description']))
+ sys.exit(0)
+
+
+def vsperf_finalize():
+ """ Clean up before exit
+ """
+ # remove directory if no result files were created
+ try:
+ results_path = settings.getValue('RESULTS_PATH')
+ if os.path.exists(results_path):
+ files_list = os.listdir(results_path)
+ if files_list == []:
+ _LOGGER.info("Removing empty result directory: " + results_path)
+ shutil.rmtree(results_path)
+ except AttributeError:
+ # skip it if parameter doesn't exist
+ pass
+
+ # disable SRIOV if needed
+ try:
+ if settings.getValue('SRIOV_ENABLED'):
+ disable_sriov(settings.getValue('WHITELIST_NICS_ORIG'))
+ except AttributeError:
+ # skip it if parameter doesn't exist
+ pass
class MockTestCase(unittest.TestCase):
@@ -383,8 +517,10 @@ def main():
if 'none' == settings.getValue('VSWITCH').strip().lower():
vswitch_none = True
+ # if required, handle list-* operations
+ handle_list_options(args)
+
configure_logging(settings.getValue('VERBOSITY'))
- logger = logging.getLogger()
# check and fix locale
check_and_set_locale()
@@ -393,12 +529,12 @@ def main():
if args['trafficgen']:
trafficgens = Loader().get_trafficgens()
if args['trafficgen'] not in trafficgens:
- logging.error('There are no trafficgens matching \'%s\' found in'
+ _LOGGER.error('There are no trafficgens matching \'%s\' found in'
' \'%s\'. Exiting...', args['trafficgen'],
settings.getValue('TRAFFICGEN_DIR'))
sys.exit(1)
- # configure vswitch
+ # configuration validity checks
if args['vswitch']:
vswitch_none = 'none' == args['vswitch'].strip().lower()
if vswitch_none:
@@ -406,7 +542,7 @@ def main():
else:
vswitches = Loader().get_vswitches()
if args['vswitch'] not in vswitches:
- logging.error('There are no vswitches matching \'%s\' found in'
+ _LOGGER.error('There are no vswitches matching \'%s\' found in'
' \'%s\'. Exiting...', args['vswitch'],
settings.getValue('VSWITCH_DIR'))
sys.exit(1)
@@ -415,7 +551,7 @@ def main():
settings.setValue('PKTFWD', args['fwdapp'])
fwdapps = Loader().get_pktfwds()
if args['fwdapp'] not in fwdapps:
- logging.error('There are no forwarding application'
+ _LOGGER.error('There are no forwarding application'
' matching \'%s\' found in'
' \'%s\'. Exiting...', args['fwdapp'],
settings.getValue('PKTFWD_DIR'))
@@ -424,16 +560,45 @@ def main():
if args['vnf']:
vnfs = Loader().get_vnfs()
if args['vnf'] not in vnfs:
- logging.error('there are no vnfs matching \'%s\' found in'
+ _LOGGER.error('there are no vnfs matching \'%s\' found in'
' \'%s\'. exiting...', args['vnf'],
settings.getValue('vnf_dir'))
sys.exit(1)
+ if args['exact_test_name'] and args['tests']:
+ _LOGGER.error("Cannot specify tests with both positional args and --test.")
+ sys.exit(1)
+
+ # sriov handling
+ settings.setValue('SRIOV_ENABLED', enable_sriov(settings.getValue('WHITELIST_NICS')))
+
+ # modify NIC configuration to decode enhanced PCI IDs
+ wl_nics_orig = list(networkcard.check_pci(pci) for pci in settings.getValue('WHITELIST_NICS'))
+ settings.setValue('WHITELIST_NICS_ORIG', wl_nics_orig)
+
+ nic_list = []
+ for nic in wl_nics_orig:
+ tmp_nic = networkcard.get_nic_info(nic)
+ if tmp_nic:
+ nic_list.append({'pci' : tmp_nic,
+ 'type' : 'vf' if networkcard.get_sriov_pf(tmp_nic) else 'pf',
+ 'mac' : networkcard.get_mac(tmp_nic),
+ 'driver' : networkcard.get_driver(tmp_nic),
+ 'device' : networkcard.get_device_name(tmp_nic)})
+ else:
+ _LOGGER.error("Invalid network card PCI ID: '%s'", nic)
+ vsperf_finalize()
+ raise
+
+ settings.setValue('NICS', nic_list)
+ # for backward compatibility
+ settings.setValue('WHITELIST_NICS', list(nic['pci'] for nic in nic_list))
+
# update global settings
guest_loopback = get_test_param('guest_loopback', None)
if guest_loopback:
tmp_gl = []
- for i in range(len(settings.getValue('GUEST_LOOPBACK'))):
+ for dummy_i in range(len(settings.getValue('GUEST_LOOPBACK'))):
tmp_gl.append(guest_loopback)
settings.setValue('GUEST_LOOPBACK', tmp_gl)
@@ -443,15 +608,16 @@ def main():
date = datetime.datetime.fromtimestamp(time.time())
results_dir = "results_" + date.strftime('%Y-%m-%d_%H-%M-%S')
results_path = os.path.join(settings.getValue('LOG_DIR'), results_dir)
+ settings.setValue('RESULTS_PATH', results_path)
# create results directory
if not os.path.exists(results_path):
- logger.info("Creating result directory: " + results_path)
+ _LOGGER.info("Creating result directory: " + results_path)
os.makedirs(results_path)
if settings.getValue('mode') == 'trafficgen':
# execute only traffic generator
- logging.debug("Executing traffic generator:")
+ _LOGGER.debug("Executing traffic generator:")
loader = Loader()
# set traffic details, so they can be passed to traffic ctl
traffic = copy.deepcopy(TRAFFIC_DEFAULTS)
@@ -466,7 +632,7 @@ def main():
loader.get_trafficgen_class())
with traffic_ctl:
traffic_ctl.send_traffic(traffic)
- logging.debug("Traffic Results:")
+ _LOGGER.debug("Traffic Results:")
traffic_ctl.print_results()
else:
# configure tests
@@ -479,48 +645,16 @@ def main():
for cfg in testcases:
try:
if args['integration']:
- all_tests.append(IntegrationTestCase(cfg, results_path))
+ all_tests.append(IntegrationTestCase(cfg))
else:
- all_tests.append(PerformanceTestCase(cfg, results_path))
+ all_tests.append(PerformanceTestCase(cfg))
except (Exception) as _:
- logger.exception("Failed to create test: %s",
- cfg.get('Name', '<Name not set>'))
+ _LOGGER.exception("Failed to create test: %s",
+ cfg.get('Name', '<Name not set>'))
+ vsperf_finalize()
raise
- # if required, handle list-* operations
-
- if args['list']:
- print("Available Tests:")
- print("================")
- for test in all_tests:
- print('* %-30s %s' % ('%s:' % test.name, test.desc))
- exit()
-
- if args['list_trafficgens']:
- print(Loader().get_trafficgens_printable())
- exit()
-
- if args['list_collectors']:
- print(Loader().get_collectors_printable())
- exit()
-
- if args['list_vswitches']:
- print(Loader().get_vswitches_printable())
- exit()
-
- if args['list_vnfs']:
- print(Loader().get_vnfs_printable())
- exit()
-
- if args['list_settings']:
- print(str(settings))
- exit()
-
# select requested tests
- if args['exact_test_name'] and args['tests']:
- logger.error("Cannot specify tests with both positional args and --test.")
- sys.exit(1)
-
if args['exact_test_name']:
exact_names = args['exact_test_name']
# positional args => exact matches only
@@ -533,7 +667,8 @@ def main():
selected_tests = all_tests
if not selected_tests:
- logger.error("No tests matched --test option or positional args. Done.")
+ _LOGGER.error("No tests matched --test option or positional args. Done.")
+ vsperf_finalize()
sys.exit(1)
# run tests
@@ -544,12 +679,12 @@ def main():
suite.addTest(MockTestCase('', True, test.name))
#pylint: disable=broad-except
except (Exception) as ex:
- logger.exception("Failed to run test: %s", test.name)
+ _LOGGER.exception("Failed to run test: %s", test.name)
suite.addTest(MockTestCase(str(ex), False, test.name))
- logger.info("Continuing with next test...")
+ _LOGGER.info("Continuing with next test...")
# generate final rst report with results of all executed TCs
- generate_final_report(results_path)
+ generate_final_report()
if settings.getValue('XUNIT'):
xmlrunner.XMLTestRunner(
@@ -574,13 +709,9 @@ def main():
int_data['cuse'] = True
opnfvdashboard.results2opnfv_dashboard(results_path, int_data)
- #remove directory if no result files were created.
- if os.path.exists(results_path):
- files_list = os.listdir(results_path)
- if files_list == []:
- shutil.rmtree(results_path)
+ # cleanup before exit
+ vsperf_finalize()
if __name__ == "__main__":
main()
-
diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py
index 6a380b1b..f5fecc2a 100644
--- a/vswitches/ovs_vanilla.py
+++ b/vswitches/ovs_vanilla.py
@@ -32,12 +32,12 @@ class OvsVanilla(IVSwitchOvs):
see the interface definition.
"""
- _ports = settings.getValue('VSWITCH_VANILLA_PHY_PORT_NAMES')
_current_id = 0
_vport_id = 0
def __init__(self):
super(OvsVanilla, self).__init__()
+ self._ports = list(nic['device'] for nic in settings.getValue('NICS'))
self._logger = logging.getLogger(__name__)
self._vswitchd_args = ["unix:%s" % VSwitchd.get_db_sock_path()]
self._vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS')
@@ -77,8 +77,7 @@ class OvsVanilla(IVSwitchOvs):
def add_phy_port(self, switch_name):
"""
- Method adds port based on configured VSWITCH_VANILLA_PHY_PORT_NAMES
- stored in config file.
+ Method adds port based on detected device names.
See IVswitch for general description
"""
@@ -89,8 +88,8 @@ class OvsVanilla(IVSwitchOvs):
raise
if not self._ports[self._current_id]:
- self._logger.error("VSWITCH_VANILLA_PHY_PORT_NAMES not set")
- raise ValueError("Invalid VSWITCH_VANILLA_PHY_PORT_NAMES")
+ self._logger.error("Can't detect device name for NIC %s", self._current_id)
+ raise ValueError("Invalid device name for %s" % self._current_id)
bridge = self._bridges[switch_name]
port_name = self._ports[self._current_id]