aboutsummaryrefslogtreecommitdiffstats
path: root/vsperf
blob: 0747a20519b98ba73d443f0276193268fc2ae8f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
# VSPERF - vSwitch Performance Tests

Documentation for this project including the [quickstart guide] is contained
under the ./docs directory.

---

[quickstart guide]: docs/quickstart.md
'#n325'>325 326 327 328 329 330 331 332 333 334 335 336 337
#!/usr/bin/env python3

# Copyright 2015 Intel Corporation.
#
# 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.

"""VSPERF main script.
"""

import logging
import os
import sys
import argparse
import time
import datetime
import shutil

sys.dont_write_bytecode = True

from conf import settings
from core.loader import Loader
from testcases import TestCase
from tools import tasks
from tools.collectors import collector
from tools.pkt_gen import trafficgen

VERBOSITY_LEVELS = {
    'debug': logging.DEBUG,
    'info': logging.INFO,
    'warning': logging.WARNING,
    'error': logging.ERROR,
    'critical': logging.CRITICAL
}


def parse_arguments():
    """
    Parse command line arguments.
    """
    class _SplitTestParamsAction(argparse.Action):
        """
        Parse and split the '--test-params' argument.

        This expects either 'x=y' or 'x' (implicit true) values.
        """
        def __call__(self, parser, namespace, values, option_string=None):
            results = {}

            for value in values.split(';'):
                result = [key.strip() for key in value.split('=')]
                if len(result) == 1:
                    results[result[0]] = True
                elif len(result) == 2:
                    results[result[0]] = result[1]
                else:
                    raise argparse.ArgumentTypeError(
                        'expected \'%s\' to be of format \'key=val\' or'
                        ' \'key\'' % result)

            setattr(namespace, self.dest, results)

    class _ValidateFileAction(argparse.Action):
        """Validate a file can be read from before using it.
        """
        def __call__(self, parser, namespace, values, option_string=None):
            if not os.path.isfile(values):
                raise argparse.ArgumentTypeError(
                    'the path \'%s\' is not a valid path' % values)
            elif not os.access(values, os.R_OK):
                raise argparse.ArgumentTypeError(
                    'the path \'%s\' is not accessible' % values)

            setattr(namespace, self.dest, values)

    class _ValidateDirAction(argparse.Action):
        """Validate a directory can be written to before using it.
        """
        def __call__(self, parser, namespace, values, option_string=None):
            if not os.path.isdir(values):
                raise argparse.ArgumentTypeError(
                    'the path \'%s\' is not a valid path' % values)
            elif not os.access(values, os.W_OK):
                raise argparse.ArgumentTypeError(
                    'the path \'%s\' is not accessible' % values)

            setattr(namespace, self.dest, values)

    def list_logging_levels():
        """Give a summary of all available logging levels.

	:return: List of verbosity level names in decreasing order of
            verbosity
        """
        return sorted(VERBOSITY_LEVELS.keys(),
                      key=lambda x: VERBOSITY_LEVELS[x])

    parser = argparse.ArgumentParser(prog=__file__, formatter_class=
                                     argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('--version', action='version', version='%(prog)s 0.2')
    parser.add_argument('--list', '--list-tests', action='store_true',
                        help='list all tests and exit')
    parser.add_argument('--list-trafficgens', action='store_true',
                        help='list all traffic generators and exit')
    parser.add_argument('--list-collectors', action='store_true',
                        help='list all system metrics loggers and exit')
    parser.add_argument('--list-vswitches', action='store_true',
                        help='list all system vswitches and exit')
    parser.add_argument('--list-settings', action='store_true',
                        help='list effective settings configuration and exit')
    parser.add_argument('test', nargs='*', help='test specification(s)')

    group = parser.add_argument_group('test selection options')
    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 \
            indicating tests to run. e.g. "RFC2544,!p2p" - run all tests whose\
            name contains RFC2544 less those containing "p2p"')
    group.add_argument('--verbosity', choices=list_logging_levels(),
                       help='debug level')
    group.add_argument('--trafficgen', help='traffic generator to use')
    group.add_argument('--sysmetrics', help='system metrics logger to use')
    group = parser.add_argument_group('test behavior options')
    group.add_argument('--load-env', action='store_true',
                       help='enable loading of settings from the environment')
    group.add_argument('--conf-file', action=_ValidateFileAction,
                       help='settings file')
    group.add_argument('--test-params', action=_SplitTestParamsAction,
                       help='csv list of test parameters: key=val;...')

    args = vars(parser.parse_args())

    return args


def configure_logging(level):
    """Configure logging.
    """
    log_file_default = os.path.join(
        settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_DEFAULT'))
    log_file_host_cmds = os.path.join(
        settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_HOST_CMDS'))
    log_file_traffic_gen = os.path.join(
        settings.getValue('LOG_DIR'),
        settings.getValue('LOG_FILE_TRAFFIC_GEN'))
    log_file_sys_metrics = os.path.join(
        settings.getValue('LOG_DIR'),
        settings.getValue('LOG_FILE_SYS_METRICS'))

    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    stream_logger = logging.StreamHandler(sys.stdout)
    stream_logger.setLevel(VERBOSITY_LEVELS[level])
    stream_logger.setFormatter(logging.Formatter(
        '[%(levelname)s]  %(asctime)s : (%(name)s) - %(message)s'))
    logger.addHandler(stream_logger)

    file_logger = logging.FileHandler(filename=log_file_default)
    file_logger.setLevel(logging.DEBUG)
    logger.addHandler(file_logger)

    class CommandFilter(logging.Filter):
        """Filter out strings beginning with 'cmd :'"""
        def filter(self, record):
            return record.getMessage().startswith(tasks.CMD_PREFIX)

    class TrafficGenCommandFilter(logging.Filter):
        """Filter out strings beginning with 'gencmd :'"""
        def filter(self, record):
            return record.getMessage().startswith(trafficgen.CMD_PREFIX)

    class SystemMetricsCommandFilter(logging.Filter):
        """Filter out strings beginning with 'gencmd :'"""
        def filter(self, record):
            return record.getMessage().startswith(collector.CMD_PREFIX)

    cmd_logger = logging.FileHandler(filename=log_file_host_cmds)
    cmd_logger.setLevel(logging.DEBUG)
    cmd_logger.addFilter(CommandFilter())
    logger.addHandler(cmd_logger)

    gen_logger = logging.FileHandler(filename=log_file_traffic_gen)
    gen_logger.setLevel(logging.DEBUG)
    gen_logger.addFilter(TrafficGenCommandFilter())
    logger.addHandler(gen_logger)

    metrics_logger = logging.FileHandler(filename=log_file_sys_metrics)
    metrics_logger.setLevel(logging.DEBUG)
    metrics_logger.addFilter(SystemMetricsCommandFilter())
    logger.addHandler(metrics_logger)


def apply_filter(tests, tc_filter):
    """Allow a subset of tests to be conveniently selected

    :param tests: The list of Tests from which to select.
    :param tc_filter: A case-insensitive string of comma-separated terms
        indicating the Tests to select.
        e.g. 'RFC' - select all tests whose name contains 'RFC'
        e.g. 'RFC,burst' - select all tests whose name contains 'RFC' or
            'burst'
        e.g. 'RFC,burst,!p2p' - select all tests whose name contains 'RFC'
            or 'burst' and from these remove any containing 'p2p'.
        e.g. '' - empty string selects all tests.
    :return: A list of the selected Tests.
    """
    result = []
    if tc_filter is None:
        tc_filter = ""

    for term in [x.strip() for x in tc_filter.lower().split(",")]:
        if not term or term[0] != '!':
            # Add matching tests from 'tests' into results
            result.extend([test for test in tests \
                if test.name.lower().find(term) >= 0])
        else:
            # Term begins with '!' so we remove matching tests
            result = [test for test in result \
                if test.name.lower().find(term[1:]) < 0]

    return result


def main():
    """Main function.
    """
    args = parse_arguments()

    # configure settings

    settings.load_from_dir('conf')

    # load command line parameters first in case there are settings files
    # to be used
    settings.load_from_dict(args)

    if args['conf_file']:
        settings.load_from_file(args['conf_file'])

    if args['load_env']:
        settings.load_from_env()

    # reload command line parameters since these should take higher priority
    # than both a settings file and environment variables
    settings.load_from_dict(args)

    configure_logging(settings.getValue('VERBOSITY'))
    logger = logging.getLogger()

    # configure trafficgens

    if args['trafficgen']:
        trafficgens = Loader().get_trafficgens()
        if args['trafficgen'] not in trafficgens:
            logging.error('There are no trafficgens matching \'%s\' found in'
                          ' \'%s\'. Exiting...', args['trafficgen'],
                          settings.getValue('TRAFFICGEN_DIR'))
            sys.exit(1)


    # 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

    # TODO(BOM) Apply filter to select requested tests
    all_tests = apply_filter(all_tests, args['tests'])

    # 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_settings']:
        print(str(settings))
        exit()

    # create results directory
    if not os.path.exists(results_dir):
        logger.info("Creating result directory: "  + results_path)
        os.makedirs(results_path)

    # run tests
    for test in all_tests:
        try:
            test.run()
        #pylint: disable=broad-except
        except (Exception) as _:
            logger.exception("Failed to run test: %s", test.name)
            logger.info("Continuing with next test...")

    #remove directory if no result files were created.
    if os.path.exists(results_path):
        if os.listdir(results_path) == []:
            shutil.rmtree(results_path)

if __name__ == "__main__":
    main()