summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Klozik <martinx.klozik@intel.com>2016-01-11 17:56:45 +0100
committerMaryam Tahhan <maryam.tahhan@intel.com>2016-01-19 13:10:29 +0000
commit6da6b66a1affe7a79180a49534602a02e7994c7f (patch)
tree6471656e169abd25b6c26b01adb5aabacf854831
parentc28daf3e37f38ec570b70f33e9ce18a8e6b24f61 (diff)
reporting: add vswitch, vnf and trafficgen version into the report
Final test report MD file should contain information about version of vswitch, vnf, VM loopback forwarding application and traffic generator used during the test. In case that component is cloned from GIT repository, then hash of its recent commit should be part of the report too. Change-Id: I4eb398bc95bc5030d0852d08bcf9febbf17640d4 JIRA: VSPERF-172 Signed-off-by: Martin Klozik <martinx.klozik@intel.com> Reviewed-by: Maryam Tahhan <maryam.tahhan@intel.com> Reviewed-by: Radek Zetik <radekx.zetik@intel.com> Reviewed-by: Al Morton <acmorton@att.com> Reviewed-by: Brian Castelli <brian.castelli@spirent.com> Reviewed-by: Tv Rao <tv.rao@freescale.com>
-rw-r--r--tools/report/report.jinja11
-rw-r--r--tools/report/report.py39
-rw-r--r--tools/systeminfo.py135
-rw-r--r--vnfs/qemu/qemu.py5
-rwxr-xr-xvsperf11
5 files changed, 162 insertions, 39 deletions
diff --git a/tools/report/report.jinja b/tools/report/report.jinja
index 6542a202..f59dba72 100644
--- a/tools/report/report.jinja
+++ b/tools/report/report.jinja
@@ -61,7 +61,16 @@ Below is the environment that the test was performed in:
- CPU cores: {{tests[0].env.cpu_cores}}
- Memory: {{tests[0].env.memory}}
- Virtual Switch Set-up: {{tests[0].deployment}}
-- IxNetwork: {{tests[0].env.ixnetwork_ver}}
+- vswitchperf: GIT tag: {{tests[0].env.vsperf['git_tag']}}
+- Traffic Generator: {{tests[0].env.traffic_gen['name']}}, Version: {{tests[0].env.traffic_gen['version']}}, GIT tag: {{tests[0].env.traffic_gen['git_tag']}}
+- vSwitch: {{tests[0].env.vswitch['name']}}, Version: {{tests[0].env.vswitch['version']}}, GIT tag: {{tests[0].env.vswitch['git_tag']}}
+- DPDK Version: {{tests[0].env.dpdk['version']}}, GIT tag: {{tests[0].env.dpdk['git_tag']}}
+{%- if 'vnf' in tests[0].env %}
+- VNF: {{tests[0].env.vnf['name']}}, Version: {{tests[0].env.vnf['version']}}, GIT tag: {{tests[0].env.vnf['git_tag']}}
+- VM images: {% for guest_image in tests[0].env.guest_image %}{{guest_image}} {% endfor %}
+- VM loopback apps: {% for loopback_app in tests[0].env.loopback_app %}{{loopback_app['name']}}, Version: {{loopback_app['version']}}, GIT tag: {{loopback_app['git_tag']}}
+ {% endfor %}
+{%- endif %}
For each test, a summary of the key test results is provided.
{% for test in tests %}
diff --git a/tools/report/report.py b/tools/report/report.py
index d51ff47d..4264c055 100644
--- a/tools/report/report.py
+++ b/tools/report/report.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Intel Corporation.
+# Copyright 2015-2016 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -24,14 +24,14 @@ import jinja2
import logging
from core.results.results_constants import ResultsConstants
-from conf import settings
+from conf import settings as S
from tools import systeminfo
_TEMPLATE_FILE = 'report.jinja'
_ROOT_DIR = os.path.normpath(os.path.dirname(os.path.realpath(__file__)))
-def _get_env():
+def _get_env(result):
"""
Get system configuration.
@@ -53,8 +53,18 @@ def _get_env():
'cpu_cores': systeminfo.get_cpu_cores(),
'memory' : systeminfo.get_memory(),
'platform': systeminfo.get_platform(),
+ 'vsperf': systeminfo.get_version('vswitchperf'),
+ 'traffic_gen': systeminfo.get_version(S.getValue('TRAFFICGEN')),
+ 'vswitch': systeminfo.get_version(S.getValue('VSWITCH')),
+ 'dpdk': systeminfo.get_version('dpdk'),
}
+ 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'))),
+ })
+
return env
@@ -78,28 +88,27 @@ def generate(input_file, tc_results, tc_stats):
try:
for result in tc_results:
test_config = {}
- for tc_conf in settings.getValue('PERFORMANCE_TESTS'):
+ for tc_conf in S.getValue('PERFORMANCE_TESTS'):
if tc_conf['Name'] == result[ResultsConstants.ID]:
test_config = tc_conf
break
- # remove id and deployment from results but store their values
- tc_id = result[ResultsConstants.ID]
- tc_deployment = result[ResultsConstants.DEPLOYMENT]
- del result[ResultsConstants.ID]
- del result[ResultsConstants.DEPLOYMENT]
-
# pass test results, env details and configuration to template
tests.append({
- 'ID': tc_id.upper(),
- 'id': tc_id,
- 'deployment': tc_deployment,
+ 'ID': result[ResultsConstants.ID].upper(),
+ 'id': result[ResultsConstants.ID],
+ 'deployment': result[ResultsConstants.DEPLOYMENT],
'conf': test_config,
'result': result,
- 'env': _get_env(),
+ 'env': _get_env(result),
'stats': tc_stats
})
+ # remove id and deployment from results before rendering
+ # but after _get_env() is called; tests dict has its shallow copy
+ del result[ResultsConstants.ID]
+ del result[ResultsConstants.DEPLOYMENT]
+
template_vars = {
'tests': tests,
}
@@ -117,6 +126,6 @@ def generate(input_file, tc_results, tc_stats):
if __name__ == '__main__':
- settings.load_from_dir('conf')
+ S.load_from_dir('conf')
OUT = generate(sys.argv[1], '', '')
print('Test report written to "%s"...' % OUT)
diff --git a/tools/systeminfo.py b/tools/systeminfo.py
index cb9ca6ee..901b334e 100644
--- a/tools/systeminfo.py
+++ b/tools/systeminfo.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Intel Corporation.
+# Copyright 2015-2016 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -20,7 +20,25 @@ import platform
import subprocess
import locale
-from conf import settings
+from conf import settings as S
+
+def match_line(file_name, pattern):
+ """ loops through given file and returns first line matching given pattern
+
+ :returns: string with the matching line without end of line or None
+ """
+ try:
+ with open(file_name, encoding="latin-1") as file_:
+ for line in file_:
+ if not line.strip():
+ continue
+ if not line.strip().startswith(pattern):
+ continue
+
+ return line.strip().rstrip('\n')
+ return None
+ except OSError:
+ return None
def get_os():
"""Get distro name.
@@ -41,14 +59,8 @@ def get_cpu():
:returns: Return CPU information as a string
"""
- with open('/proc/cpuinfo') as file_:
- for line in file_:
- if not line.strip():
- continue
- if not line.rstrip('\n').startswith('model name'):
- continue
-
- return line.rstrip('\n').split(':')[1]
+ cpu = match_line('/proc/cpuinfo', 'model name')
+ return cpu.split(':')[1] if cpu else cpu
def get_nic():
"""Get NIC(s) information.
@@ -59,7 +71,7 @@ def get_nic():
output = subprocess.check_output('lspci', shell=True)
output = output.decode(locale.getdefaultlocale()[1])
for line in output.split('\n'):
- for nic_pciid in settings.getValue('WHITELIST_NICS'):
+ for nic_pciid in S.getValue('WHITELIST_NICS'):
if line.startswith(nic_pciid):
nics.append(''.join(line.split(':')[2:]).strip())
return ', '.join(nics).strip()
@@ -108,14 +120,8 @@ def get_memory():
:returns: amount of system memory as string together with unit
"""
- with open('/proc/meminfo') as file_:
- for line in file_:
- if not line.strip():
- continue
- if not line.rstrip('\n').startswith('MemTotal'):
- continue
-
- return line.rstrip('\n').split(':')[1].strip()
+ memory = match_line('/proc/meminfo', 'MemTotal')
+ return memory.split(':')[1].strip() if memory else memory
def get_memory_bytes():
"""Get memory information in bytes
@@ -145,7 +151,7 @@ def get_pids(proc_names_list):
"""
try:
- pids = subprocess.check_output(['sudo', 'LC_ALL=' + settings.getValue('DEFAULT_CMD_LOCALE'), 'pidof']
+ pids = subprocess.check_output(['sudo', 'LC_ALL=' + S.getValue('DEFAULT_CMD_LOCALE'), 'pidof']
+ proc_names_list)
except subprocess.CalledProcessError:
# such process isn't running
@@ -160,3 +166,92 @@ def get_pid(proc_name_str):
with given name is not running
"""
return get_pids([proc_name_str])
+
+# This function uses long switch per purpose, so let us suppress pylint warning too-many-branches
+# pylint: disable=R0912
+def get_version(app_name):
+ """ Get version of given 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
+
+ """
+ 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'),
+ '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'))
+ elif app_name.lower() in ['dpdk', 'testpmd']:
+ tmp_ver = ['', '', '']
+ found = False
+ with open(app_version_file['dpdk']) as file_:
+ for line in file_:
+ if not line.strip():
+ continue
+ if line.startswith('#define RTE_VER_MAJOR'):
+ found = True
+ tmp_ver[0] = line.rstrip('\n').split(' ')[2]
+ if line.startswith('#define RTE_VER_MINOR'):
+ found = True
+ tmp_ver[1] = line.rstrip('\n').split(' ')[2]
+ if line.startswith('#define RTE_VER_PATCH_LEVEL'):
+ found = True
+ tmp_ver[2] = line.rstrip('\n').split(' ')[2]
+
+ if found:
+ app_version = '.'.join(tmp_ver)
+ app_git_tag = get_git_tag(S.getValue('RTE_SDK'))
+ elif app_name.lower().startswith('qemu'):
+ app_version = match_line(app_version_file['qemu'], '')
+ app_git_tag = get_git_tag(S.getValue('QEMU_DIR'))
+ elif app_name.lower() == 'ixnet':
+ app_version = match_line(app_version_file['ixnet'], 'package provide IxTclNetwork')
+ if app_version:
+ app_version = app_version.split(' ')[3]
+ elif app_name.lower() == 'dummy':
+ # get git tag of file with Dummy implementation
+ app_git_tag = get_git_tag(os.path.join(S.getValue('ROOT_DIR'), 'tools/pkt_gen/dummy/dummy.py'))
+ 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')
+ if app_version:
+ app_version = app_version.split('"')[1]
+ app_git_tag = get_git_tag(app_version_file[app_name])
+ 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}
diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py
index bf7b1a90..83eea679 100644
--- a/vnfs/qemu/qemu.py
+++ b/vnfs/qemu/qemu.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Intel Corporation.
+# Copyright 2015-2016 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -200,7 +200,8 @@ class IVnfQemu(IVnf):
"""
# set guest loopback application based on VNF configuration
# cli option take precedence to config file values
- guest_loopback = get_test_param('guest_loopback', S.getValue('GUEST_LOOPBACK')[self._number])
+ guest_loopback = S.getValue('GUEST_LOOPBACK')[self._number]
+
if guest_loopback == 'testpmd':
self._login()
self._configure_testpmd()
diff --git a/vsperf b/vsperf
index 62c905c3..53699104 100755
--- a/vsperf
+++ b/vsperf
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
-# Copyright 2015 Intel Corporation.
+# Copyright 2015-2016 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import locale
sys.dont_write_bytecode = True
from conf import settings
+from conf import get_test_param
from core.loader import Loader
from testcases import TestCase
from tools import tasks
@@ -358,6 +359,14 @@ def main():
logging.error('The selected Duration is not a number')
sys.exit(1)
+ # update global settings
+ guest_loopback = get_test_param('guest_loopback', None)
+ if guest_loopback:
+ tmp_gl = []
+ for i in range(len(settings.getValue('GUEST_LOOPBACK'))):
+ tmp_gl.append(guest_loopback)
+ settings.setValue('GUEST_LOOPBACK', tmp_gl)
+
# generate results directory name
date = datetime.datetime.fromtimestamp(time.time())
results_dir = "results_" + date.strftime('%Y-%m-%d_%H-%M-%S')