diff options
author | Martin Klozik <martinx.klozik@intel.com> | 2016-01-11 17:56:45 +0100 |
---|---|---|
committer | Maryam Tahhan <maryam.tahhan@intel.com> | 2016-01-22 16:48:12 +0000 |
commit | b7865213f0942217ce8396d60f27ccac7f65cd07 (patch) | |
tree | 31008b4b8659a2d5296afd3e0ea3f8288b0186db | |
parent | 4e48999bc6956dee6f9134b4ef26f34f578cdd80 (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>
(cherry picked from commit 6da6b66a1affe7a79180a49534602a02e7994c7f)
-rw-r--r-- | tools/report/report.jinja | 11 | ||||
-rw-r--r-- | tools/report/report.py | 39 | ||||
-rw-r--r-- | tools/systeminfo.py | 135 | ||||
-rw-r--r-- | vnfs/qemu/qemu.py | 5 | ||||
-rwxr-xr-x | vsperf | 11 |
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() @@ -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') |