diff options
-rw-r--r-- | conf/02_vswitch.conf | 15 | ||||
-rw-r--r-- | conf/04_vnf.conf | 3 | ||||
-rw-r--r-- | core/component_factory.py | 5 | ||||
-rw-r--r-- | core/loader/loader_servant.py | 4 | ||||
-rw-r--r-- | core/pktfwd_controller.py | 9 | ||||
-rwxr-xr-x | docs/userguide/integration.rst | 1 | ||||
-rwxr-xr-x | docs/userguide/testusage.rst | 62 | ||||
-rw-r--r-- | src/dpdk/dpdk.py | 56 | ||||
-rw-r--r-- | testcases/integration.py | 4 | ||||
-rw-r--r-- | testcases/performance.py | 4 | ||||
-rw-r--r-- | testcases/testcase.py | 13 | ||||
-rw-r--r-- | tools/networkcard.py | 266 | ||||
-rw-r--r-- | tools/pkt_fwd/testpmd.py | 2 | ||||
-rw-r--r-- | tools/systeminfo.py | 5 | ||||
-rw-r--r-- | vnfs/qemu/qemu.py | 22 | ||||
-rw-r--r-- | vnfs/qemu/qemu_pci_passthrough.py | 87 | ||||
-rwxr-xr-x | vsperf | 273 | ||||
-rw-r--r-- | vswitches/ovs_vanilla.py | 9 |
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() @@ -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] |