diff options
-rwxr-xr-x | conf/01_testcases.conf | 6 | ||||
-rw-r--r-- | core/pktfwd_controller.py | 10 | ||||
-rwxr-xr-x | docs/userguides/quickstart.rst | 57 | ||||
-rw-r--r-- | testcases/testcase.py | 56 | ||||
-rw-r--r-- | vnfs/qemu/qemu.py | 2 | ||||
-rwxr-xr-x | vsperf | 229 |
6 files changed, 226 insertions, 134 deletions
diff --git a/conf/01_testcases.conf b/conf/01_testcases.conf index b855405d..77072eeb 100755 --- a/conf/01_testcases.conf +++ b/conf/01_testcases.conf @@ -20,14 +20,18 @@ # "Name": "phy2phy_burst", # A human-readable string identifying the # # test. # "Traffic Type": "rfc2544", # One of the supported traffic types. +# # It can be overridden by cli option traffic_type. +# # Default value is "rfc2544". # "Deployment": "p2p", # One of the supported deployment scenarios. # "Description": "Lorem ipsum..." # Optional. A human-readable string # # describing the test. # "Frame Modification": "vlan" # One of the supported frame modifications: # # vlan, mpls, mac, dscp, ttl, ip_addr, # # ip_port. -# "biDirectional": [true|false], # Specifies if genearted traffic will be +# "biDirectional": [true|false], # Specifies if generated traffic will be # # full-duplex (true) or half-duplex (false) +# # It can be overridden by cli option bidirectional. +# # Default value is "false". # "MultiStream": 0-65535 # Optional. Defines number of flows simulated # # by traffic generator. Value 0 disables # # MultiStream feature diff --git a/core/pktfwd_controller.py b/core/pktfwd_controller.py index 4f300ce8..40565504 100644 --- a/core/pktfwd_controller.py +++ b/core/pktfwd_controller.py @@ -17,9 +17,6 @@ import logging -from conf import settings - - class PktFwdController(object): """Packet forwarder controller for P2P deployment scenario. @@ -66,3 +63,10 @@ class PktFwdController(object): :return: The controlled IPktFwd """ return self._pktfwd + + def dump_vswitch_flows(self): + """ Dumps flows from vswitch + """ + raise NotImplementedError( + "The PktFwdController does not implement the " + "\"dump_vswitch_flows\" function.") diff --git a/docs/userguides/quickstart.rst b/docs/userguides/quickstart.rst index caf3be04..fa951e0e 100755 --- a/docs/userguides/quickstart.rst +++ b/docs/userguides/quickstart.rst @@ -294,9 +294,9 @@ To run tests using Vanilla OVS: or use --test-param - ./vsperf --conf-file=<path_to_custom_conf>/10_custom.conf - --test-param "vanilla_tgen_tx_ip=n.n.n.n; - vanilla_tgen_tx_mac=nn:nn:nn:nn:nn:nn" + $ ./vsperf --conf-file=<path_to_custom_conf>/10_custom.conf + --test-param "vanilla_tgen_tx_ip=n.n.n.n; + vanilla_tgen_tx_mac=nn:nn:nn:nn:nn:nn" 2. Recompile src for Vanilla OVS testing @@ -356,7 +356,7 @@ following configuration parameter should be configured: or use --vswitch and --fwdapp - ./vsperf --conf-file user_settings.py + $ ./vsperf --conf-file user_settings.py --vswitch none --fwdapp TestPMD @@ -386,8 +386,55 @@ for selected Packet Forwarder: .. code-block:: console - ./vsperf --conf-file <path_to_settings_py> + $ ./vsperf --conf-file <path_to_settings_py> +VSPERF modes of operation +------------------------- +VSPERF can be run in different modes. By default it will configure vSwitch, +traffic generator and VNF. However it can be used just for configuration +and execution of traffic generator. Another option is execution of all +components except traffic generator itself. + +Mode of operation is driven by configuration parameter -m or --mode + +.. code-block:: console + + -m MODE, --mode MODE vsperf mode of operation; + Values: + "normal" - execute vSwitch, VNF and traffic generator + "trafficgen" - execute only traffic generator + "trafficgen-off" - execute vSwitch and VNF + +In case, that VSPERF is executed in "trafficgen" mode, then configuration +of traffic generator should be configured through --test-param option. +Supported CLI options useful for traffic generator configuration are: + +.. code-block:: console + + 'traffic_type' - One of the supported traffic types. E.g. rfc2544, + back2back or continuous + Default value is "rfc2544". + 'bidirectional' - Specifies if generated traffic will be full-duplex (true) + or half-duplex (false) + Default value is "false". + 'iload' - Defines desired percentage of frame rate used during + continuous stream tests. + Default value is 100. + 'multistream' - Defines number of flows simulated by traffic generator. + Value 0 disables MultiStream feature + Default value is 0. + 'stream_type' - Stream Type is an extension of the "MultiStream" feature. + If MultiStream is disabled, then Stream Type will be + ignored. Stream Type defines ISO OSI network layer used + for simulation of multiple streams. + Default value is "L4". + +Example of execution of VSPERF in "trafficgen" mode: + +.. code-block:: console + + $ ./vsperf -m trafficgen --trafficgen IxNet --conf-file vsperf.conf + --test-params "traffic_type=continuous;bidirectional=True;iload=60" Code change verification by pylint ---------------------------------- diff --git a/testcases/testcase.py b/testcases/testcase.py index 7d5162e6..9c755ea7 100644 --- a/testcases/testcase.py +++ b/testcases/testcase.py @@ -19,6 +19,7 @@ import os import logging import subprocess import copy +import time from collections import OrderedDict from core.results.results_constants import ResultsConstants @@ -45,14 +46,23 @@ class TestCase(object): :param results_dir: Where the csv formatted results are written. """ self._hugepages_mounted = False + + # set test parameters; CLI options take precedence to testcase settings self._logger = logging.getLogger(__name__) self.name = cfg['Name'] self.desc = cfg.get('Description', 'No description given.') + + bidirectional = cfg.get('biDirectional', False) + bidirectional = get_test_param('bidirectional', bidirectional) + + traffic_type = cfg.get('Traffic Type', 'rfc2544') + traffic_type = get_test_param('traffic_type', traffic_type) + + framerate = cfg.get('iLoad', 100) + framerate = get_test_param('iload', framerate) + self.deployment = cfg['Deployment'] self._frame_mod = cfg.get('Frame Modification', None) - framerate = get_test_param('iload', None) - if framerate == None: - framerate = cfg.get('iLoad', 100) # identify guest loopback method, so it can be added into reports self.guest_loopback = [] @@ -75,7 +85,6 @@ class TestCase(object): pre_installed_flows = cfg.get('Pre-installed Flows', 'No') pre_installed_flows = get_test_param('pre-installed_flows', pre_installed_flows) - # check if test requires background load and which generator it uses self._load_cfg = cfg.get('Load', None) if self._load_cfg and 'tool' in self._load_cfg: @@ -90,9 +99,9 @@ class TestCase(object): # set traffic details, so they can be passed to vswitch and traffic ctls self._traffic = copy.deepcopy(TRAFFIC_DEFAULTS) - self._traffic.update({'traffic_type': cfg['Traffic Type'], + self._traffic.update({'traffic_type': traffic_type, 'flow_type': cfg.get('Flow Type', 'port'), - 'bidir': cfg['biDirectional'], + 'bidir': bidirectional, 'multistream': int(multistream), 'stream_type': stream_type, 'pre_installed_flows' : pre_installed_flows, @@ -152,29 +161,36 @@ class TestCase(object): if not self._vswitch_none: self._add_flows(vswitch_ctl) - with traffic_ctl: - traffic_ctl.send_traffic(self._traffic) + # run traffic generator if requested, otherwise wait for manual termination + if S.getValue('mode') == 'trafficgen-off': + time.sleep(2) + self._logger.debug("All is set. Please run traffic generator manually.") + input(os.linesep + "Press Enter to terminate vswitchperf..." + os.linesep + os.linesep) + else: + with traffic_ctl: + traffic_ctl.send_traffic(self._traffic) - # dump vswitch flows before they are affected by VNF termination - if not self._vswitch_none: - vswitch_ctl.dump_vswitch_flows() + # dump vswitch flows before they are affected by VNF termination + if not self._vswitch_none: + vswitch_ctl.dump_vswitch_flows() # umount hugepages if mounted self._umount_hugepages() - self._logger.debug("Traffic Results:") - traffic_ctl.print_results() - self._logger.debug("Collector Results:") collector.print_results() - output_file = os.path.join(self._results_dir, "result_" + self.name + - "_" + self.deployment + ".csv") + if S.getValue('mode') != 'trafficgen-off': + self._logger.debug("Traffic Results:") + traffic_ctl.print_results() + + output_file = os.path.join(self._results_dir, "result_" + self.name + + "_" + self.deployment + ".csv") - tc_results = self._append_results(traffic_ctl.get_results()) - TestCase._write_result_to_file(tc_results, output_file) + tc_results = self._append_results(traffic_ctl.get_results()) + TestCase._write_result_to_file(tc_results, output_file) - report.generate(output_file, tc_results, collector.get_results()) + report.generate(output_file, tc_results, collector.get_results()) def _append_results(self, results): """ @@ -287,7 +303,7 @@ class TestCase(object): return list(result.keys()) - def _add_flows(vswitch_ctl): + def _add_flows(self, vswitch_ctl): """Add flows to the vswitch :param vswitch_ctl vswitch controller diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py index 536f1fd0..38394974 100644 --- a/vnfs/qemu/qemu.py +++ b/vnfs/qemu/qemu.py @@ -83,7 +83,7 @@ class IVnfQemu(IVnf): '-nographic', '-vnc', str(vnc), '-name', name, '-snapshot', '-net none', '-no-reboot', '-drive', - 'if=scsi,type=raw,file=fat:rw:%s,snapshot=off' % + 'if=scsi,format=raw,file=fat:rw:%s,snapshot=off' % S.getValue('GUEST_SHARE_DIR')[self._number], ] self._configure_logging() @@ -27,6 +27,7 @@ import shutil import unittest import xmlrunner import locale +import copy sys.dont_write_bytecode = True @@ -37,6 +38,9 @@ from testcases import TestCase from tools import tasks from tools.pkt_gen import trafficgen from tools.opnfvdashboard import opnfvdashboard +from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS +from conf import get_test_param +import core.component_factory as component_factory VERBOSITY_LEVELS = { 'debug': logging.DEBUG, @@ -133,6 +137,11 @@ def parse_arguments(): To run all tests omit both positional args and --tests arg.') group = parser.add_argument_group('test selection options') + group.add_argument('-m', '--mode', help='vsperf mode of operation;\ + Values: "normal" - execute vSwitch, VNF and traffic generator;\ + "trafficgen" - execute only traffic generator; "trafficgen-off" \ + - execute vSwitch and VNF', default='normal') + group.add_argument('-f', '--test-spec', help='test specification file') group.add_argument('-d', '--test-dir', help='directory containing tests') group.add_argument('-t', '--tests', help='Comma-separated list of terms \ @@ -332,7 +341,6 @@ def main(): check_and_set_locale() # configure trafficgens - if args['trafficgen']: trafficgens = Loader().get_trafficgens() if args['trafficgen'] not in trafficgens: @@ -387,119 +395,132 @@ def main(): tmp_gl.append(guest_loopback) settings.setValue('GUEST_LOOPBACK', tmp_gl) + settings.setValue('mode', args['mode']) + # generate results directory name date = datetime.datetime.fromtimestamp(time.time()) results_dir = "results_" + date.strftime('%Y-%m-%d_%H-%M-%S') results_path = os.path.join(settings.getValue('LOG_DIR'), results_dir) - # configure tests - testcases = settings.getValue('PERFORMANCE_TESTS') - all_tests = [] - for cfg in testcases: - try: - all_tests.append(TestCase(cfg, results_path)) - except (Exception) as _: - logger.exception("Failed to create test: %s", - cfg.get('Name', '<Name not set>')) - raise - - # if required, handle list-* operations - - if args['list']: - print("Available Tests:") - print("======") - for test in all_tests: - print('* %-18s%s' % ('%s:' % test.name, test.desc)) - exit() - - if args['list_trafficgens']: - print(Loader().get_trafficgens_printable()) - exit() - - if args['list_collectors']: - print(Loader().get_collectors_printable()) - exit() - - if args['list_vswitches']: - print(Loader().get_vswitches_printable()) - exit() - - if args['list_fwdapps']: - print(Loader().get_pktfwds_printable()) - exit() - - if args['list_vnfs']: - print(Loader().get_vnfs_printable()) - exit() - - if args['list_settings']: - print(str(settings)) - exit() - - # select requested tests - if args['exact_test_name'] and args['tests']: - logger.error("Cannot specify tests with both positional args and --test.") - sys.exit(1) - - if args['exact_test_name']: - exact_names = args['exact_test_name'] - # positional args => exact matches only - selected_tests = [test for test in all_tests if test.name in exact_names] - elif args['tests']: - # --tests => apply filter to select requested tests - selected_tests = apply_filter(all_tests, args['tests']) - else: - # Default - run all tests - selected_tests = all_tests - - if not selected_tests: - logger.error("No tests matched --test option or positional args. Done.") - sys.exit(1) - # create results directory if not os.path.exists(results_path): logger.info("Creating result directory: " + results_path) os.makedirs(results_path) - # run tests - suite = unittest.TestSuite() - for test in selected_tests: - try: - if vswitch_none: - if test.deployment.lower() != 'p2p': - logging.error('\'none\' vswitch option supported only' - ' for p2p deployment.') - sys.exit(1) - test.run() - suite.addTest(MockTestCase('', True, test.name)) - #pylint: disable=broad-except - except (Exception) as ex: - logger.exception("Failed to run test: %s", test.name) - suite.addTest(MockTestCase(str(ex), False, test.name)) - logger.info("Continuing with next test...") - - if settings.getValue('XUNIT'): - xmlrunner.XMLTestRunner( - output=settings.getValue('XUNIT_DIR'), outsuffix="", - verbosity=0).run(suite) - - if args['opnfvpod']: - pod_name = args['opnfvpod'] - installer_name = settings.getValue('OPNFV_INSTALLER') - opnfv_url = settings.getValue('OPNFV_URL') - pkg_list = settings.getValue('PACKAGE_LIST') - - int_data = {'cuse': False, - 'vanilla': False, - 'pod': pod_name, - 'installer': installer_name, - 'pkg_list': pkg_list, - 'db_url': opnfv_url} - if settings.getValue('VSWITCH').endswith('Vanilla'): - int_data['vanilla'] = True - if settings.getValue('VNF').endswith('Cuse'): - int_data['cuse'] = True - opnfvdashboard.results2opnfv_dashboard(results_path, int_data) + if settings.getValue('mode') == 'trafficgen': + # execute only traffic generator + logging.debug("Executing traffic generator:") + loader = Loader() + # set traffic details, so they can be passed to traffic ctl + traffic = copy.deepcopy(TRAFFIC_DEFAULTS) + traffic.update({'traffic_type': get_test_param('traffic_type', 'rfc2544'), + 'bidir': get_test_param('bidirectional', False), + 'multistream': int(get_test_param('multistream', 0)), + 'stream_type': get_test_param('stream_type', 'L4'), + 'frame_rate': int(get_test_param('iload', 100))}) + + traffic_ctl = component_factory.create_traffic( + traffic['traffic_type'], + loader.get_trafficgen_class()) + with traffic_ctl: + traffic_ctl.send_traffic(traffic) + logging.debug("Traffic Results:") + traffic_ctl.print_results() + else: + # configure tests + testcases = settings.getValue('PERFORMANCE_TESTS') + all_tests = [] + for cfg in testcases: + try: + all_tests.append(TestCase(cfg, results_path)) + except (Exception) as _: + logger.exception("Failed to create test: %s", + cfg.get('Name', '<Name not set>')) + raise + + # if required, handle list-* operations + + if args['list']: + print("Available Tests:") + print("======") + for test in all_tests: + print('* %-18s%s' % ('%s:' % test.name, test.desc)) + exit() + + if args['list_trafficgens']: + print(Loader().get_trafficgens_printable()) + exit() + + if args['list_collectors']: + print(Loader().get_collectors_printable()) + exit() + + if args['list_vswitches']: + print(Loader().get_vswitches_printable()) + exit() + + if args['list_vnfs']: + print(Loader().get_vnfs_printable()) + exit() + + if args['list_settings']: + print(str(settings)) + exit() + + # select requested tests + if args['exact_test_name'] and args['tests']: + logger.error("Cannot specify tests with both positional args and --test.") + sys.exit(1) + + if args['exact_test_name']: + exact_names = args['exact_test_name'] + # positional args => exact matches only + selected_tests = [test for test in all_tests if test.name in exact_names] + elif args['tests']: + # --tests => apply filter to select requested tests + selected_tests = apply_filter(all_tests, args['tests']) + else: + # Default - run all tests + selected_tests = all_tests + + if not selected_tests: + logger.error("No tests matched --test option or positional args. Done.") + sys.exit(1) + + # run tests + suite = unittest.TestSuite() + for test in selected_tests: + try: + test.run() + suite.addTest(MockTestCase('', True, test.name)) + #pylint: disable=broad-except + except (Exception) as ex: + logger.exception("Failed to run test: %s", test.name) + suite.addTest(MockTestCase(str(ex), False, test.name)) + logger.info("Continuing with next test...") + + if settings.getValue('XUNIT'): + xmlrunner.XMLTestRunner( + output=settings.getValue('XUNIT_DIR'), outsuffix="", + verbosity=0).run(suite) + + if args['opnfvpod']: + pod_name = args['opnfvpod'] + installer_name = settings.getValue('OPNFV_INSTALLER') + opnfv_url = settings.getValue('OPNFV_URL') + pkg_list = settings.getValue('PACKAGE_LIST') + + int_data = {'cuse': False, + 'vanilla': False, + 'pod': pod_name, + 'installer': installer_name, + 'pkg_list': pkg_list, + 'db_url': opnfv_url} + if settings.getValue('VSWITCH').endswith('Vanilla'): + int_data['vanilla'] = True + if settings.getValue('VNF').endswith('Cuse'): + int_data['cuse'] = True + opnfvdashboard.results2opnfv_dashboard(results_path, int_data) #remove directory if no result files were created. if os.path.exists(results_path): |