summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--conf/03_traffic.conf33
-rw-r--r--conf/10_custom.conf25
-rw-r--r--docs/release/release-notes/release-notes.rst35
-rw-r--r--docs/testing/developer/devguide/results/results.rst42
-rw-r--r--docs/testing/user/configguide/installation.rst1
-rw-r--r--docs/testing/user/configguide/trafficgen.rst96
-rw-r--r--requirements.txt1
-rw-r--r--src/Makefile1
-rw-r--r--src/package-list.mk4
-rw-r--r--src/trex/Makefile55
-rwxr-xr-xsystems/opensuse/42.2/build_base_machine.sh4
-rwxr-xr-xsystems/opensuse/42.3/build_base_machine.sh93
-rwxr-xr-xsystems/opensuse/42.3/prepare_python_env.sh28
-rw-r--r--tools/opnfvdashboard/opnfvdashboard.py131
-rw-r--r--tools/pkt_gen/trex/__init__.py13
-rw-r--r--tools/pkt_gen/trex/trex.py338
-rw-r--r--tools/systeminfo.py4
-rwxr-xr-xvsperf15
18 files changed, 819 insertions, 100 deletions
diff --git a/conf/03_traffic.conf b/conf/03_traffic.conf
index 764fd731..179ff3e2 100644
--- a/conf/03_traffic.conf
+++ b/conf/03_traffic.conf
@@ -178,6 +178,7 @@ TRAFFICGEN = 'Dummy'
#TRAFFICGEN = 'Ixia'
#TRAFFICGEN = 'Xena'
#TRAFFICGEN = 'Moongen'
+#TRAFFICGEN = 'Trex'
# List of packet sizes to send.
# Expand like this: (64, 128, 256, 512, 1024)
@@ -415,4 +416,34 @@ TRAFFICGEN_MOONGEN_PORTS = ''
TRAFFICGEN_MOONGEN_LINE_SPEED_GBPS = ''
# MoonGen Configuration and Connection Info-- END
-###################################################
+#################################################
+
+################################################
+# Trex Configuration and Connection Info-- BEGIN
+
+# Example: TRAFFICGEN_TREX_HOST_IP_ADDR = "192.10.1.1"
+# Example: TRAFFICGEN_TREX_USER = 'root'
+# Example: TRAFFICGEN_TREX_BASE_DIR = '/traffic_gen/trex/'
+# Example: TRAFFICGEN_TREX_PORT1 = '00:00:00:00:00:00'
+TRAFFICGEN_TREX_HOST_IP_ADDR = ''
+TRAFFICGEN_TREX_USER = ''
+TRAFFICGEN_TREX_BASE_DIR = ''
+TRAFFICGEN_TREX_PORT1 = ''
+TRAFFICGEN_TREX_PORT2 = ''
+# Latency statistics are collected by separate stream created for each interface.
+# Parameter below defines frequency of packets used for latency measurement in PPS.
+# Value 0 will disable latency specific streams.
+TRAFFICGEN_TREX_LATENCY_PPS = 1000
+# Example 10 Gbps: TRAFFICGEN_TREXINE_SPEED_GBPS = '10'
+# Today only 10 Gbps is supported
+TRAFFICGEN_TREX_LINE_SPEED_GBPS = '10'
+PATHS['trafficgen'] = {
+ 'trex': {
+ 'type' : 'src',
+ 'src': {
+ 'path': os.path.join(ROOT_DIR, 'src/trex/trex/scripts/automation/trex_control_plane/stl')
+ }
+ }
+}
+# TREX Configuration and Connection Info-- END
+##############################################
diff --git a/conf/10_custom.conf b/conf/10_custom.conf
index 9622fd71..6011e6a8 100644
--- a/conf/10_custom.conf
+++ b/conf/10_custom.conf
@@ -21,6 +21,7 @@ TRAFFICGEN = 'Dummy'
#TRAFFICGEN = 'Ixia'
#TRAFFICGEN = 'Xena'
#TRAFFICGEN = 'Moongen'
+#TRAFFICGEN = 'Trex'
###########################################
# Spirent TestCenter Configuration -- BEGIN
@@ -116,6 +117,30 @@ TRAFFICGEN_MOONGEN_LINE_SPEED_GBPS = '10'
# MoonGen Configuration and Connection Info-- END
###################################################
+###################################################
+# TREX Configuration and Connection Info-- BEGIN
+
+# Example: TRAFFICGEN_TREX_HOST_IP_ADDR = "192.10.1.1"
+# Example: TRAFFICGEN_TREX_USER = 'root'
+# Example: TRAFFICGEN_TREX_BASE_DIR = '/traffic_gen/trex/'
+# Example: TRAFFICGEN_TREX_PORT1 = '00:00:00:00:00:00'
+TRAFFICGEN_TREX_HOST_IP_ADDR = ''
+TRAFFICGEN_TREX_USER = ''
+TRAFFICGEN_TREX_BASE_DIR = ''
+TRAFFICGEN_TREX_PORT1 = ''
+TRAFFICGEN_TREX_PORT2 = ''
+# Latency statistics are collected by separate stream created for each interface.
+# Parameter below defines frequency of packets used for latency measurement in PPS.
+# Value 0 will disable latency specific streams.
+TRAFFICGEN_TREX_LATENCY_PPS = 1000
+# Example 10 Gbps: TRAFFICGEN_TREXINE_SPEED_GBPS = '10'
+# Today only 10 Gbps is supported
+TRAFFICGEN_TREX_LINE_SPEED_GBPS = '10'
+
+# TREX Configuration and Connection Info-- END
+####################################################
+
+####################################################
#TEST_PARAMS = {'TRAFFICGEN_PKT_SIZES':(64,)}
OPNFV_INSTALLER = "Fuel"
OPNFV_URL = "http://testresults.opnfv.org/test/api/v1"
diff --git a/docs/release/release-notes/release-notes.rst b/docs/release/release-notes/release-notes.rst
index c2dff390..860cca77 100644
--- a/docs/release/release-notes/release-notes.rst
+++ b/docs/release/release-notes/release-notes.rst
@@ -2,6 +2,41 @@
.. http://creativecommons.org/licenses/by/4.0
.. (c) OPNFV, Intel Corporation, AT&T and others.
+OPNFV Euphrates Release
+=======================
+
+* Improvement of stepdriven testcases
+* Support for graph plotting from vsperf results
+* Support for vHost User client mode in OVS and VPP
+* Support for DPDK 17.02
+* Support for dpdk driver NIC binding by drivectl tool
+* Support for openSUSE Leap 42.3
+* Several bugfixes and small improvements
+
+* vSwitches
+
+ * Support for VPP virtual switch
+ * OVS: Support for jumbo frames
+
+* Traffic Generators:
+
+ * Support for Trex traffic generator
+ * Support for huge number of streams
+ * Ixia: L3, L4 or vlan headers can be turned off/on, support of 1 NIC connection
+ between DUT and Ixia, bugfixing
+ * MoonGen: fix multistream support
+ * Xena: option for final verification, JSON refactoring, support for xena
+ pairs topology and port removal options, bugfixes
+
+* Guest specific:
+
+ * Support for additional QEMU cpu features
+ * Support for pinning of vCPU threads
+
+* Integration tests:
+
+ * New VPP related testcases
+ * New multistream testcases focused on L3 and L4 performance of OVS and VPP
OPNFV Danube Release
====================
diff --git a/docs/testing/developer/devguide/results/results.rst b/docs/testing/developer/devguide/results/results.rst
index df9c52cb..0a0ab75d 100644
--- a/docs/testing/developer/devguide/results/results.rst
+++ b/docs/testing/developer/devguide/results/results.rst
@@ -7,32 +7,22 @@ OPNFV Test Results
VSPERF CI jobs are run daily and sample results can be found at
https://wiki.opnfv.org/display/vsperf/Vsperf+Results
-The following example maps the results in the test dashboard to the appropriate
-test case in the VSPERF Framework and specifies the metric the vertical/Y axis
-is plotting. **Please note**, the presence of dpdk within a test name signifies
-that the vswitch under test was OVS with DPDK, while its absence indicates that
-the vswitch under test was stock OVS.
+Testcase names shown in the dashboard are combination of orignal testcase
+name from VSPERF framework and indication of used vswitch.
-===================== ===================== ================== ===============
- Dashboard Test Framework Test Metric Guest Interface
-===================== ===================== ================== ===============
-tput_ovsdpdk phy2phy_tput Throughput (FPS) N/A
-tput_ovs phy2phy_tput Throughput (FPS) N/A
-b2b_ovsdpdk back2back Back-to-back value N/A
-b2b_ovs back2back Back-to-back value N/A
-tput_mod_vlan_ovs phy2phy_tput_mod_vlan Throughput (FPS) N/A
-tput_mod_vlan_ovsdpdk phy2phy_tput_mod_vlan Throughput (FPS) N/A
-scalability_ovs phy2phy_scalability Throughput (FPS) N/A
-scalability_ovsdpdk phy2phy_scalability Throughput (FPS) N/A
-pvp_tput_ovsdpdkuser pvp_tput Throughput (FPS) vhost-user
-pvp_tput_ovsvirtio pvp_tput Throughput (FPS) virtio-net
-pvp_b2b_ovsdpdkuser pvp_back2back Back-to-back value vhost-user
-pvp_b2b_ovsvirtio pvp_back2back Back-to-back value virtio-net
-pvvp_tput_ovsdpdkuser pvvp_tput Throughput (FPS) vhost-user
-pvvp_tput_ovsvirtio pvvp_tput Throughput (FPS) virtio-net
-pvvp_b2b_ovsdpdkuser pvvp_back2back Throughput (FPS) vhost-user
-pvvp_b2b_ovsvirtio pvvp_back2back Throughput (FPS) virtio-net
-===================== ===================== ================== ===============
+ Example:
-The loopback application in the VNF was used for PVP and PVVP scenarios was DPDK
+ Testcase ``phy2phy_tput`` is executed for three vSwitch types: ``OvsDpdkVhost``,
+ ``OvsVanilla`` and ``VppDpdkVhost``. In this case, following testcase names
+ will be used in the dashboard: ``phy2phy_tput_ovsdpdkvhost``,
+ ``phy2phy_tput_ovsvanilla`` and ``phy2phy_tput_vppdpdkvhost``.
+
+In case of RFC2544 Throughput test, the recorded metric is FPS (frames per
+second) without packet loss. For RFC2544 Back2Back test, the recorded metric
+is back-to-back value (number of frames) without packet loss.
+
+The loopback application in the VNF used for PVP and PVVP scenarios was DPDK
testpmd.
+
+Guest interface types are ``vhost-user`` for ``OvsDpdkVhost`` and ``VppDpdkVhost``
+and ``virtio-net`` for ``OvsVanilla``.
diff --git a/docs/testing/user/configguide/installation.rst b/docs/testing/user/configguide/installation.rst
index 207e50a4..837bee1c 100644
--- a/docs/testing/user/configguide/installation.rst
+++ b/docs/testing/user/configguide/installation.rst
@@ -48,6 +48,7 @@ Supported Operating Systems
* Fedora 24 (kernel 4.8 requires DPDK 16.11 and newer)
* Fedora 25 (kernel 4.9 requires DPDK 16.11 and newer)
* openSUSE 42.2
+* openSUSE 42.3
* RedHat 7.2 Enterprise Linux
* RedHat 7.3 Enterprise Linux
* Ubuntu 14.04
diff --git a/docs/testing/user/configguide/trafficgen.rst b/docs/testing/user/configguide/trafficgen.rst
index 1059ce12..3c827f38 100644
--- a/docs/testing/user/configguide/trafficgen.rst
+++ b/docs/testing/user/configguide/trafficgen.rst
@@ -18,6 +18,7 @@ VSPERF supports the following traffic generators:
* `Spirent TestCenter`_
* `Xena Networks`_
* MoonGen_
+ * Trex_
To see the list of traffic gens from the cli:
@@ -714,3 +715,98 @@ set to allow for proper connections to the host with MoonGen.
TRAFFICGEN_MOONGEN_BASE_DIR = ""
TRAFFICGEN_MOONGEN_PORTS = ""
TRAFFICGEN_MOONGEN_LINE_SPEED_GBPS = ""
+
+Trex
+----
+
+Installation
+~~~~~~~~~~~~
+
+Trex architecture overview and general installation instructions
+can be found here:
+
+https://trex-tgn.cisco.com/trex/doc/trex_stateless.html
+
+You can directly download from GitHub:
+
+.. code-block:: console
+
+ git clone https://github.com/cisco-system-traffic-generator/trex-core
+
+and use the master branch:
+
+.. code-block:: console
+
+ git checkout master
+
+or Trex latest release you can download from here:
+
+.. code-block:: console
+
+ wget --no-cache http://trex-tgn.cisco.com/trex/release/latest
+
+After download, Trex repo has to be built:
+
+.. code-block:: console
+
+ cd trex-core/linux_dpdk
+ ./b configure (run only once)
+ ./b build
+
+Next step is to create a minimum configuration file. It can be created by script ``dpdk_setup_ports.py``.
+The script with parameter ``-i`` will run in interactive mode and it will create file ``/etc/trex_cfg.yaml``.
+
+.. code-block:: console
+
+ cd trex-core/scripts
+ sudo ./dpdk_setup_ports.py -i
+
+Or example of configuration file can be found at location below, but it must be updated manually:
+
+.. code-block:: console
+
+ cp trex-core/scripts/cfg/simple_cfg /etc/trex_cfg.yaml
+
+For additional information about configuration file see official documentation (chapter 3.1.2):
+
+https://trex-tgn.cisco.com/trex/doc/trex_manual.html#_creating_minimum_configuration_file
+
+After compilation and configuration it is possible to run trex server in stateless mode.
+It is neccesary for proper connection between Trex server and VSPERF.
+
+.. code-block:: console
+
+ cd trex-core/scripts/
+ ./t-rex-64 -i
+
+For additional information about Trex stateless mode see Trex stateless documentation:
+
+https://trex-tgn.cisco.com/trex/doc/trex_stateless.html
+
+**NOTE:** One will need to set up ssh login to not use passwords between the server
+running Trex and the device under test (running the VSPERF test
+infrastructure). This is because VSPERF on one server uses 'ssh' to
+configure and run Trex upon the other server.
+
+One can set up this ssh access by doing the following on both servers:
+
+.. code-block:: console
+
+ ssh-keygen -b 2048 -t rsa
+ ssh-copy-id <other server>
+
+Configuration
+~~~~~~~~~~~~~
+
+Connection information for Trex must be supplied inside the custom configuration
+file. The following parameters must be set to allow for proper connections to the host with Trex.
+Example of this configuration is in conf/03_traffic.conf or conf/10_custom.conf.
+
+.. code-block:: console
+
+ TRAFFICGEN_TREX_HOST_IP_ADDR = ''
+ TRAFFICGEN_TREX_USER = ''
+ TRAFFICGEN_TREX_BASE_DIR = ''
+
+TRAFFICGEN_TREX_USER has to have sudo permission and passwordless access.
+TRAFFICGEN_TREX_BASE_DIR is the place, where is stored 't-rex-64' file.
diff --git a/requirements.txt b/requirements.txt
index 8b928d4d..33bee1bf 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,4 +12,5 @@ xmlrunner==1.7.7
requests==2.8.1
netaddr==0.7.18
scapy-python3==0.18
+pyzmq==14.5.0
distro
diff --git a/src/Makefile b/src/Makefile
index 6cd21dd6..db6c5e3c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -41,6 +41,7 @@ SUBDIRS += dpdk
SUBDIRS += ovs
SUBDIRS += qemu
SUBDIRS += vpp
+SUBDIRS += trex
ovs: dpdk
WITH_LINUX =
VHOST_USER = y
diff --git a/src/package-list.mk b/src/package-list.mk
index 5abb6018..cf2ff57f 100644
--- a/src/package-list.mk
+++ b/src/package-list.mk
@@ -26,3 +26,7 @@ VPP_TAG ?= v17.04
# QEMU section
QEMU_URL ?= https://github.com/qemu/qemu.git
QEMU_TAG ?= v2.5.0
+
+# TREX section
+TREX_URL ?= https://github.com/cisco-system-traffic-generator/trex-core.git
+TREX_TAG ?= 8bf9c16556843e55c232b64d9a5061bf588fad42
diff --git a/src/trex/Makefile b/src/trex/Makefile
new file mode 100644
index 00000000..9aaaa203
--- /dev/null
+++ b/src/trex/Makefile
@@ -0,0 +1,55 @@
+# makefile to manage trex package
+#
+
+# Copyright 2017 Martin Goldammer, OPNFV
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include ../mk/master.mk
+include ../package-list.mk
+
+WORK_DIR = trex
+TAG_DONE_FLAG = $(WORK_DIR)/.$(TREX_TAG).done
+
+.PHONY: force_pull
+
+all: force_pull
+ @echo "Finished pulling $(WORK_DIR) "
+
+force_pull: $(WORK_DIR) Makefile
+ $(AT)cd $(WORK_DIR) && git pull $(TREX_URL) $(TREX_TAG)
+ @echo "git pull done"
+
+$(WORK_DIR):
+ $(AT)git clone $(TREX_URL) $(WORK_DIR)
+
+$(TAG_DONE_FLAG): $(WORK_DIR)
+ $(AT)cd $(WORK_DIR); git checkout $(TREX_TAG)
+
+install:
+ @echo "Make install in $(WORK_DIR) (stub) "
+
+clobber:
+ $(AT)rm -rf $(WORK_DIR)
+
+distclean:
+ @echo "Make distclean in $(WORK_DIR) (stub) "
+
+clean:
+ @echo "Make clean in $(WORK_DIR) (stub) "
+
+test:
+ @echo "Make test in $(WORK_DIR) (stub) "
+
+sanity:
+ @echo "Make sanity in $(WORK_DIR) (stub) "
diff --git a/systems/opensuse/42.2/build_base_machine.sh b/systems/opensuse/42.2/build_base_machine.sh
index cf55484b..44d6f02b 100755
--- a/systems/opensuse/42.2/build_base_machine.sh
+++ b/systems/opensuse/42.2/build_base_machine.sh
@@ -19,8 +19,8 @@
# Contributors:
# Marco Varlese, SUSE LINUX GmbH
-zypper update
-zypper install $(echo "
+zypper -q -n update
+zypper -q -n install -y $(echo "
# compiler, tools and dependencies
make
automake
diff --git a/systems/opensuse/42.3/build_base_machine.sh b/systems/opensuse/42.3/build_base_machine.sh
new file mode 100755
index 00000000..277b8b1b
--- /dev/null
+++ b/systems/opensuse/42.3/build_base_machine.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+#
+# Build a base machine for openSUSE Leap 42.2 systems
+#
+# Copyright (c) 2017 SUSE LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Contributors:
+# Marco Varlese, SUSE LINUX GmbH
+
+zypper -q -n update
+zypper -q -n in -y $(echo "
+# compiler, tools and dependencies
+make
+automake
+gcc
+gcc-c++
+glibc
+glibc-devel
+fuse
+fuse-devel
+glib2-devel
+zlib-devel
+ncurses-devel
+kernel-default
+kernel-default-devel
+pkg-config
+findutils-locate
+curl
+automake
+autoconf
+vim
+wget
+git
+pciutils
+cifs-utils
+socat
+sysstat
+java-1_8_0-openjdk
+git-review
+
+# python
+python3
+python-pip
+python3-pip
+python3-setuptools
+python3-devel
+python3-tk
+
+# libraries
+libnuma1
+libnuma-devel
+libpixman-1-0
+libpixman-1-0-devel
+libtool
+libpcap-devel
+libnet9
+libncurses5
+libcurl4
+libcurl-devel
+libxml2
+libfuse2
+libopenssl1_0_0
+libopenssl-devel
+libpython3_4m1_0
+
+" | grep -v ^#)
+
+updatedb
+
+# fix for the Ixia TclClient
+ln -sf $(locate libc.so.6) /lib/libc.so.6
+
+# virtual environment for python
+pip3 install virtualenv
+
+# hugepages setup
+mkdir -p /dev/hugepages
+
+# fix for non-utf8 characters in file
+cp /etc/services /etc/services.bak
+iconv -o /etc/services -f utf-8 -t utf-8 -c /etc/services.bak
diff --git a/systems/opensuse/42.3/prepare_python_env.sh b/systems/opensuse/42.3/prepare_python_env.sh
new file mode 100755
index 00000000..66f94cf7
--- /dev/null
+++ b/systems/opensuse/42.3/prepare_python_env.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Prepare Python environment for vsperf execution on openSUSE Leap 42.2 systems
+#
+# Copyright (c) 2017 SUSE LLC.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [ -d "$VSPERFENV_DIR" ] ; then
+ echo "Directory $VSPERFENV_DIR already exists. Skipping python virtualenv creation."
+ exit
+fi
+
+virtualenv "$VSPERFENV_DIR"
+source "$VSPERFENV_DIR"/bin/activate
+pip install -r ../requirements.txt
+pip install pylint
+
diff --git a/tools/opnfvdashboard/opnfvdashboard.py b/tools/opnfvdashboard/opnfvdashboard.py
index c24b9f8c..a77a9c3a 100644
--- a/tools/opnfvdashboard/opnfvdashboard.py
+++ b/tools/opnfvdashboard/opnfvdashboard.py
@@ -17,10 +17,14 @@ vsperf2dashboard
import os
import csv
+import copy
import logging
+from datetime import datetime as dt
import requests
-def results2opnfv_dashboard(results_path, int_data):
+_DETAILS = {"64": '', "128": '', "512": '', "1024": '', "1518": ''}
+
+def results2opnfv_dashboard(tc_names, results_path, int_data):
"""
the method open the csv file with results and calls json encoder
"""
@@ -31,22 +35,33 @@ def results2opnfv_dashboard(results_path, int_data):
resfile = results_path + '/' + test
with open(resfile, 'r') as in_file:
reader = csv.DictReader(in_file)
- _push_results(reader, int_data)
+ tc_data = _prepare_results(reader, int_data)
+ _push_results(tc_data)
+ tc_names.remove(tc_data['id'])
+
+ # report TCs without results as FAIL
+ if tc_names:
+ tc_fail = copy.deepcopy(int_data)
+ tc_fail['start_time'] = dt.now().strftime('%Y-%m-%d %H:%M:%S')
+ tc_fail['stop_time'] = tc_fail['start_time']
+ tc_fail['criteria'] = 'FAIL'
+ tc_fail['version'] = 'N/A'
+ tc_fail['details'] = copy.deepcopy(_DETAILS)
+ for tc_name in tc_names:
+ tc_fail['dashboard_id'] = "{}_{}".format(tc_name, tc_fail['vswitch'])
+ _push_results(tc_fail)
-def _push_results(reader, int_data):
+def _prepare_results(reader, int_data):
"""
- the method encodes results and sends them into opnfv dashboard
+ the method prepares dashboard details for passed testcases
"""
- db_url = int_data['db_url']
- url = db_url + "/results"
- casename = ""
- version_ovs = ""
+ version_vswitch = ""
version_dpdk = ""
- version = ""
allowed_pkt = ["64", "128", "512", "1024", "1518"]
- details = {"64": '', "128": '', "512": '', "1024": '', "1518": ''}
- test_start = None
- test_stop = None
+ vswitch = None
+ details = copy.deepcopy(_DETAILS)
+ tc_data = copy.deepcopy(int_data)
+ tc_data['criteria'] = 'PASS'
for row_reader in reader:
if allowed_pkt.count(row_reader['packet_size']) == 0:
@@ -55,77 +70,65 @@ def _push_results(reader, int_data):
# test execution time includes all frame sizes, so start & stop time
# is the same (repeated) for every framesize in CSV file
- if test_start is None:
- test_start = row_reader['start_time']
- test_stop = row_reader['stop_time']
+ if not 'test_start' in tc_data:
+ tc_data['start_time'] = row_reader['start_time']
+ tc_data['stop_time'] = row_reader['stop_time']
+ tc_data['id'] = row_reader['id']
+ # CI job executes/reports TCs per vswitch type
+ vswitch = row_reader['vswitch']
- casename = _generate_test_name(row_reader['id'], int_data)
+ tc_data['dashboard_id'] = "{}_{}".format(row_reader['id'], row_reader['vswitch'].lower())
if "back2back" in row_reader['id']:
+ # 0 B2B frames is quite common, so we can't mark such TC as FAIL
details[row_reader['packet_size']] = row_reader['b2b_frames']
else:
details[row_reader['packet_size']] = row_reader['throughput_rx_fps']
+ # 0 PPS is definitelly a failure
+ if float(row_reader['throughput_rx_fps']) == 0:
+ tc_data['criteria'] = 'FAIL'
# Create version field
- with open(int_data['pkg_list'], 'r') as pkg_file:
+ with open(tc_data['pkg_list'], 'r') as pkg_file:
for line in pkg_file:
- if "OVS_TAG" in line:
- version_ovs = line.replace(' ', '')
- version_ovs = version_ovs.replace('OVS_TAG?=', '')
+ if "OVS_TAG" in line and vswitch.startswith('Ovs'):
+ version_vswitch = line.replace(' ', '')
+ version_vswitch = "OVS " + version_vswitch.replace('OVS_TAG?=', '')
+ if "VPP_TAG" in line and vswitch.startswith('Vpp'):
+ version_vswitch = line.replace(' ', '')
+ version_vswitch = "VPP " + version_vswitch.replace('VPP_TAG?=', '')
if "DPDK_TAG" in line:
- if int_data['vanilla'] is False:
+ # DPDK_TAG is not used by VPP, it downloads its onw DPDK version
+ if vswitch == "OvsDpdkVhost":
version_dpdk = line.replace(' ', '')
- version_dpdk = version_dpdk.replace('DPDK_TAG?=', '')
- else:
- version_dpdk = "not used"
- version = "OVS " + version_ovs.replace('\n', '') + " DPDK " + version_dpdk.replace('\n', '')
+ version_dpdk = " DPDK {}".format(
+ version_dpdk.replace('DPDK_TAG?=', ''))
+
+ tc_data['details'] = details
+ tc_data['version'] = version_vswitch.replace('\n', '') + version_dpdk.replace('\n', '')
+
+ return tc_data
+
+def _push_results(int_data):
+ """
+ the method sends testcase details into dashboard database
+ """
+ url = int_data['db_url'] + "/results"
# Build body
body = {"project_name": "vsperf",
"scenario": "vsperf",
- "start_date": test_start,
- "stop_date": test_stop,
- "case_name": casename,
+ "start_date": int_data['start_time'],
+ "stop_date": int_data['stop_time'],
+ "case_name": int_data['dashboard_id'],
"pod_name": int_data['pod'],
"installer": int_data['installer'],
- "version": version,
+ "version": int_data['version'],
"build_tag": int_data['build_tag'],
"criteria": int_data['criteria'],
- "details": details}
+ "details": int_data['details']}
my_data = requests.post(url, json=body)
- logging.info("Results for %s sent to opnfv, http response: %s", casename, my_data)
- logging.debug("opnfv url: %s", db_url)
+ logging.info("Results for %s sent to opnfv, http response: %s", int_data['dashboard_id'], my_data)
+ logging.debug("opnfv url: %s", int_data['db_url'])
logging.debug("the body sent to opnfv")
logging.debug(body)
-
-def _generate_test_name(testcase, int_data):
- """
- the method generates testcase name for releng
- """
- vanilla = int_data['vanilla']
- res_name = ""
-
- names = {'phy2phy_tput': ["tput_ovsdpdk", "tput_ovs"],
- 'back2back': ["b2b_ovsdpdk", "b2b_ovs"],
- 'phy2phy_tput_mod_vlan': ["tput_mod_vlan_ovsdpdk", "tput_mod_vlan_ovs"],
- 'phy2phy_cont': ["cont_ovsdpdk", "cont_ovs"],
- 'pvp_cont': ["pvp_cont_ovsdpdkuser", "pvp_cont_ovsvirtio"],
- 'pvvp_cont': ["pvvp_cont_ovsdpdkuser", "pvvp_cont_ovsvirtio"],
- 'phy2phy_scalability': ["scalability_ovsdpdk", "scalability_ovs"],
- 'pvp_tput': ["pvp_tput_ovsdpdkuser", "pvp_tput_ovsvirtio"],
- 'pvp_back2back': ["pvp_b2b_ovsdpdkuser", "pvp_b2b_ovsvirtio"],
- 'pvvp_tput': ["pvvp_tput_ovsdpdkuser", "pvvp_tput_ovsvirtio"],
- 'pvvp_back2back': ["pvvp_b2b_ovsdpdkuser", "pvvp_b2b_ovsvirtio"],
- 'phy2phy_cpu_load': ["cpu_load_ovsdpdk", "cpu_load_ovs"],
- 'phy2phy_mem_load': ["mem_load_ovsdpdk", "mem_load_ovs"]}
-
- for name, name_list in names.items():
- if name != testcase:
- continue
- if vanilla is True:
- res_name = name_list[1]
- else:
- res_name = name_list[0]
- break
-
- return res_name
diff --git a/tools/pkt_gen/trex/__init__.py b/tools/pkt_gen/trex/__init__.py
new file mode 100644
index 00000000..455a1ef0
--- /dev/null
+++ b/tools/pkt_gen/trex/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 Martin Goldammer OPNFV.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tools/pkt_gen/trex/trex.py b/tools/pkt_gen/trex/trex.py
new file mode 100644
index 00000000..ae262306
--- /dev/null
+++ b/tools/pkt_gen/trex/trex.py
@@ -0,0 +1,338 @@
+# Copyright 2017 Martin Goldammer, OPNFV, Red Hat Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+Trex Traffic Generator Model
+"""
+# pylint: disable=undefined-variable
+import logging
+import subprocess
+import sys
+from collections import OrderedDict
+# pylint: disable=unused-import
+import zmq
+from conf import settings
+from conf import merge_spec
+from core.results.results_constants import ResultsConstants
+from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
+# pylint: disable=wrong-import-position, import-error
+sys.path.append(settings.getValue('PATHS')['trafficgen']['trex']['src']['path'])
+from trex_stl_lib.api import *
+
+class Trex(ITrafficGenerator):
+ """Trex Traffic generator wrapper."""
+ _logger = logging.getLogger(__name__)
+
+ def __init__(self):
+ """Trex class constructor."""
+ super().__init__()
+ self._logger.info("In trex __init__ method")
+ self._params = {}
+ self._trex_host_ip_addr = (
+ settings.getValue('TRAFFICGEN_TREX_HOST_IP_ADDR'))
+ self._trex_base_dir = (
+ settings.getValue('TRAFFICGEN_TREX_BASE_DIR'))
+ self._trex_user = settings.getValue('TRAFFICGEN_TREX_USER')
+ self._stlclient = None
+
+ def connect(self):
+ '''Connect to Trex traffic generator
+
+ Verify that Trex is on the system indicated by
+ the configuration file
+ '''
+ self._stlclient = STLClient()
+ self._logger.info("TREX: In Trex connect method...")
+ if self._trex_host_ip_addr:
+ cmd_ping = "ping -c1 " + self._trex_host_ip_addr
+ else:
+ raise RuntimeError('TREX: Trex host not defined')
+
+ ping = subprocess.Popen(cmd_ping, shell=True, stderr=subprocess.PIPE)
+ output, error = ping.communicate()
+
+ if ping.returncode:
+ self._logger.error(error)
+ self._logger.error(output)
+ raise RuntimeError('TREX: Cannot ping Trex host at ' + \
+ self._trex_host_ip_addr)
+
+ connect_trex = "ssh " + self._trex_user + \
+ "@" + self._trex_host_ip_addr
+
+ cmd_find_trex = connect_trex + " ls " + \
+ self._trex_base_dir + "t-rex-64"
+
+
+ find_trex = subprocess.Popen(cmd_find_trex,
+ shell=True,
+ stderr=subprocess.PIPE)
+ output, error = find_trex.communicate()
+
+ if find_trex.returncode:
+ self._logger.error(error)
+ self._logger.error(output)
+ raise RuntimeError(
+ 'TREX: Cannot locate Trex program at %s within %s' \
+ % (self._trex_host_ip_addr, self._trex_base_dir))
+
+ self._stlclient = STLClient(username=self._trex_user, server=self._trex_host_ip_addr,
+ verbose_level=0)
+ self._stlclient.connect()
+ self._logger.info("TREX: Trex host successfully found...")
+
+ def disconnect(self):
+ """Disconnect from the traffic generator.
+
+ As with :func:`connect`, this function is optional.
+
+ Where implemented, this function should raise an exception on
+ failure.
+
+ :returns: None
+ """
+ self._logger.info("TREX: In trex disconnect method")
+ self._stlclient.disconnect(stop_traffic=True, release_ports=True)
+
+ @staticmethod
+ def create_packets(traffic, ports_info):
+ """Create base packet according to traffic specification.
+ If traffic haven't specified srcmac and dstmac fields
+ packet will be create with mac address of trex server.
+ """
+ mac_add = [li['hw_mac'] for li in ports_info]
+
+ if traffic and traffic['l2']['framesize'] > 0:
+ if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
+ traffic['l2']['srcmac'] == '00:00:00:00:00:00':
+ base_pkt_a = Ether(src=mac_add[0], dst=mac_add[1])/ \
+ IP(proto=traffic['l3']['proto'], src=traffic['l3']['srcip'],
+ dst=traffic['l3']['dstip'])/ \
+ UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
+ base_pkt_b = Ether(src=mac_add[1], dst=mac_add[0])/ \
+ IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
+ dst=traffic['l3']['srcip'])/ \
+ UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
+ else:
+ base_pkt_a = Ether(src=traffic['l2']['srcmac'], dst=traffic['l2']['dstmac'])/ \
+ IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
+ dst=traffic['l3']['srcip'])/ \
+ UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
+
+ base_pkt_b = Ether(src=traffic['l2']['dstmac'], dst=traffic['l2']['srcmac'])/ \
+ IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
+ dst=traffic['l3']['srcip'])/ \
+ UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
+
+ return (base_pkt_a, base_pkt_b)
+
+ @staticmethod
+ def create_streams(base_pkt_a, base_pkt_b, traffic):
+ """Add the base packet to the streams. Erase FCS and add payload
+ according to traffic specification
+ """
+ stream_1_lat = None
+ stream_2_lat = None
+ frame_size = int(traffic['l2']['framesize'])
+ fsize_no_fcs = frame_size - 4
+ payload_a = max(0, fsize_no_fcs - len(base_pkt_a)) * 'x'
+ payload_b = max(0, fsize_no_fcs - len(base_pkt_b)) * 'x'
+ pkt_a = STLPktBuilder(pkt=base_pkt_a/payload_a)
+ pkt_b = STLPktBuilder(pkt=base_pkt_b/payload_b)
+ stream_1 = STLStream(packet=pkt_a,
+ name='stream_1',
+ mode=STLTXCont(percentage=traffic['frame_rate']))
+ stream_2 = STLStream(packet=pkt_b,
+ name='stream_2',
+ mode=STLTXCont(percentage=traffic['frame_rate']))
+ lat_pps = settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS')
+ if lat_pps > 0:
+ stream_1_lat = STLStream(packet=pkt_a,
+ flow_stats=STLFlowLatencyStats(pg_id=0),
+ name='stream_1_lat',
+ mode=STLTXCont(pps=lat_pps))
+ stream_2_lat = STLStream(packet=pkt_b,
+ flow_stats=STLFlowLatencyStats(pg_id=1),
+ name='stream_2_lat',
+ mode=STLTXCont(pps=lat_pps))
+
+ return (stream_1, stream_2, stream_1_lat, stream_2_lat)
+
+ def generate_traffic(self, traffic, duration):
+ """The method that generate a stream
+ """
+ my_ports = [0, 1]
+ self._stlclient.reset(my_ports)
+ ports_info = self._stlclient.get_port_info(my_ports)
+ packet_1, packet_2 = Trex.create_packets(traffic, ports_info)
+ stream_1, stream_2, stream_1_lat, stream_2_lat = Trex.create_streams(packet_1, packet_2, traffic)
+ self._stlclient.add_streams(stream_1, ports=[0])
+ self._stlclient.add_streams(stream_2, ports=[1])
+
+ if stream_1_lat is not None:
+ self._stlclient.add_streams(stream_1_lat, ports=[0])
+ self._stlclient.add_streams(stream_2_lat, ports=[1])
+
+ self._stlclient.clear_stats()
+ self._stlclient.start(ports=[0, 1], force=True, duration=duration)
+ self._stlclient.wait_on_traffic(ports=[0, 1])
+ stats = self._stlclient.get_stats(sync_now=True)
+ return stats
+
+ @staticmethod
+ def calculate_results(stats):
+ """Calculate results from Trex statistic
+ """
+ result = OrderedDict()
+ result[ResultsConstants.TX_RATE_FPS] = (
+ '{:.3f}'.format(
+ float(stats["total"]["tx_pps"])))
+
+ result[ResultsConstants.THROUGHPUT_RX_FPS] = (
+ '{:.3f}'.format(
+ float(stats["total"]["rx_pps"])))
+
+ result[ResultsConstants.TX_RATE_MBPS] = (
+ '{:.3f}'.format(
+ float(stats["total"]["tx_bps"] / 1000000)))
+ result[ResultsConstants.THROUGHPUT_RX_MBPS] = (
+ '{:.3f}'.format(
+ float(stats["total"]["rx_bps"] / 1000000)))
+
+ result[ResultsConstants.TX_RATE_PERCENT] = 'Unknown'
+
+ result[ResultsConstants.THROUGHPUT_RX_PERCENT] = 'Unknown'
+
+ result[ResultsConstants.FRAME_LOSS_PERCENT] = (
+ '{:.3f}'.format(
+ float((stats["total"]["opackets"] - stats["total"]["ipackets"]) * 100 /
+ stats["total"]["opackets"])))
+
+ if settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS') > 0:
+ result[ResultsConstants.MIN_LATENCY_NS] = (
+ '{:.3f}'.format(
+ (float(min(stats["latency"][0]["latency"]["total_min"],
+ stats["latency"][1]["latency"]["total_min"])))))
+
+ result[ResultsConstants.MAX_LATENCY_NS] = (
+ '{:.3f}'.format(
+ (float(max(stats["latency"][0]["latency"]["total_max"],
+ stats["latency"][1]["latency"]["total_max"])))))
+
+ result[ResultsConstants.AVG_LATENCY_NS] = (
+ '{:.3f}'.format(
+ float((stats["latency"][0]["latency"]["average"]+
+ stats["latency"][1]["latency"]["average"])/2)))
+
+ else:
+ result[ResultsConstants.MIN_LATENCY_NS] = 'Unknown'
+ result[ResultsConstants.MAX_LATENCY_NS] = 'Unknown'
+ result[ResultsConstants.AVG_LATENCY_NS] = 'Unknown'
+ return result
+
+ def send_cont_traffic(self, traffic=None, duration=30):
+ """See ITrafficGenerator for description
+ """
+ self._logger.info("In Trex send_cont_traffic method")
+ self._params.clear()
+
+ self._params['traffic'] = self.traffic_defaults.copy()
+ if traffic:
+ self._params['traffic'] = merge_spec(
+ self._params['traffic'], traffic)
+
+ stats = self.generate_traffic(traffic, duration)
+
+ return self.calculate_results(stats)
+
+ def start_cont_traffic(self, traffic=None, duration=30):
+ raise NotImplementedError(
+ 'Trex start cont traffic not implemented')
+
+ def stop_cont_traffic(self):
+ """See ITrafficGenerator for description
+ """
+ raise NotImplementedError(
+ 'Trex stop_cont_traffic method not implemented')
+
+ def send_rfc2544_throughput(self, traffic=None, duration=60,
+ lossrate=0.0, tests=10):
+ """See ITrafficGenerator for description
+ """
+ self._logger.info("In Trex send_rfc2544_throughput method")
+ self._params.clear()
+ test_lossrate = 0
+ left = 0
+ num_test = 1
+ self._params['traffic'] = self.traffic_defaults.copy()
+ if traffic:
+ self._params['traffic'] = merge_spec(
+ self._params['traffic'], traffic)
+ new_params = copy.deepcopy(traffic)
+ stats = self.generate_traffic(traffic, duration)
+ right = traffic['frame_rate']
+ center = traffic['frame_rate']
+
+ while num_test <= tests:
+ test_lossrate = ((stats["total"]["opackets"] - stats["total"]
+ ["ipackets"]) * 100) / stats["total"]["opackets"]
+ self._logger.debug("Iteration: %s, frame rate: %s, throughput_rx_fps: %s, frame_loss_percent: %s",
+ num_test, "{:.3f}".format(new_params['frame_rate']), stats['total']['rx_pps'],
+ "{:.3f}".format(test_lossrate))
+ if test_lossrate == 0.0 and new_params['frame_rate'] == traffic['frame_rate']:
+ break
+ elif test_lossrate > lossrate:
+ right = center
+ center = (left+right) / 2
+ new_params = copy.deepcopy(traffic)
+ new_params['frame_rate'] = center
+ stats = self.generate_traffic(new_params, duration)
+ else:
+ left = center
+ center = (left+right) / 2
+ new_params = copy.deepcopy(traffic)
+ new_params['frame_rate'] = center
+ stats = self.generate_traffic(new_params, duration)
+ num_test += 1
+ return self.calculate_results(stats)
+
+ def start_rfc2544_throughput(self, traffic=None, tests=1, duration=60,
+ lossrate=0.0):
+ raise NotImplementedError(
+ 'Trex start rfc2544 throughput not implemented')
+
+ def wait_rfc2544_throughput(self):
+ raise NotImplementedError(
+ 'Trex wait rfc2544 throughput not implemented')
+
+ def send_burst_traffic(self, traffic=None, numpkts=100, duration=5):
+ raise NotImplementedError(
+ 'Trex send burst traffic not implemented')
+
+ def send_rfc2544_back2back(self, traffic=None, tests=1, duration=30,
+ lossrate=0.0):
+ raise NotImplementedError(
+ 'Trex send rfc2544 back2back not implemented')
+
+ def start_rfc2544_back2back(self, traffic=None, tests=1, duration=30,
+ lossrate=0.0):
+ raise NotImplementedError(
+ 'Trex start rfc2544 back2back not implemented')
+
+ def wait_rfc2544_back2back(self):
+ raise NotImplementedError(
+ 'Trex wait rfc2544 back2back not implemented')
+
+if __name__ == "__main__":
+ pass
diff --git a/tools/systeminfo.py b/tools/systeminfo.py
index 75b7aa0d..f34bcce6 100644
--- a/tools/systeminfo.py
+++ b/tools/systeminfo.py
@@ -230,6 +230,7 @@ def get_version(app_name):
'loopback_l2fwd' : os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/l2fwd.c'),
'ixnet' : os.path.join(S.getValue('TRAFFICGEN_IXNET_LIB_PATH'), 'pkgIndex.tcl'),
'ixia' : os.path.join(S.getValue('TRAFFICGEN_IXIA_ROOT_DIR'), 'lib/ixTcl1.0/ixTclHal.tcl'),
+ 'trex' : os.path.join(S.getValue('ROOT_DIR'), 'src/trex/trex'),
}
@@ -312,6 +313,9 @@ def get_version(app_name):
app_version = match_line(app_version_file['ixia'], 'package provide IxTclHal')
if app_version:
app_version = app_version.split(' ')[3]
+ elif app_name.lower() == 'trex':
+ app_version = match_line(os.path.join(app_version_file['trex'], 'VERSION'), 'v')
+ app_git_tag = get_git_tag(app_version_file['trex'])
elif app_name.lower() == 'xena':
try:
app_version = S.getValue('XENA_VERSION')
diff --git a/vsperf b/vsperf
index f4bc63b8..6efe53da 100755
--- a/vsperf
+++ b/vsperf
@@ -707,16 +707,17 @@ def main():
opnfv_url = settings.getValue('OPNFV_URL')
pkg_list = settings.getValue('PACKAGE_LIST')
- int_data = {'vanilla': False,
- 'pod': pod_name,
- 'criteria': "PASS",
+ int_data = {'pod': pod_name,
'build_tag': get_build_tag(),
'installer': installer_name,
'pkg_list': pkg_list,
- 'db_url': opnfv_url}
- if str(settings.getValue('VSWITCH')).endswith('Vanilla'):
- int_data['vanilla'] = True
- opnfvdashboard.results2opnfv_dashboard(results_path, int_data)
+ 'db_url': opnfv_url,
+ # pass vswitch name from configuration to be used for failed
+ # TCs; In case of successful TCs it is safer to use vswitch
+ # name from CSV as TC can override global configuration
+ 'vswitch': str(settings.getValue('VSWITCH')).lower()}
+ tc_names = [tc['Name'] for tc in selected_tests]
+ opnfvdashboard.results2opnfv_dashboard(tc_names, results_path, int_data)
# cleanup before exit
vsperf_finalize()