paths: Support binary packages 15/20015/6
authorMartin Klozik <martinx.klozik@intel.com>
Fri, 26 Aug 2016 14:39:29 +0000 (15:39 +0100)
committerMartin Klozik <martinx.klozik@intel.com>
Thu, 15 Sep 2016 13:37:44 +0000 (14:37 +0100)
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>
22 files changed:
conf/00_common.conf
conf/02_vswitch.conf
conf/04_vnf.conf
docs/design/vswitchperf_design.rst
docs/userguide/integration.rst
docs/userguide/testusage.rst
src/dpdk/dpdk.py
src/dpdk/testpmd_proc.py
src/ovs/dpctl.py
src/ovs/ofctl.py
testcases/testcase.py
tools/functions.py
tools/module_manager.py
tools/networkcard.py
tools/report/report.py
tools/systeminfo.py
vnfs/qemu/qemu.py
vnfs/qemu/qemu_dpdk_vhost_user.py
vnfs/qemu/qemu_pci_passthrough.py
vswitches/ovs.py
vswitches/ovs_dpdk_vhost.py
vswitches/ovs_vanilla.py

index fe4e1f5..7f30deb 100644 (file)
@@ -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
index abca63b..e504d3a 100644 (file)
 # See the License for the specific language governing permissions and
 # 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'
 
index 2e86b35..dcdf331 100644 (file)
@@ -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'
index cdf9f31..375fa12 100755 (executable)
@@ -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
 ------------------------------
 
index 51c2f24..b0926d8 100755 (executable)
@@ -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
index ce647c6..3c5cc4d 100755 (executable)
@@ -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.
 
index bd9bb9c..1622391 100644 (file)
@@ -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,44 +89,13 @@ 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.
     """
     _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']),
index 990ef8d..a8fa8ee 100644 (file)
@@ -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
 
index 8ecac6d..44a4ec9 100644 (file)
@@ -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(
index a75d0be..27349a9 100644 (file)
@@ -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)
 
index 6e215b4..06e9cd2 100644 (file)
@@ -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
index 60ed080..3bd8cc4 100644 (file)
 """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)
index 2eb4c63..911f725 100644 (file)
@@ -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 []
index 8d704fd..945534b 100644 (file)
@@ -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)
index 7d99101..1115f05 100644 (file)
@@ -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
index 50dc17e..fb1616d 100644 (file)
@@ -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
index c9569ae..01c16a0 100644 (file)
@@ -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',
index fc46aba..7e418e1 100644 (file)
@@ -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 +
index ba1f101..f32f33d 100644 (file)
@@ -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:
index d2814b6..886a98e 100644 (file)
 
 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
index c0764c8..327a697 100644 (file)
@@ -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
index 40ca970..12a460a 100644 (file)
@@ -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