diff options
author | Martin Klozik <martinx.klozik@intel.com> | 2016-08-26 15:39:29 +0100 |
---|---|---|
committer | Martin Klozik <martinx.klozik@intel.com> | 2016-09-15 14:37:44 +0100 |
commit | 9b1af783ec53050129239102355e1a5c3ceb1d97 (patch) | |
tree | 4a994af7b986af3c72c21c0ac87f398b42a44b74 | |
parent | acdb444a54124834acdde45107062eaf1452c119 (diff) |
paths: Support binary packages
Currently VSPERF supports OVS, DPDK and QEMU built
from the source code only. In some cases it is required
to support installation of these tools from binary packages
available for given linux distribution. Thus VSPERF
configuration and code was modified to suport both source
and binary versions of tools. This can be configured perf
tool, so various combinations of source and binary version
are supported.
Together with new configuration also a handling of kernel
modules was modified to automatically detect and load module
dependencies.
JIRA: VSPERF-340
JIRA: VSPERF-339
Change-Id: I855cb438cbd8998bdc499613ea5e7de2526299d7
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: Bill Michalowski <bmichalo@redhat.com>
Reviewed-by: Otto Sabart <osabart@redhat.com>
-rw-r--r-- | conf/00_common.conf | 54 | ||||
-rw-r--r-- | conf/02_vswitch.conf | 113 | ||||
-rw-r--r-- | conf/04_vnf.conf | 14 | ||||
-rwxr-xr-x | docs/design/vswitchperf_design.rst | 158 | ||||
-rwxr-xr-x | docs/userguide/integration.rst | 44 | ||||
-rwxr-xr-x | docs/userguide/testusage.rst | 13 | ||||
-rw-r--r-- | src/dpdk/dpdk.py | 51 | ||||
-rw-r--r-- | src/dpdk/testpmd_proc.py | 6 | ||||
-rw-r--r-- | src/ovs/dpctl.py | 5 | ||||
-rw-r--r-- | src/ovs/ofctl.py | 17 | ||||
-rw-r--r-- | testcases/testcase.py | 20 | ||||
-rw-r--r-- | tools/functions.py | 124 | ||||
-rw-r--r-- | tools/module_manager.py | 50 | ||||
-rw-r--r-- | tools/networkcard.py | 2 | ||||
-rw-r--r-- | tools/report/report.py | 5 | ||||
-rw-r--r-- | tools/systeminfo.py | 104 | ||||
-rw-r--r-- | vnfs/qemu/qemu.py | 2 | ||||
-rw-r--r-- | vnfs/qemu/qemu_dpdk_vhost_user.py | 2 | ||||
-rw-r--r-- | vnfs/qemu/qemu_pci_passthrough.py | 6 | ||||
-rw-r--r-- | vswitches/ovs.py | 81 | ||||
-rw-r--r-- | vswitches/ovs_dpdk_vhost.py | 3 | ||||
-rw-r--r-- | vswitches/ovs_vanilla.py | 9 |
22 files changed, 635 insertions, 248 deletions
diff --git a/conf/00_common.conf b/conf/00_common.conf index fe4e1f5d..7f30deb2 100644 --- a/conf/00_common.conf +++ b/conf/00_common.conf @@ -17,6 +17,7 @@ # ########################### import os +import copy # default language and encoding, which will be set in case # that locale is not set properly @@ -36,14 +37,51 @@ ROOT_DIR = os.path.normpath(os.path.join( TRAFFICGEN_DIR = os.path.join(ROOT_DIR, 'tools/pkt_gen') SYSMETRICS_DIR = os.path.join(ROOT_DIR, 'tools/collectors') -# deployment specific paths to OVS and DPDK -OVS_DIR_VANILLA = os.path.join(ROOT_DIR, 'src_vanilla/ovs/ovs/') - -RTE_SDK_USER = os.path.join(ROOT_DIR, 'src/dpdk/dpdk/') -OVS_DIR_USER = os.path.join(ROOT_DIR, 'src/ovs/ovs/') - -# the same qemu version is used for vanilla and vHost User -QEMU_DIR = os.path.join(ROOT_DIR, 'src/qemu/qemu/') +# Dictionary PATHS is used for configuration of vswitches, dpdk and qemu. +# It contains paths to various utilities, temporary directories and kernel +# modules used by VSPERF. Particular sections of PATHS dictionary are spread +# among several configuration files, i.e.: +# conf/02_vswtich.conf - configuration of vswitches (i.e. PATHS['vswitch']) +# and dpdk (i.e. PATHS['dpdk']) can be found there +# conf/04_vnf.conf - configuration of qemu (i.e. PATHS['qemu']) can +# be found there +# +# VSPERF will process PATHS dictionary before execution of every testcase +# and it will create a testcase specific dictionary TOOLS with paths to the +# utilities used by the test. During PATHS processing, following rules +# will apply for every item of PATHS dictionary: +# item 'type' - string, which defines the type of paths ('src' or 'bin') to be selected +# for a given section: +# 'src' means, that VSPERF will use OVS, DPDK or QEMU built from sources +# e.g. by execution of systems/build_base_machine.sh script during VSPERF +# installation +# 'bin' means, that VSPERF will use OVS, DPDK or QEMU binaries installed +# in the OS, e.g. via OS specific packaging system +# item 'path' - string with valid path; Its content is checked for existence, prefixed +# with section name and stored into TOOLS for later use +# e.g. TOOLS['dpdk_src'] or TOOLS['vswitch_src'] +# item 'modules' - list of strings; Every value from given list is checked for '.ko' +# suffix. In case it matches and it is not an absolute path to the module, then +# module name is prefixed with 'path' defined for the same section +# e.g. TOOLS['vswitch_modules'] = [ +# '/tmp/vsperf/src_vanilla/ovs/ovs/datapath/linux/openvswitch.ko'] +# all other items - string - if given string is a relative path and item 'path' +# is defined for a given section, then item content will be prefixed with +# content of the 'path'. Otherwise tool name will be searched within +# standard system directories. Also any OS filename wildcards will be +# expanded to the real path. At the end of processing, every absolute +# path is checked for its existence. In case that temporary path (i.e. path +# with '_tmp' suffix) doesn't exist, then log will be written and vsperf will +# continue. If any other path will not exist, then vsperf execution will +# be terminated with runtime error. +# +# Note: In case that 'bin' type is set for DPDK, then TOOLS['dpdk_src'] will be set to +# the value of PATHS['dpdk']['src']['path']. The reason is, that VSPERF uses downloaded +# DPDK sources to copy DPDK and testpmd into the GUEST, where testpmd is built. In case, +# that DPDK sources are not available, then vsperf will continue with test execution, +# but testpmd can't be used as a guest loopback. This is useful in case, that other guest +# loopback applications (e.g. buildin) are used by CI jobs, etc. +PATHS = {} # ############################ # Process configuration diff --git a/conf/02_vswitch.conf b/conf/02_vswitch.conf index abca63bb..e504d3ad 100644 --- a/conf/02_vswitch.conf +++ b/conf/02_vswitch.conf @@ -13,18 +13,6 @@ # limitations under the License. # ############################ -# Directories -# ############################ -# use DPDK VHOST USER by default -RTE_SDK = RTE_SDK_USER -OVS_DIR = OVS_DIR_USER - -OVS_VAR_DIR = '/usr/local/var/run/openvswitch/' -OVS_ETC_DIR = '/usr/local/etc/openvswitch/' - -VSWITCH_DIR = os.path.join(ROOT_DIR, 'vswitches') - -# ############################ # DPDK configuration # ############################ @@ -38,29 +26,83 @@ RTE_TARGET = 'x86_64-native-linuxapp-gcc' # 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 -# DPDK_MODULES = [ -# ('vfio-pci'), -# ] -DPDK_MODULES = [ - ('kmod', 'igb_uio'), -] +# vhost character device file used by dpdkvhostport QemuWrap cases +VHOST_DEV_FILE = 'ovs-vhost-net' -VHOST_MODULE = [ - ('eventfd_link', 'eventfd_link') -] +# location of vhost-user sockets relative to 'ovs_var_tmp' +VHOST_USER_SOCKS = 'dpdkvhostuser*' + +# please see conf/00_common.conf for description of PATHS dictionary +PATHS['dpdk'] = { + 'type' : 'src', + 'src': { + 'path': os.path.join(ROOT_DIR, 'src/dpdk/dpdk/'), + # To use vfio set: + # 'modules' : ['uio', 'vfio-pci'], + 'modules' : ['uio', os.path.join(RTE_TARGET, 'kmod/igb_uio.ko')], + 'bind-tool': 'tools/dpdk*bind.py', + 'testpmd': os.path.join(RTE_TARGET, 'app', 'testpmd'), + }, + 'bin': { + 'bind-tool': '/usr/share/dpdk/tools/dpdk*bind.py', + 'modules' : ['uio', 'igb_uio'], + 'testpmd' : 'testpmd' + } + } -# list of modules that will be inserted using 'modprobe' on system init -# To use vfio set -# SYS_MODULES = ['cuse'] -SYS_MODULES = ['uio', 'cuse'] +# ############################ +# Directories +# ############################ +VSWITCH_DIR = os.path.join(ROOT_DIR, 'vswitches') -# vhost character device file used by dpdkvhostport QemuWrap cases -VHOST_DEV_FILE = 'ovs-vhost-net' +# please see conf/00_common.conf for description of PATHS dictionary +# Every vswitch type supported by VSPERF must have its configuration +# stored inside PATHS['vswitch']. List of all supported vswitches +# can be obtained by call of ./vsperf --list-vswitches +# +# Directories defined by "ovs_var_tmp" and "ovs_etc_tmp" will be used +# by OVS to temporarily store its configuration, pid and socket files. +# In case, that these directories exist already, then their original +# content will be restored after the testcase execution. + +PATHS['vswitch'] = { + 'none' : { # used by SRIOV tests + 'type' : 'src', + 'src' : {}, + }, + 'OvsDpdkVhost': { + 'type' : 'src', + 'src': { + 'path': os.path.join(ROOT_DIR, 'src/ovs/ovs/'), + 'ovs-vswitchd': 'vswitchd/ovs-vswitchd', + 'ovsdb-server': 'ovsdb/ovsdb-server', + 'ovsdb-tool': 'ovsdb/ovsdb-tool', + 'ovsschema': 'vswitchd/vswitch.ovsschema', + 'ovs-vsctl': 'utilities/ovs-vsctl', + 'ovs-ofctl': 'utilities/ovs-ofctl', + 'ovs-dpctl': 'utilities/ovs-dpctl', + 'ovs-appctl': 'utilities/ovs-appctl', + }, + 'bin': { + 'ovs-vswitchd': 'ovs-vswitchd', + 'ovsdb-server': 'ovsdb-server', + 'ovsdb-tool': 'ovsdb-tool', + 'ovsschema': '/usr/share/openvswitch/vswitch.ovsschema', + 'ovs-vsctl': 'ovs-vsctl', + 'ovs-ofctl': 'ovs-ofctl', + 'ovs-dpctl': 'ovs-dpctl', + 'ovs-appctl': 'ovs-appctl', + } + }, + 'ovs_var_tmp': '/usr/local/var/run/openvswitch/', + 'ovs_etc_tmp': '/usr/local/etc/openvswitch/', +} -# location of vhost-user sockets -VHOST_USER_SOCKS = os.path.join(OVS_VAR_DIR, 'dpdkvhostuser*') +# default OvsVanilla configuration is similar to OvsDpdkVhost except 'path' and 'modules' +PATHS['vswitch'].update({'OvsVanilla' : copy.deepcopy(PATHS['vswitch']['OvsDpdkVhost'])}) +PATHS['vswitch']['OvsVanilla']['src']['path'] = os.path.join(ROOT_DIR, 'src_vanilla/ovs/ovs/') +PATHS['vswitch']['OvsVanilla']['src']['modules'] = ['datapath/linux/openvswitch.ko'] +PATHS['vswitch']['OvsVanilla']['bin']['modules'] = ['openvswitch'] # ############################ # vswitch configuration @@ -93,9 +135,6 @@ OVS_OLD_STYLE_MQ = False # parameters passed to ovs-vswitchd in case that OvsVanilla is selected VSWITCHD_VANILLA_ARGS = [] -# 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')] - # Bridge name to be used by VSWTICH VSWITCH_BRIDGE_NAME = 'br0' @@ -114,12 +153,6 @@ VSWITCH_AFFINITIZATION_ON = 1 VSWITCH_FLOW_TIMEOUT = '30000' -# list of tuples of format (path, module_name), which will be inserted -# using 'insmod' on system init - -# for OVS modules the path is in reference to the OVS directory. -OVS_MODULES = [] - # log file for ovs-vswitchd LOG_FILE_VSWITCHD = 'vswitchd.log' diff --git a/conf/04_vnf.conf b/conf/04_vnf.conf index 2e86b358..dcdf3316 100644 --- a/conf/04_vnf.conf +++ b/conf/04_vnf.conf @@ -20,10 +20,20 @@ VNF = 'QemuDpdkVhostUser' VNF_AFFINITIZATION_ON = True # ############################ -# Executables and log files +# Directories, executables and log files # ############################ -QEMU_BIN = os.path.join(QEMU_DIR, 'x86_64-softmmu/qemu-system-x86_64') +# please see conf/00_common.conf for description of PATHS dictionary +PATHS['qemu'] = { + 'type' : 'src', + 'src': { + 'path': os.path.join(ROOT_DIR, 'src/qemu/qemu/'), + 'qemu-system': 'x86_64-softmmu/qemu-system-x86_64' + }, + 'bin': { + 'qemu-system': 'qemu-system-x86_64' + } +} # log file for qemu LOG_FILE_QEMU = 'qemu.log' diff --git a/docs/design/vswitchperf_design.rst b/docs/design/vswitchperf_design.rst index cdf9f318..375fa12e 100755 --- a/docs/design/vswitchperf_design.rst +++ b/docs/design/vswitchperf_design.rst @@ -100,6 +100,164 @@ The values in the file specified by ``--conf-file`` takes precedence over all the other configuration files and does not have to follow the naming convention. +Configuration of PATHS dictionary +--------------------------------- + +VSPERF uses external tools like Open vSwitch and Qemu for execution of testcases. These +tools may be downloaded and built automatically by `VSPERF installation scripts`_ +or installed manually by user from binary packages. It is also possible to use a combination +of both approaches, but it is essential to correctly set paths to all required tools. +These paths are stored within a PATHS dictionary, which is evaluated before execution +of each testcase, in order to setup testcase specific environment. Values selected for testcase +execution are internally stored inside TOOLS dictionary, which is used by VSPERF to execute +external tools, load kernel modules, etc. + +The default configuration of PATHS dictionary is spread among three different configuration files +to follow logical grouping of configuration options. Basic description of PATHS dictionary +is placed inside ``conf/00_common.conf``. The configuration specific to DPDK and vswitches +is located at ``conf/02_vswitch.conf``. The last part related to the Qemu is defined inside +``conf/04_vnf.conf``. Default configuration values can be used in case, that all required +tools were downloaded and built automatically by vsperf itself. In case, that some of +tools were installed manually from binary packages, then it will be necessary to modify +the content of PATHS dictionary accordingly. + +Dictionary has a specific section of configuration options for every tool type, it means: + + * ``PATHS['vswitch']`` - contains a separete dictionary for each of vswitches supported by VSPEF + + Example: + + .. code-block:: python + + PATHS['vswitch'] = { + 'OvsDpdkVhost': { ... }, + 'OvsVanilla' : { ... }, + ... + } + + * ``PATHS['dpdk']`` - contains paths to the dpdk sources, kernel modules and tools (e.g. testpmd) + + Example: + + .. code-block:: python + + PATHS['dpdk'] = { + 'type' : 'src', + 'src': { + 'path': os.path.join(ROOT_DIR, 'src/dpdk/dpdk/'), + 'modules' : ['uio', os.path.join(RTE_TARGET, 'kmod/igb_uio.ko')], + 'bind-tool': 'tools/dpdk*bind.py', + 'testpmd': os.path.join(RTE_TARGET, 'app', 'testpmd'), + }, + ... + } + + * ``PATHS['qemu']`` - contains paths to the qemu sources and executable file + + Example: + + .. code-block:: python + + PATHS['qemu'] = { + 'type' : 'bin', + 'bin': { + 'qemu-system': 'qemu-system-x86_64' + }, + ... + } + +Every section specific to the particular vswitch, dpdk or qemu may contain following types +of configuration options: + + * option ``type`` - is a string, which defines the type of configured paths ('src' or 'bin') + to be selected for a given section: + + * value ``src`` means, that VSPERF will use vswitch, DPDK or QEMU built from sources + e.g. by execution of ``systems/build_base_machine.sh`` script during VSPERF + installation + + * value ``bin`` means, that VSPERF will use vswitch, DPDK or QEMU binaries installed + directly in the operating system, e.g. via OS specific packaging system + + * option ``path`` - is a string with a valid system path; Its content is checked for + existence, prefixed with section name and stored into TOOLS for later use + e.g. ``TOOLS['dpdk_src']`` or ``TOOLS['vswitch_src']`` + + * option ``modules`` - is list of strings with names of kernel modules; Every module name + from given list is checked for a '.ko' suffix. In case that it matches and if it is not + an absolute path to the module, then module name is prefixed with value of ``path`` + option defined for the same section + + Example: + + .. code-block:: python + + """ + snippet of PATHS definition from the configuration file: + """ + PATHS['vswitch'] = { + 'OvsVanilla' = { + 'type' : 'src', + 'src': { + 'path': '/tmp/vsperf/src_vanilla/ovs/ovs/', + 'modules' : ['datapath/linux/openvswitch.ko'], + ... + }, + ... + } + ... + } + + """ + Final content of TOOLS dictionary used during runtime: + """ + TOOLS['vswitch_modules'] = ['/tmp/vsperf/src_vanilla/ovs/ovs/datapath/linux/openvswitch.ko'] + + * all other options are strings with names and paths to specific tools; If a given string + contains a relative path and option ``path`` is defined for a given section, then string + content will be prefixed with content of the ``path``. Otherwise the name of the tool will be + searched within standard system directories. In case that filename contains OS specific + wildcards, then they will be expanded to the real path. At the end of the processing, every + absolute path will be checked for its existence. In case that temporary path (i.e. path with + a ``_tmp`` suffix) does not exist, then log will be written and vsperf will continue. If any + other path will not exist, then vsperf execution will be terminated with a runtime error. + + Example: + + .. code-block:: python + + """ + snippet of PATHS definition from the configuration file: + """ + PATHS['vswitch'] = { + 'OvsDpdkVhost': { + 'type' : 'src', + 'src': { + 'path': '/tmp/vsperf/src_vanilla/ovs/ovs/', + 'ovs-vswitchd': 'vswitchd/ovs-vswitchd', + 'ovsdb-server': 'ovsdb/ovsdb-server', + ... + } + ... + } + ... + } + + """ + Final content of TOOLS dictionary used during runtime: + """ + TOOLS['ovs-vswitchd'] = '/tmp/vsperf/src_vanilla/ovs/ovs/vswitchd/ovs-vswitchd' + TOOLS['ovsdb-server'] = '/tmp/vsperf/src_vanilla/ovs/ovs/ovsdb/ovsdb-server' + +Note: In case that ``bin`` type is set for DPDK, then ``TOOLS['dpdk_src']`` will be set to +the value of ``PATHS['dpdk']['src']['path']``. The reason is, that VSPERF uses downloaded +DPDK sources to copy DPDK and testpmd into the GUEST, where testpmd is built. In case, +that DPDK sources are not available, then vsperf will continue with test execution, +but testpmd can't be used as a guest loopback. This is useful in case, that other guest +loopback applications (e.g. buildin or l2fwd) are used. + +.. _VSPERF installation scripts: http://artifacts.opnfv.org/vswitchperf/docs/configguide/installation.html#other-requirements + Configuration of GUEST options ------------------------------ diff --git a/docs/userguide/integration.rst b/docs/userguide/integration.rst index 51c2f241..b0926d89 100755 --- a/docs/userguide/integration.rst +++ b/docs/userguide/integration.rst @@ -509,11 +509,15 @@ To run OVS NATIVE tunnel tests (VXLAN/GRE/GENEVE): VSWITCH = 'OvsVanilla' # Specify vport_* kernel module to test. - VSWITCH_VANILLA_KERNEL_MODULES = ['vport_vxlan', - 'vport_gre', - 'vport_geneve', - os.path.join(OVS_DIR_VANILLA, - 'datapath/linux/openvswitch.ko')] + PATHS['vswitch']['OvsVanilla']['src']['modules'] = [ + 'vport_vxlan', + 'vport_gre', + 'vport_geneve', + 'datapath/linux/openvswitch.ko', + ] + + **NOTE:** In case, that Vanilla OVS is installed from binary package, then + please set ``PATHS['vswitch']['OvsVanilla']['bin']['modules']`` instead. 3. Run tests: @@ -674,9 +678,10 @@ To run VXLAN decapsulation tests: .. code-block:: python - VSWITCH_VANILLA_KERNEL_MODULES = ['vport_vxlan', - os.path.join(OVS_DIR_VANILLA, - 'datapath/linux/openvswitch.ko')] + PATHS['vswitch']['OvsVanilla']['src']['modules'] = [ + 'vport_vxlan', + 'datapath/linux/openvswitch.ko', + ] DUT_NIC1_MAC = '<DUT NIC1 MAC ADDRESS>' @@ -714,6 +719,9 @@ To run VXLAN decapsulation tests: 'inner_dstport': 3001, } + **NOTE:** In case, that Vanilla OVS is installed from binary package, then + please set ``PATHS['vswitch']['OvsVanilla']['bin']['modules']`` instead. + 2. Run test: .. code-block:: console @@ -730,9 +738,10 @@ To run GRE decapsulation tests: .. code-block:: python - VSWITCH_VANILLA_KERNEL_MODULES = ['vport_gre', - os.path.join(OVS_DIR_VANILLA, - 'datapath/linux/openvswitch.ko')] + PATHS['vswitch']['OvsVanilla']['src']['modules'] = [ + 'vport_gre', + 'datapath/linux/openvswitch.ko', + ] DUT_NIC1_MAC = '<DUT NIC1 MAC ADDRESS>' @@ -769,6 +778,9 @@ To run GRE decapsulation tests: 'inner_dstport': 3001, } + **NOTE:** In case, that Vanilla OVS is installed from binary package, then + please set ``PATHS['vswitch']['OvsVanilla']['bin']['modules']`` instead. + 2. Run test: .. code-block:: console @@ -785,9 +797,10 @@ To run GENEVE decapsulation tests: .. code-block:: python - VSWITCH_VANILLA_KERNEL_MODULES = ['vport_geneve', - os.path.join(OVS_DIR_VANILLA, - 'datapath/linux/openvswitch.ko')] + PATHS['vswitch']['OvsVanilla']['src']['modules'] = [ + 'vport_geneve', + 'datapath/linux/openvswitch.ko', + ] DUT_NIC1_MAC = '<DUT NIC1 MAC ADDRESS>' @@ -824,6 +837,9 @@ To run GENEVE decapsulation tests: 'inner_dstport': 3001, } + **NOTE:** In case, that Vanilla OVS is installed from binary package, then + please set ``PATHS['vswitch']['OvsVanilla']['bin']['modules']`` instead. + 2. Run test: .. code-block:: console diff --git a/docs/userguide/testusage.rst b/docs/userguide/testusage.rst index ce647c6b..3c5cc4d4 100755 --- a/docs/userguide/testusage.rst +++ b/docs/userguide/testusage.rst @@ -306,15 +306,16 @@ To run tests using Vanilla OVS: Using vfio_pci with DPDK ^^^^^^^^^^^^^^^^^^^^^^^^^ -To use vfio with DPDK instead of igb_uio edit 'conf/02_vswitch.conf' -with the following parameters: +To use vfio with DPDK instead of igb_uio add into your custom configuration +file the following parameter: .. code-block:: console - DPDK_MODULES = [ - ('vfio-pci'), - ] - SYS_MODULES = ['cuse'] + PATHS['dpdk']['src']['modules'] = ['uio', 'vfio-pci'] + + +**NOTE:** In case, that DPDK is installed from binary package, then please +set ``PATHS['dpdk']['bin']['modules']`` instead. **NOTE:** Please ensure that Intel VT-d is enabled in BIOS. diff --git a/src/dpdk/dpdk.py b/src/dpdk/dpdk.py index bd9bb9cf..16223915 100644 --- a/src/dpdk/dpdk.py +++ b/src/dpdk/dpdk.py @@ -25,13 +25,11 @@ import subprocess import logging import glob -from conf import settings +from conf import settings as S from tools import tasks from tools.module_manager import ModuleManager _LOGGER = logging.getLogger(__name__) -RTE_PCI_TOOL = glob.glob(os.path.join( - settings.getValue('RTE_SDK_USER'), 'tools', 'dpdk*bind.py'))[0] _DPDK_MODULE_MANAGER = ModuleManager() @@ -48,7 +46,7 @@ def init(): """ global _NICS global _NICS_PCI - _NICS = settings.getValue('NICS') + _NICS = S.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...') @@ -91,18 +89,7 @@ def _insert_modules(): """Ensure required modules are inserted on system. """ - _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('SYS_MODULES')) - - mod_path_prefix = settings.getValue('OVS_DIR') - _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('OVS_MODULES'), - mod_path_prefix) - if 'vfio-pci' not in settings.getValue('DPDK_MODULES'): - mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'), - settings.getValue('RTE_TARGET')) - _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('DPDK_MODULES'), - mod_path_prefix) - else: - _DPDK_MODULE_MANAGER.insert_modules(settings.getValue('DPDK_MODULES')) + _DPDK_MODULE_MANAGER.insert_modules(S.getValue('TOOLS')['dpdk_modules']) def _remove_modules(): """Ensure required modules are removed from system. @@ -110,26 +97,6 @@ def _remove_modules(): _DPDK_MODULE_MANAGER.remove_modules() # -# vhost specific modules management -# - -def insert_vhost_modules(): - """Inserts VHOST related kernel modules - """ - mod_path_prefix = os.path.join(settings.getValue('RTE_SDK'), - 'lib', - 'librte_vhost') - _DPDK_MODULE_MANAGER.insert_module_group(settings.getValue('VHOST_MODULE'), mod_path_prefix) - - -def remove_vhost_modules(): - """Removes all VHOST related kernel modules - """ - # all modules are removed automatically by _remove_modules() method - pass - - -# # 'vhost-net' module cleanup # @@ -150,7 +117,8 @@ def _remove_vhost_net(): def _vhost_user_cleanup(): """Remove files created by vhost-user tests. """ - for sock in glob.glob(settings.getValue('VHOST_USER_SOCKS')): + for sock in glob.glob(os.path.join(S.getValue('TOOLS')['ovs_var_tmp'], + S.getValue('VHOST_USER_SOCKS'))): if os.path.exists(sock): try: tasks.run_task(['sudo', 'rm', sock], @@ -173,7 +141,7 @@ def _bind_nics(): """ try: _driver = 'igb_uio' - if 'vfio-pci' in settings.getValue('DPDK_MODULES'): + if 'vfio-pci' in S.getValue('TOOLS')['dpdk_modules']: _driver = 'vfio-pci' tasks.run_task(['sudo', 'chmod', 'a+x', '/dev/vfio'], _LOGGER, 'Setting VFIO permissions .. a+x', @@ -182,7 +150,8 @@ def _bind_nics(): _LOGGER, 'Setting VFIO permissions .. 0666', True) - tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind=' + _driver] + + tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], + '--bind=' + _driver] + _NICS_PCI, _LOGGER, 'Binding NICs %s...' % _NICS_PCI, True) @@ -193,7 +162,7 @@ def _unbind_nics(): """Unbind NICs using the Intel DPDK ``dpdk*bind.py`` tool. """ try: - tasks.run_task(['sudo', RTE_PCI_TOOL, '--unbind'] + + tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '--unbind'] + _NICS_PCI, _LOGGER, 'Unbinding NICs %s...' % str(_NICS_PCI), True) @@ -204,7 +173,7 @@ def _unbind_nics(): for nic in _NICS: try: if nic['driver']: - tasks.run_task(['sudo', RTE_PCI_TOOL, '--bind', + tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '--bind', nic['driver'], nic['pci']], _LOGGER, 'Binding NIC %s to %s...' % (nic['pci'], nic['driver']), diff --git a/src/dpdk/testpmd_proc.py b/src/dpdk/testpmd_proc.py index 990ef8da..a8fa8eee 100644 --- a/src/dpdk/testpmd_proc.py +++ b/src/dpdk/testpmd_proc.py @@ -27,10 +27,6 @@ from tools import tasks _TESTPMD_PROMPT = 'Done' -_TESTPMD_BIN = os.path.join( - settings.getValue('RTE_SDK'), settings.getValue('RTE_TARGET'), - 'app', 'testpmd') - _LOG_FILE_VSWITCHD = os.path.join( settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD')) @@ -57,7 +53,7 @@ class TestPMDProcess(tasks.Process): if not self._expect: self._expect = _TESTPMD_PROMPT testpmd_args = testpmd_args or [] - self._cmd = ['sudo', '-E', _TESTPMD_BIN] + testpmd_args + self._cmd = ['sudo', '-E', settings.getValue('TOOLS')['testpmd']] + testpmd_args # startup/shutdown diff --git a/src/ovs/dpctl.py b/src/ovs/dpctl.py index 8ecac6dc..44a4ec9b 100644 --- a/src/ovs/dpctl.py +++ b/src/ovs/dpctl.py @@ -23,9 +23,6 @@ import string from tools import tasks from conf import settings -_OVS_DPCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities', - 'ovs-dpctl') - _OVS_LOCAL_DATAPATH = 'ovs-system' class DPCtl(object): @@ -51,7 +48,7 @@ class DPCtl(object): :return: None """ - cmd = ['sudo', _OVS_DPCTL_BIN, + cmd = ['sudo', settings.getValue('TOOLS')['ovs-dpctl'], '--timeout', str(self.timeout)] + args return tasks.run_task( diff --git a/src/ovs/ofctl.py b/src/ovs/ofctl.py index a75d0be2..27349a92 100644 --- a/src/ovs/ofctl.py +++ b/src/ovs/ofctl.py @@ -28,13 +28,6 @@ import re from tools import tasks from conf import settings -_OVS_VSCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities', - 'ovs-vsctl') -_OVS_OFCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities', - 'ovs-ofctl') -_OVS_APPCTL_BIN = os.path.join(settings.getValue('OVS_DIR'), 'utilities', - 'ovs-appctl') - _OVS_BRIDGE_NAME = settings.getValue('VSWITCH_BRIDGE_NAME') _CACHE_FILE_NAME = '/tmp/vsperf_flows_cache' @@ -66,9 +59,9 @@ class OFBase(object): :return: None """ if self.timeout == -1: - cmd = ['sudo', _OVS_VSCTL_BIN, '--no-wait'] + args + cmd = ['sudo', settings.getValue('TOOLS')['ovs-vsctl'], '--no-wait'] + args else: - cmd = ['sudo', _OVS_VSCTL_BIN, '--timeout', str(self.timeout)] + args + cmd = ['sudo', settings.getValue('TOOLS')['ovs-vsctl'], '--timeout', str(self.timeout)] + args return tasks.run_task( cmd, self.logger, 'Running ovs-vsctl...', check_error) @@ -81,7 +74,7 @@ class OFBase(object): :return: None """ - cmd = ['sudo', _OVS_APPCTL_BIN, + cmd = ['sudo', settings.getValue('TOOLS')['ovs-appctl'], '--timeout', str(self.timeout)] + args return tasks.run_task( @@ -184,8 +177,8 @@ class OFBridge(OFBase): :return: None """ tmp_timeout = self.timeout if timeout == None else timeout - cmd = ['sudo', _OVS_OFCTL_BIN, '-O', 'OpenFlow13', '--timeout', - str(tmp_timeout)] + args + cmd = ['sudo', settings.getValue('TOOLS')['ovs-ofctl'], '-O', + 'OpenFlow13', '--timeout', str(tmp_timeout)] + args return tasks.run_task( cmd, self.logger, 'Running ovs-ofctl...', check_error) diff --git a/testcases/testcase.py b/testcases/testcase.py index 6e215b46..06e9cd24 100644 --- a/testcases/testcase.py +++ b/testcases/testcase.py @@ -67,18 +67,12 @@ class TestCase(object): self._update_settings('TEST_PARAMS', cfg.get('Parameters', S.getValue('TEST_PARAMS'))) # update global settings + functions.settings_update_paths() guest_loopback = get_test_param('guest_loopback', None) if guest_loopback: # we can put just one item, it'll be expanded automatically for all VMs self._update_settings('GUEST_LOOPBACK', [guest_loopback]) - if 'VSWITCH' in self._settings_original or 'VNF' in self._settings_original: - self._settings_original.update({ - 'RTE_SDK' : S.getValue('RTE_SDK'), - 'OVS_DIR' : S.getValue('OVS_DIR'), - }) - functions.settings_update_paths() - # set test parameters; CLI options take precedence to testcase settings self._logger = logging.getLogger(__name__) self.name = cfg['Name'] @@ -384,14 +378,19 @@ class TestCase(object): # copy sources into shared dir only if neccessary guest_loopback = set(S.getValue('GUEST_LOOPBACK')) - if 'testpmd' in guest_loopback or 'l2fwd' in guest_loopback: + if 'testpmd' in guest_loopback: try: tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"', - os.path.join(S.getValue('RTE_SDK_USER'), ''), + os.path.join(S.getValue('TOOLS')['dpdk_src'], ''), os.path.join(guest_dir, 'DPDK')], self._logger, 'Copying DPDK to shared directory...', True) + except subprocess.CalledProcessError: + self._logger.error('Unable to copy DPDK to shared directory') + raise + if 'l2fwd' in guest_loopback: + try: tasks.run_task(['rsync', '-a', '-r', '-l', os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/'), os.path.join(guest_dir, 'l2fwd')], @@ -399,7 +398,8 @@ class TestCase(object): 'Copying l2fwd to shared directory...', True) except subprocess.CalledProcessError: - self._logger.error('Unable to copy DPDK and l2fwd to shared directory') + self._logger.error('Unable to copy l2fwd to shared directory') + raise def _mount_hugepages(self): """Mount hugepages if usage of DPDK or Qemu is detected diff --git a/tools/functions.py b/tools/functions.py index 60ed0802..3bd8cc4d 100644 --- a/tools/functions.py +++ b/tools/functions.py @@ -15,20 +15,126 @@ """Various helper functions """ -from conf import settings +import os +import logging +import glob +import shutil +from conf import settings as S # # Support functions # def settings_update_paths(): - """ Configure paths to OVS and DPDK based on VSWITCH and VNF values + """ Configure paths to OVS, DPDK and QEMU sources and binaries based on + selected vswitch type and src/binary switch. Data are taken from + PATHS dictionary and after their processing they are stored inside TOOLS. + PATHS dictionary has specific section for 'vswitch', 'qemu' and 'dpdk' + Following processing is done for every item: + item 'type' - string, which defines the type of paths ('src' or 'bin') to be selected + for a given section: + 'src' means, that VSPERF will use OVS, DPDK or QEMU built from sources + e.g. by execution of systems/build_base_machine.sh script during VSPERF + installation + 'bin' means, that VSPERF will use OVS, DPDK or QEMU binaries installed + in the OS, e.g. via OS specific packaging system + item 'path' - string with valid path; Its content is checked for existence, prefixed + with section name and stored into TOOLS for later use + e.g. TOOLS['dpdk_src'] or TOOLS['vswitch_src'] + item 'modules' - list of strings; Every value from given list is checked for '.ko' + suffix. In case it matches and it is not an absolute path to the module, then + module name is prefixed with 'path' defined for the same section + e.g. TOOLS['vswitch_modules'] = [ + '/tmp/vsperf/src_vanilla/ovs/ovs/datapath/linux/openvswitch.ko'] + all other items - string - if given string is a relative path and item 'path' + is defined for a given section, then item content will be prefixed with + content of the 'path'. Otherwise tool name will be searched within + standard system directories. Also any OS filename wildcards will be + expanded to the real path. At the end of processing, every absolute + path is checked for its existence. In case that temporary path (i.e. path + with '_tmp' suffix) doesn't exist, then log will be written and vsperf will + continue. If any other path will not exist, then vsperf execution will + be terminated with runtime error. + + Note: In case that 'bin' type is set for DPDK, then TOOLS['dpdk_src'] will be set to + the value of PATHS['dpdk']['src']['path']. The reason is, that VSPERF uses downloaded + DPDK sources to copy DPDK and testpmd into the GUEST, where testpmd is built. In case, + that DPDK sources are not available, then vsperf will continue with test execution, + but testpmd can't be used as a guest loopback. This is useful in case, that other guest + loopback applications (e.g. buildin) are used by CI jobs, etc. """ # set dpdk and ovs paths accorfing to VNF and VSWITCH - if settings.getValue('VSWITCH').endswith('Vanilla'): - # settings paths for Vanilla - settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_VANILLA'))) - else: - # default - set to VHOST USER but can be changed during enhancement - settings.setValue('RTE_SDK', (settings.getValue('RTE_SDK_USER'))) - settings.setValue('OVS_DIR', (settings.getValue('OVS_DIR_USER'))) + paths = {} + vswitch_type = S.getValue('PATHS')['vswitch'][S.getValue('VSWITCH')]['type'] + paths['vswitch'] = S.getValue('PATHS')['vswitch'][S.getValue('VSWITCH')][vswitch_type] + paths['dpdk'] = S.getValue('PATHS')['dpdk'][S.getValue('PATHS')['dpdk']['type']] + paths['qemu'] = S.getValue('PATHS')['qemu'][S.getValue('PATHS')['qemu']['type']] + paths['paths'] = {} + paths['paths']['ovs_var_tmp'] = S.getValue('PATHS')['vswitch']['ovs_var_tmp'] + paths['paths']['ovs_etc_tmp'] = S.getValue('PATHS')['vswitch']['ovs_etc_tmp'] + + tools = {} + for path_class in paths: + for tool in paths[path_class]: + tmp_tool = paths[path_class][tool] + + # store valid path of given class into tools dict + if tool == 'path': + if os.path.isdir(tmp_tool): + tools['{}_src'.format(path_class)] = tmp_tool + continue + else: + raise RuntimeError('Path {} does not exist.'.format(tmp_tool)) + + # store list of modules of given class into tools dict + if tool == 'modules': + tmp_modules = [] + for module in tmp_tool: + # add path to the .ko modules and check it for existence + if module.endswith('.ko') and not os.path.isabs(module): + module = os.path.join(paths[path_class]['path'], module) + if not os.path.exists(module): + raise RuntimeError('Cannot locate modlue {}'.format(module)) + + tmp_modules.append(module) + + tools['{}_modules'.format(path_class)] = tmp_modules + continue + + # if path to the tool is relative, then 'path' will be prefixed + # in case that 'path' is not defined, then tool will be searched + # within standard system paths + if not os.path.isabs(tmp_tool): + if 'path' in paths[path_class]: + tmp_tool = os.path.join(paths[path_class]['path'], tmp_tool) + elif shutil.which(tmp_tool): + tmp_tool = shutil.which(tmp_tool) + else: + raise RuntimeError('Cannot locate tool {}'.format(tmp_tool)) + + # expand OS wildcards in paths if needed + if glob.has_magic(tmp_tool): + tmp_glob = glob.glob(tmp_tool) + if len(tmp_glob) == 0: + raise RuntimeError('Path to the {} is not valid: {}.'.format(tool, tmp_tool)) + elif len(tmp_glob) > 1: + raise RuntimeError('Path to the {} is ambiguous {}'.format(tool, tmp_glob)) + elif len(tmp_glob) == 1: + tmp_tool = tmp_glob[0] + elif not os.path.exists(tmp_tool): + if tool.endswith('_tmp'): + logging.getLogger().debug('Temporary path to the {} does not ' + 'exist: {}.'.format(tool, tmp_tool)) + else: + raise RuntimeError('Path to the {} is not valid: {}'.format(tool, tmp_tool)) + + tools[tool] = tmp_tool + + # ensure, that dpkg_src for bin will be set to downloaded DPDK sources, so it can + # be copied to the guest share dir and used by GUEST to build and run testpmd + # Validity of the path is not checked by purpose, so user can use VSPERF without + # downloading DPDK sources. In that case guest loopback can't be set to 'testpmd' + if S.getValue('PATHS')['dpdk']['type'] == 'bin': + tools['dpdk_src'] = S.getValue('PATHS')['dpdk']['src']['path'] + + S.setValue('TOOLS', tools) diff --git a/tools/module_manager.py b/tools/module_manager.py index 2eb4c63d..911f7252 100644 --- a/tools/module_manager.py +++ b/tools/module_manager.py @@ -31,30 +31,40 @@ class ModuleManager(object): """ self._modules = [] - def insert_module(self, module): + def insert_module(self, module, auto_remove=True): """Method inserts given module. In case that module name ends with .ko suffix then insmod will be used for its insertion. Otherwise modprobe will be called. :param module: a name of kernel module + :param auto_remove: if True (by default), then module will be + automatically removed by remove_modules() method """ module_base_name = os.path.basename(os.path.splitext(module)[0]) if self.is_module_inserted(module): self._logger.info('Module already loaded \'%s\'.', module_base_name) # add it to internal list, so we can try to remove it at the end - self._modules.append(module) + if auto_remove: + self._modules.append(module) return try: if module.endswith('.ko'): + # load module dependecies first, but suppress automatic + # module removal at the end; Just for case, that module + # depends on generic module + for depmod in self.get_module_dependecies(module): + self.insert_module(depmod, auto_remove=False) + tasks.run_task(['sudo', 'insmod', module], self._logger, 'Insmod module \'%s\'...' % module_base_name, True) else: tasks.run_task(['sudo', 'modprobe', module], self._logger, 'Modprobe module \'%s\'...' % module_base_name, True) - self._modules.append(module) + if auto_remove: + self._modules.append(module) except subprocess.CalledProcessError: # in case of error, show full module name self._logger.error('Unable to insert module \'%s\'.', module) @@ -68,17 +78,6 @@ class ModuleManager(object): for module in modules: self.insert_module(module) - def insert_module_group(self, module_group, path_prefix): - """Ensure all modules in a group are inserted into the system. - - :param module_group: A name of configuration item containing a list - of module names - :param path_prefix: A name of directory which contains given - group of modules - """ - for (path_suffix, module) in module_group: - self.insert_module(os.path.join(path_prefix, path_suffix, '%s.ko' % module)) - def remove_module(self, module): """Removes a single module. @@ -143,3 +142,26 @@ class ModuleManager(object): return line return None + + @staticmethod + def get_module_dependecies(module): + """Return list of modules, which must be loaded before module itself + + :param module: a name of kernel module + :returns: In case that module has any dependencies, then list of module + names will be returned. Otherwise it returns empty list, i.e. []. + """ + deps = '' + try: + # get list of module dependecies from kernel + deps = subprocess.check_output('modinfo -F depends {}'.format(module), + shell=True).decode().rstrip('\n') + except subprocess.CalledProcessError: + # in case of error, show full module name... + self._logger.info('Unable to get list of dependecies for module \'%s\'.', module) + # ...and try to continue, just for case that dependecies are already loaded + + if len(deps): + return deps.split(',') + else: + return [] diff --git a/tools/networkcard.py b/tools/networkcard.py index 8d704fd5..945534be 100644 --- a/tools/networkcard.py +++ b/tools/networkcard.py @@ -249,7 +249,7 @@ def reinit_vfs(pf_pci_handle): :param pf_pci_handle: PCI slot identifier of PF with domain part. """ - rte_pci_tool = glob.glob(os.path.join(settings.getValue('RTE_SDK'), 'tools', 'dpdk*bind.py'))[0] + rte_pci_tool = settings.getValue('TOOLS')['bind-tool'] for vf_nic in get_sriov_vfs_list(pf_pci_handle): nic_driver = get_driver(vf_nic) diff --git a/tools/report/report.py b/tools/report/report.py index 7d991011..1115f052 100644 --- a/tools/report/report.py +++ b/tools/report/report.py @@ -20,8 +20,8 @@ Generate reports in format defined by X. import sys import os -import jinja2 import logging +import jinja2 from core.results.results_constants import ResultsConstants from conf import settings as S @@ -64,7 +64,8 @@ def _get_env(result): if result[ResultsConstants.DEPLOYMENT].count('v'): env.update({'vnf': systeminfo.get_version(S.getValue('VNF')), 'guest_image': S.getValue('GUEST_IMAGE'), - 'loopback_app': list(map(systeminfo.get_version, S.getValue('GUEST_LOOPBACK'))), + 'loopback_app': list(map(systeminfo.get_loopback_version, + S.getValue('GUEST_LOOPBACK'))), }) return env diff --git a/tools/systeminfo.py b/tools/systeminfo.py index 50dc17e0..fb1616d4 100644 --- a/tools/systeminfo.py +++ b/tools/systeminfo.py @@ -19,6 +19,7 @@ import os import platform import subprocess import locale +import re from conf import settings as S @@ -176,6 +177,40 @@ def pid_isalive(pid): """ return os.path.isdir('/proc/' + str(pid)) +def get_bin_version(binary, regex): + """ get version of given binary selected by given regex + + :returns: version string or None + """ + try: + output = subprocess.check_output(binary, shell=True).decode().rstrip('\n') + except subprocess.CalledProcessError: + return None + + versions = re.findall(regex, output) + if len(versions): + return versions[0] + else: + return None + +def get_git_tag(path): + """ get tag of recent commit from repository located at 'path' + + :returns: git tag in form of string with commit hash or None if there + isn't any git repository at given path + """ + try: + if os.path.isdir(path): + return subprocess.check_output('cd {}; git rev-parse HEAD'.format(path), shell=True, + stderr=subprocess.DEVNULL).decode().rstrip('\n') + elif os.path.isfile(path): + return subprocess.check_output('cd $(dirname {}); git log -1 --pretty="%H" {}'.format(path, path), + shell=True, stderr=subprocess.DEVNULL).decode().rstrip('\n') + else: + return None + except subprocess.CalledProcessError: + return None + # This function uses long switch per purpose, so let us suppress pylint warning too-many-branches # pylint: disable=R0912 def get_version(app_name): @@ -186,45 +221,38 @@ def get_version(app_name): """ app_version_file = { - 'ovs' : os.path.join(S.getValue('OVS_DIR'), 'include/openvswitch/version.h'), - 'dpdk' : os.path.join(S.getValue('RTE_SDK'), 'lib/librte_eal/common/include/rte_version.h'), - 'qemu' : os.path.join(S.getValue('QEMU_DIR'), 'VERSION'), - 'l2fwd' : os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/l2fwd.c'), + 'ovs' : r'Open vSwitch\) ([0-9.]+)', + 'testpmd' : r'RTE Version: \'\S+ ([0-9.]+)', + 'qemu' : r'QEMU emulator version ([0-9.]+)', + 'loopback_l2fwd' : os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/l2fwd.c'), + 'loopback_testpmd' : os.path.join(S.getValue('TOOLS')['dpdk_src'], + 'lib/librte_eal/common/include/rte_version.h'), 'ixnet' : os.path.join(S.getValue('TRAFFICGEN_IXNET_LIB_PATH'), 'pkgIndex.tcl'), } - def get_git_tag(path): - """ get tag of recent commit from repository located at 'path' - - :returns: git tag in form of string with commit hash or None if there - isn't any git repository at given path - """ - try: - if os.path.isdir(path): - return subprocess.check_output('cd {}; git rev-parse HEAD'.format(path), shell=True, - stderr=subprocess.DEVNULL).decode().rstrip('\n') - elif os.path.isfile(path): - return subprocess.check_output('cd $(dirname {}); git log -1 --pretty="%H" {}'.format(path, path), - shell=True, stderr=subprocess.DEVNULL).decode().rstrip('\n') - else: - return None - except subprocess.CalledProcessError: - return None - app_version = None app_git_tag = None if app_name.lower().startswith('ovs'): - app_version = match_line(app_version_file['ovs'], '#define OVS_PACKAGE_VERSION') - if app_version: - app_version = app_version.split('"')[1] - app_git_tag = get_git_tag(S.getValue('OVS_DIR')) + app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['ovs-vswitchd']), + app_version_file['ovs']) + if 'vswitch_src' in S.getValue('TOOLS'): + app_git_tag = get_git_tag(S.getValue('TOOLS')['vswitch_src']) elif app_name.lower() in ['dpdk', 'testpmd']: + app_version = get_bin_version('{} -v -h'.format(S.getValue('TOOLS')['testpmd']), + app_version_file['testpmd']) + # we have to consult PATHS settings to be sure, that dpdk/testpmd + # were build from the sources + if S.getValue('PATHS')[app_name.lower()]['type'] == 'src': + app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src']) + elif app_name.lower() == 'loopback_testpmd': + # testpmd inside the guest is compiled from downloaded sources + # stored at TOOS['dpdk_src'] directory tmp_ver = ['', '', ''] dpdk_16 = False - with open(app_version_file['dpdk']) as file_: + with open(app_version_file['loopback_testpmd']) as file_: for line in file_: if not line.strip(): continue @@ -263,10 +291,12 @@ def get_version(app_name): if len(tmp_ver[0]): app_version = '.'.join(tmp_ver) - app_git_tag = get_git_tag(S.getValue('RTE_SDK')) + app_git_tag = get_git_tag(S.getValue('TOOLS')['dpdk_src']) elif app_name.lower().startswith('qemu'): - app_version = match_line(app_version_file['qemu'], '') - app_git_tag = get_git_tag(S.getValue('QEMU_DIR')) + app_version = get_bin_version('{} --version'.format(S.getValue('TOOLS')['qemu-system']), + app_version_file['qemu']) + if 'qemu_src' in S.getValue('TOOLS'): + app_git_tag = get_git_tag(S.getValue('TOOLS')['qemu_src']) elif app_name.lower() == 'ixnet': app_version = match_line(app_version_file['ixnet'], 'package provide IxTclNetwork') if app_version: @@ -283,13 +313,23 @@ def get_version(app_name): elif app_name.lower() == 'vswitchperf': app_git_tag = get_git_tag(S.getValue('ROOT_DIR')) elif app_name.lower() == 'l2fwd': - app_version = match_line(app_version_file[app_name], 'MODULE_VERSION') + app_version = match_line(app_version_file['loopback_l2fwd'], 'MODULE_VERSION') if app_version: app_version = app_version.split('"')[1] - app_git_tag = get_git_tag(app_version_file[app_name]) + app_git_tag = get_git_tag(app_version_file['loopback_l2fwd']) elif app_name.lower() in ['linux_bridge', 'buildin']: # without login into running VM, it is not possible to check bridge_utils version app_version = 'NA' app_git_tag = 'NA' return {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag} + +def get_loopback_version(loopback_app_name): + """ Get version of given guest loopback application and its git tag + + :returns: dictionary {'name' : app_name, 'version' : app_version, 'git_tag' : app_git_tag) in case that + version or git tag are not known or not applicaple, than None is returned for any unknown value + """ + version = get_version("loopback_{}".format(loopback_app_name)) + version['name'] = loopback_app_name + return version diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py index c9569ae6..01c16a0f 100644 --- a/vnfs/qemu/qemu.py +++ b/vnfs/qemu/qemu.py @@ -85,7 +85,7 @@ class IVnfQemu(IVnf): vnc = ':%d' % self._number # don't use taskset to affinize main qemu process; It causes hangup # of 2nd VM in case of DPDK. It also slows down VM responsivnes. - self._cmd = ['sudo', '-E', S.getValue('QEMU_BIN'), + self._cmd = ['sudo', '-E', S.getValue('TOOLS')['qemu-system'], '-m', S.getValue('GUEST_MEMORY')[self._number], '-smp', str(S.getValue('GUEST_SMP')[self._number]), '-cpu', 'host,migratable=off', diff --git a/vnfs/qemu/qemu_dpdk_vhost_user.py b/vnfs/qemu/qemu_dpdk_vhost_user.py index fc46aba1..7e418e18 100644 --- a/vnfs/qemu/qemu_dpdk_vhost_user.py +++ b/vnfs/qemu/qemu_dpdk_vhost_user.py @@ -52,7 +52,7 @@ class QemuDpdkVhostUser(IVnfQemu): self._cmd += ['-chardev', 'socket,id=char' + ifi + - ',path=' + S.getValue('OVS_VAR_DIR') + + ',path=' + S.getValue('TOOLS')['ovs_var_tmp'] + 'dpdkvhostuser' + ifi, '-netdev', 'type=vhost-user,id=' + net + diff --git a/vnfs/qemu/qemu_pci_passthrough.py b/vnfs/qemu/qemu_pci_passthrough.py index ba1f1010..f32f33d3 100644 --- a/vnfs/qemu/qemu_pci_passthrough.py +++ b/vnfs/qemu/qemu_pci_passthrough.py @@ -19,7 +19,6 @@ import logging import subprocess import os -import glob from conf import settings as S from vnfs.qemu.qemu import IVnfQemu @@ -27,7 +26,6 @@ from tools import tasks from tools.module_manager import ModuleManager _MODULE_MANAGER = ModuleManager() -_RTE_PCI_TOOL = glob.glob(os.path.join(S.getValue('RTE_SDK'), 'tools', 'dpdk*bind.py'))[0] class QemuPciPassthrough(IVnfQemu): """ @@ -60,7 +58,7 @@ class QemuPciPassthrough(IVnfQemu): # bind every interface to vfio-pci driver try: nics_list = list(tmp_nic['pci'] for tmp_nic in self._host_nics) - tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=vfio-pci'] + nics_list, + tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '--bind=vfio-pci'] + nics_list, self._logger, 'Binding NICs %s...' % nics_list, True) except subprocess.CalledProcessError: @@ -78,7 +76,7 @@ class QemuPciPassthrough(IVnfQemu): for nic in self._host_nics: if nic['driver']: try: - tasks.run_task(['sudo', _RTE_PCI_TOOL, '--bind=' + nic['driver'], nic['pci']], + tasks.run_task(['sudo', S.getValue('TOOLS')['bind-tool'], '--bind=' + nic['driver'], nic['pci']], self._logger, 'Binding NIC %s...' % nic['pci'], True) except subprocess.CalledProcessError: diff --git a/vswitches/ovs.py b/vswitches/ovs.py index d2814b6a..886a98e0 100644 --- a/vswitches/ovs.py +++ b/vswitches/ovs.py @@ -17,19 +17,17 @@ import logging import os -import pexpect import re import time +import datetime +import random +import pexpect from conf import settings from src.ovs import OFBridge, flow_key, flow_match from tools import tasks from vswitches.vswitch import IVSwitch -_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR') -_OVS_ETC_DIR = settings.getValue('OVS_ETC_DIR') - - class IVSwitchOvs(IVSwitch, tasks.Process): """Open vSwitch base class implementation @@ -37,23 +35,26 @@ class IVSwitchOvs(IVSwitch, tasks.Process): implementation. For generic information of the nature of the methods, see the interface. """ - _logfile = os.path.join(settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD')) - _ovsdb_pidfile_path = os.path.join(_OVS_VAR_DIR, "ovsdb-server.pid") - _vswitchd_pidfile_path = os.path.join(_OVS_VAR_DIR, "ovs-vswitchd.pid") _proc_name = 'ovs-vswitchd' def __init__(self): """See IVswitch for general description """ + self._logfile = os.path.join(settings.getValue('LOG_DIR'), + settings.getValue('LOG_FILE_VSWITCHD')) + self._ovsdb_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], + "ovsdb-server.pid") + self._vswitchd_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], + "{}.pid".format(self._proc_name)) self._logger = logging.getLogger(__name__) - self._expect = r'bridge|INFO|ovs-vswitchd' + self._expect = r'bridge|INFO|{}'.format(self._proc_name) self._timeout = 30 self._bridges = {} self._vswitchd_args = ['--pidfile=' + self._vswitchd_pidfile_path, '--overwrite-pidfile', '--log-file=' + self._logfile] self._cmd = [] - self._cmd_template = ['sudo', '-E', os.path.join(settings.getValue('OVS_DIR'), - 'vswitchd', 'ovs-vswitchd')] + self._cmd_template = ['sudo', '-E', settings.getValue('TOOLS')['ovs-vswitchd']] + self._stamp = None def start(self): """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance. @@ -62,6 +63,10 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ self._logger.info("Starting vswitchd...") + # insert kernel modules if required + if 'vswitch_modules' in settings.getValue('TOOLS'): + self._module_manager.insert_modules(settings.getValue('TOOLS')['vswitch_modules']) + self._cmd = self._cmd_template + self._vswitchd_args # DB must be started before vswitchd @@ -123,7 +128,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): raise NotImplementedError def add_veth_pair_port(self, switch_name=None, remote_switch_name=None, - local_opts=None, remote_opts=None): + local_opts=None, remote_opts=None): """Creates veth-pair port between 'switch_name' and 'remote_switch_name' """ @@ -140,8 +145,8 @@ class IVSwitchOvs(IVSwitch, tasks.Process): 'type=patch', 'options:peer=' + remote_port_name] remote_params = ['--', 'set', 'Interface', remote_port_name, - 'type=patch', - 'options:peer=' + local_port_name] + 'type=patch', + 'options:peer=' + local_port_name] if local_opts is not None: local_params = local_params + local_opts @@ -296,14 +301,20 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ self._logger.info('Resetting system after last run...') - tasks.run_task(['sudo', 'rm', '-rf', _OVS_VAR_DIR], self._logger) - tasks.run_task(['sudo', 'mkdir', '-p', _OVS_VAR_DIR], self._logger) - tasks.run_task(['sudo', 'rm', '-rf', _OVS_ETC_DIR], self._logger) - tasks.run_task(['sudo', 'mkdir', '-p', _OVS_ETC_DIR], self._logger) - - tasks.run_task(['sudo', 'rm', '-f', - os.path.join(_OVS_ETC_DIR, 'conf.db')], - self._logger) + # create a backup of ovs_var_tmp and ovs_etc_tmp; It is + # essential for OVS installed from binary packages. + self._stamp = '{:%Y%m%d_%H%M%S}_{}'.format(datetime.datetime.now(), + random.randrange(1000, 9999)) + for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']: + if os.path.exists(settings.getValue('TOOLS')[tmp_dir]): + orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir]) + self._logger.info('Creating backup of %s directory...', tmp_dir) + tasks.run_task(['sudo', 'mv', orig_dir, '{}.{}'.format(orig_dir, self._stamp)], + self._logger) + + # create fresh tmp dirs + tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_var_tmp']], self._logger) + tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_etc_tmp']], self._logger) self._logger.info('System reset after last run.') @@ -312,21 +323,18 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :returns: None """ - ovsdb_tool_bin = os.path.join( - settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-tool') + ovsdb_tool_bin = settings.getValue('TOOLS')['ovsdb-tool'] tasks.run_task(['sudo', ovsdb_tool_bin, 'create', - os.path.join(_OVS_ETC_DIR, 'conf.db'), - os.path.join(settings.getValue('OVS_DIR'), 'vswitchd', - 'vswitch.ovsschema')], + os.path.join(settings.getValue('TOOLS')['ovs_etc_tmp'], 'conf.db'), + settings.getValue('TOOLS')['ovsschema']], self._logger, 'Creating ovsdb configuration database...') - ovsdb_server_bin = os.path.join( - settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-server') + ovsdb_server_bin = settings.getValue('TOOLS')['ovsdb-server'] tasks.run_background_task( ['sudo', ovsdb_server_bin, - '--remote=punix:%s' % os.path.join(_OVS_VAR_DIR, 'db.sock'), + '--remote=punix:%s' % os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock'), '--remote=db:Open_vSwitch,Open_vSwitch,manager_options', '--pidfile=' + self._ovsdb_pidfile_path, '--overwrite-pidfile'], self._logger, @@ -346,13 +354,24 @@ class IVSwitchOvs(IVSwitch, tasks.Process): if ovsdb_pid: tasks.terminate_task(ovsdb_pid, logger=self._logger) + # restore original content of ovs_var_tmp and ovs_etc_tmp; It is + # essential for OVS installed from binary packages. + if self._stamp: + for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']: + orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir]) + if os.path.exists('{}.{}'.format(orig_dir, self._stamp)): + self._logger.info('Restoring backup of %s directory...', tmp_dir) + tasks.run_task(['sudo', 'rm', '-rf', orig_dir], self._logger) + tasks.run_task(['sudo', 'mv', '{}.{}'.format(orig_dir, self._stamp), orig_dir], + self._logger) + @staticmethod def get_db_sock_path(): """Method returns location of db.sock file :returns: path to db.sock file. """ - return os.path.join(_OVS_VAR_DIR, 'db.sock') + return os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock') # # validate methods required for integration testcases diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py index c0764c87..327a697d 100644 --- a/vswitches/ovs_dpdk_vhost.py +++ b/vswitches/ovs_dpdk_vhost.py @@ -84,7 +84,6 @@ class OvsDpdkVhost(IVSwitchOvs): super(OvsDpdkVhost, self).stop() dpdk.cleanup() - dpdk.remove_vhost_modules() def add_switch(self, switch_name, params=None): """See IVswitch for general description @@ -147,7 +146,7 @@ class OvsDpdkVhost(IVSwitchOvs): :returns: True if legacy --dpdk option is supported, otherwise it returns False """ - ovs_vswitchd_bin = os.path.join(settings.getValue('OVS_DIR'), 'vswitchd', 'ovs-vswitchd') + ovs_vswitchd_bin = settings.getValue('TOOLS')['ovs-vswitchd'] try: subprocess.check_output(ovs_vswitchd_bin + r' --help | grep "\-\-dpdk"', shell=True) return True diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py index 40ca970e..12a460af 100644 --- a/vswitches/ovs_vanilla.py +++ b/vswitches/ovs_vanilla.py @@ -43,15 +43,6 @@ class OvsVanilla(IVSwitchOvs): self._vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS') self._module_manager = ModuleManager() - def start(self): - """See IVswitch for general description - - Activates kernel modules, ovsdb and vswitchd. - """ - self._module_manager.insert_modules( - settings.getValue('VSWITCH_VANILLA_KERNEL_MODULES')) - super(OvsVanilla, self).start() - def stop(self): """See IVswitch for general description |