diff options
author | Dan Klein <danklein10@gmail.com> | 2015-11-26 13:06:36 +0200 |
---|---|---|
committer | Dan Klein <danklein10@gmail.com> | 2015-11-26 13:06:36 +0200 |
commit | 91f6c24f45cbb0cbf8568a9938059a1a934e6ae6 (patch) | |
tree | 0977d1129173d2b2be8e36c91aa5b7ec97b035a1 /scripts/automation/trex_control_plane/client_utils | |
parent | e7cb8b0f6c2fbe08d2086a7408040ac7d12aee5a (diff) |
Initial implementation of stats prompting
Diffstat (limited to 'scripts/automation/trex_control_plane/client_utils')
4 files changed, 251 insertions, 3 deletions
diff --git a/scripts/automation/trex_control_plane/client_utils/external_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py index e2bb37a5..3c6eb449 100755 --- a/scripts/automation/trex_control_plane/client_utils/external_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -9,7 +9,8 @@ PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pard CLIENT_UTILS_MODULES = ['zmq', 'dpkt-1.8.6', - 'PyYAML-3.01/lib' + 'PyYAML-3.01/lib', + 'texttable-0.8.4' ] def import_client_utils_modules(): diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index b826f02f..dd208da4 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -174,7 +174,7 @@ class JsonRpcClient(object): def process_single_response (self, response_json): if (response_json.get("jsonrpc") != "2.0"): - return False, "Malfromed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response_json)) # error reported by server if ("error" in response_json): @@ -185,7 +185,7 @@ class JsonRpcClient(object): # if no error there should be a result if ("result" not in response_json): - return False, "Malformed Response ({0})".format(str(response)) + return False, "Malformed Response ({0})".format(str(response_json)) return True, response_json["result"] diff --git a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py new file mode 100755 index 00000000..c110983b --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py @@ -0,0 +1,213 @@ +import argparse +from collections import namedtuple +import sys +import re +import os + +ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options']) +ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options']) + + +# list of available parsing options +MULTIPLIER = 1 +PORT_LIST = 2 +ALL_PORTS = 3 +PORT_LIST_WITH_ALL = 4 +FILE_PATH = 5 +FILE_FROM_DB = 6 +SERVER_IP = 7 +STREAM_FROM_PATH_OR_FILE = 8 +DURATION = 9 +FORCE = 10 +GLOBAL_STATS = 11 +PORT_STATS = 12 +PORT_STATUS = 13 +STATS_MASK = 14 + +# list of ArgumentGroup types +MUTEX = 1 + + +def match_time_unit(val): + '''match some val against time shortcut inputs ''' + match = re.match("^(\d+)([m|h]?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'm': + return digit*60 + else: + return digit*60*60 + else: + raise argparse.ArgumentTypeError("Duration should be passed in the following format: \n" + "-d 100 : in sec \n" + "-d 10m : in min \n" + "-d 1h : in hours") + +def match_multiplier(val): + '''match some val against multiplier shortcut inputs ''' + match = re.match("^(\d+)(gb|kpps|%?)$", val) + if match: + digit = int(match.group(1)) + unit = match.group(2) + if not unit: + return digit + elif unit == 'gb': + raise NotImplementedError("gb units are not supported yet") + else: + raise NotImplementedError("kpps units are not supported yet") + else: + raise argparse.ArgumentTypeError("Multiplier should be passed in the following format: \n" + "-m 100 : multiply stream file by this factor \n" + "-m 10gb : from graph calculate the maximum rate as this bandwidth (for each port)\n" + "-m 10kpps : from graph calculate the maximum rate as this pps (for each port)\n" + "-m 40% : from graph calculate the maximum rate as this percent from total port (for each port)") + + + +def is_valid_file(filename): + if not os.path.isfile(filename): + raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename) + + return filename + + +OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], + {'help': "Set multiplier for stream", + 'dest': "mult", + 'default': 1.0, + 'type': match_multiplier}), + + PORT_LIST: ArgumentPack(['--port'], + {"nargs": '+', + 'dest':'ports', + 'metavar': 'PORTS', + 'type': int, + 'help': "A list of ports on which to apply the command", + 'default': []}), + + ALL_PORTS: ArgumentPack(['-a'], + {"action": "store_true", + "dest": "all_ports", + 'help': "Set this flag to apply the command on all available ports"}), + DURATION: ArgumentPack(['-d'], + {'action': "store", + 'metavar': 'TIME', + 'dest': 'duration', + 'type': match_time_unit, + 'default': -1.0, + 'help': "Set duration time for TRex."}), + + FORCE: ArgumentPack(['--force'], + {"action": "store_true", + 'default': False, + 'help': "Set if you want to stop active ports before applying new TRex run on them."}), + + FILE_PATH: ArgumentPack(['-f'], + {'metavar': 'FILE', + 'dest': 'file', + 'nargs': 1, + 'type': is_valid_file, + 'help': "File path to YAML file that describes a stream pack. "}), + + FILE_FROM_DB: ArgumentPack(['--db'], + {'metavar': 'LOADED_STREAM_PACK', + 'help': "A stream pack which already loaded into console cache."}), + + SERVER_IP: ArgumentPack(['--server'], + {'metavar': 'SERVER', + 'help': "server IP"}), + + GLOBAL_STATS: ArgumentPack(['-g'], + {'action': 'store_true', + 'help': "Fetch only global statistics"}), + + PORT_STATS: ArgumentPack(['-p'], + {'action': 'store_true', + 'help': "Fetch only port statistics"}), + + PORT_STATUS: ArgumentPack(['--ps'], + {'action': 'store_true', + 'help': "Fetch only port status data"}), + + # advanced options + PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, + ALL_PORTS], + {'required': True}), + STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH, + FILE_FROM_DB], + {'required': True}), + STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS, + PORT_STATS, + PORT_STATUS], + {}) + } + + +class CCmdArgParser(argparse.ArgumentParser): + + def __init__(self, stateless_client, *args, **kwargs): + super(CCmdArgParser, self).__init__(*args, **kwargs) + self.stateless_client = stateless_client + + def parse_args(self, args=None, namespace=None): + try: + opts = super(CCmdArgParser, self).parse_args(args, namespace) + if opts is None: + return None + + if getattr(opts, "all_ports", None): + opts.ports = self.stateless_client.get_port_ids() + + if getattr(opts, "ports", None): + for port in opts.ports: + if not self.stateless_client.validate_port_list([port]): + self.error("port id '{0}' is not a valid port id\n".format(port)) + + return opts + + except SystemExit: + # recover from system exit scenarios, such as "help", or bad arguments. + return None + + +def get_flags (opt): + return OPTIONS_DB[opt].name_or_flags + +def gen_parser(stateless_client, op_name, description, *args): + parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve', + description=description) + for param in args: + try: + + if isinstance(param, int): + argument = OPTIONS_DB[param] + else: + argument = param + + if isinstance(argument, ArgumentGroup): + if argument.type == MUTEX: + # handle as mutually exclusive group + group = parser.add_mutually_exclusive_group(**argument.options) + for sub_argument in argument.args: + group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags, + **OPTIONS_DB[sub_argument].options) + else: + # ignore invalid objects + continue + elif isinstance(argument, ArgumentPack): + parser.add_argument(*argument.name_or_flags, + **argument.options) + else: + # ignore invalid objects + continue + except KeyError as e: + cause = e.args[0] + raise KeyError("The attribute '{0}' is missing as a field of the {1} option.\n".format(cause, param)) + return parser + + +if __name__ == "__main__": + pass
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/client_utils/text_tables.py b/scripts/automation/trex_control_plane/client_utils/text_tables.py new file mode 100644 index 00000000..2debca38 --- /dev/null +++ b/scripts/automation/trex_control_plane/client_utils/text_tables.py @@ -0,0 +1,34 @@ + +import external_packages +from texttable import Texttable +from common.text_opts import format_text + +class TRexTextTable(Texttable): + + def __init__(self): + Texttable.__init__(self) + # set class attributes so that it'll be more like TRex standard output + self.set_chars(['-', '|', '-', '-']) + self.set_deco(Texttable.HEADER | Texttable.VLINES) + +class TRexTextInfo(Texttable): + + def __init__(self): + Texttable.__init__(self) + # set class attributes so that it'll be more like TRex standard output + self.set_chars(['-', ':', '-', '-']) + self.set_deco(Texttable.VLINES) + +def generate_trex_stats_table(): + pass + +def print_table_with_header(texttable_obj, header=""): + header = header.replace("_", " ").title() + print format_text(header, 'cyan', 'underline') + "\n" + print texttable_obj.draw() + "\n" + + pass + +if __name__ == "__main__": + pass + |