From 5257dbb8253fe5b70b75f9c064c4593ca7aee99f Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 4 Jan 2017 18:46:45 +0200 Subject: draft - unreviewed Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 79 +++++++++------------- .../stl/trex_stl_lib/trex_stl_port.py | 70 +++++++------------ .../stl/trex_stl_lib/trex_stl_stats.py | 1 - .../stl/trex_stl_lib/utils/parsing_opts.py | 21 ++++++ 4 files changed, 77 insertions(+), 94 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index e163d516..1b57218f 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -601,11 +601,9 @@ class STLClient(object): self.xstats, self.async_client.monitor) - - - ############# private functions - used by the class itself ########### + # some preprocessing for port argument def __ports (self, port_id_list): @@ -832,27 +830,6 @@ class STLClient(object): return rc - def __set_rx_sniffer (self, port_id_list, base_filename, limit): - port_id_list = self.__ports(port_id_list) - rc = RC() - - for port_id in port_id_list: - head, tail = os.path.splitext(base_filename) - filename = "{0}-{1}{2}".format(head, port_id, tail) - rc.add(self.ports[port_id].set_rx_sniffer(filename, limit)) - - return rc - - - def __remove_rx_sniffer (self, port_id_list): - port_id_list = self.__ports(port_id_list) - rc = RC() - - for port_id in port_id_list: - rc.add(self.ports[port_id].remove_rx_sniffer()) - - return rc - def __set_rx_queue (self, port_id_list, size): port_id_list = self.__ports(port_id_list) rc = RC() @@ -1071,7 +1048,7 @@ class STLClient(object): ############ functions used by other classes but not users ############## - def _validate_port_list (self, port_id_list): + def _validate_port_list (self, port_id_list, allow_empty = False): # listfiy single int if isinstance(port_id_list, int): port_id_list = [port_id_list] @@ -1080,7 +1057,7 @@ class STLClient(object): if not isinstance(port_id_list, list): raise STLTypeError('port_id_list', type(port_id_list), list) - if not port_id_list: + if not port_id_list and not allow_empty: raise STLError('No ports provided') valid_ports = self.get_all_ports() @@ -2084,9 +2061,9 @@ class STLClient(object): self.set_port_attr(ports, promiscuous = False, link_up = True if restart else None) - self.set_service_mode(ports, False) - self.remove_rx_sniffer(ports) self.remove_rx_queue(ports) + self.set_service_mode(ports, False) + except STLError as e: self.logger.post_cmd(False) @@ -3013,29 +2990,39 @@ class STLClient(object): @__api_check(True) - def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000): + def start_capture (self, tx_ports, rx_ports, limit = 1000): """ - Sets a RX sniffer for port(s) written to a PCAP file + Starts a capture to PCAP on port(s) :parameters: - ports - for which ports to apply a unique sniffer (each port gets a unique file) - base_filename - filename will be appended with '-', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc. + tx_ports - on which ports to capture TX + rx_ports - on which ports to capture RX limit - limit how many packets will be written :raises: + :exe:'STLError' """ - ports = ports if ports is not None else self.get_acquired_ports() - ports = self._validate_port_list(ports) - + + tx_ports = self._validate_port_list(tx_ports, allow_empty = True) + rx_ports = self._validate_port_list(rx_ports, allow_empty = True) + merge_ports = set(tx_ports + rx_ports) + + if not merge_ports: + raise STLError("start_capture - must get at least one port to capture") + # check arguments - validate_type('base_filename', base_filename, basestring) validate_type('limit', limit, (int)) if limit <= 0: raise STLError("'limit' must be a positive value") - self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports)) - rc = self.__set_rx_sniffer(ports, base_filename, limit) + non_service_ports = list_difference(set(tx_ports + rx_ports), self.get_service_enabled_ports()) + if non_service_ports: + raise STLError("Port(s) {0} are not under service mode. PCAP capturing requires all ports to be in service mode") + + + self.logger.pre_cmd("Starting PCAP capturing up to {0} packets".format(limit)) + + rc = self._transmit("start_capture", params = {'limit': limit, 'tx': tx_ports, 'rx': rx_ports}) self.logger.post_cmd(rc) @@ -3045,7 +3032,7 @@ class STLClient(object): @__api_check(True) - def remove_rx_sniffer (self, ports = None): + def stop_capture (self, ports = None): """ Removes RX sniffer from port(s) @@ -3779,21 +3766,21 @@ class STLClient(object): @__console - def set_rx_sniffer_line (self, line): - '''Sets a port sniffer on RX channel in form of a PCAP file''' + def start_capture_line (self, line): + '''Starts PCAP recorder on port(s)''' parser = parsing_opts.gen_parser(self, - "set_rx_sniffer", - self.set_rx_sniffer_line.__doc__, - parsing_opts.PORT_LIST_WITH_ALL, - parsing_opts.OUTPUT_FILENAME, + "capture", + self.start_capture_line.__doc__, + parsing_opts.TX_PORT_LIST, + parsing_opts.RX_PORT_LIST, parsing_opts.LIMIT) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if not opts: return opts - self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit) + self.start_capture(opts.tx_port_list, opts.rx_port_list, opts.limit) return RC_OK() diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 07587b9f..654514cb 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -56,7 +56,8 @@ class Port(object): def __init__ (self, port_id, user, comm_link, session_id, info): self.port_id = port_id - self.state = self.STATE_IDLE + self.state = self.STATE_IDLE + self.service_mode = False self.handler = None self.comm_link = comm_link @@ -247,14 +248,16 @@ class Port(object): raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, port_state)) self.owner = rc.data()['owner'] - + self.next_available_id = int(rc.data()['max_stream_id']) + 1 self.status = rc.data() - + # replace the attributes in a thread safe manner self.set_ts_attr(rc.data()['attr']) - + + self.service_mode = rc.data()['service'] + return self.ok() @@ -490,33 +493,17 @@ class Port(object): @owned - def set_rx_sniffer (self, pcap_filename, limit): + def start_capture (self, pcap_filename, mode, limit): - if not self.is_service_mode_on(): + if mode != 'tx' and not self.is_service_mode_on(): return self.err('port service mode must be enabled for performing RX capturing. Please enable service mode') params = {"handler": self.handler, "port_id": self.port_id, - "type": "capture", - "enabled": True, - "pcap_filename": pcap_filename, + "mode": mode, "limit": limit} - rc = self.transmit("set_rx_feature", params) - if rc.bad(): - return self.err(rc.err()) - - return self.ok() - - - @owned - def remove_rx_sniffer (self): - params = {"handler": self.handler, - "port_id": self.port_id, - "type": "capture", - "enabled": False} - - rc = self.transmit("set_rx_feature", params) + rc = self.transmit("start_capture", params) if rc.bad(): return self.err(rc.err()) @@ -719,23 +706,21 @@ class Port(object): @owned def set_service_mode (self, enabled): - rc = self.set_attr(rx_filter_mode = 'all' if enabled else 'hw') - if not rc: - return rc - - if not enabled: - rc = self.remove_rx_queue() - if not rc: - return rc - - rc = self.remove_rx_sniffer() - if not rc: - return rc - + params = {"handler": self.handler, + "port_id": self.port_id, + "enabled": enabled} + + rc = self.transmit("service", params) + if rc.bad(): + return self.err(rc.err()) + + self.service_mode = enabled return self.ok() + def is_service_mode_on (self): - return self.get_rx_filter_mode() == 'all' + return self.service_mode + @writeable def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler, min_ipg_usec): @@ -902,11 +887,6 @@ class Port(object): # RX info rx_info = self.status['rx_info'] - # RX sniffer - sniffer = rx_info['sniffer'] - info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off' - - # RX queue queue = rx_info['queue'] info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off' @@ -928,9 +908,6 @@ class Port(object): def get_layer_cfg (self): return self.__attr['layer_cfg'] - def get_rx_filter_mode (self): - return self.__attr['rx_filter_mode'] - def is_l3_mode (self): return self.get_layer_cfg()['ipv4']['state'] != 'none' @@ -1002,7 +979,6 @@ class Port(object): "layer mode": format_text(info['layer_mode'], 'green' if info['layer_mode'] == 'IPv4' else 'magenta'), "RX Filter Mode": info['rx_filter_mode'], "RX Queueing": info['rx_queue'], - "RX sniffer": info['rx_sniffer'], "Grat ARP": info['grat_arp'], } diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index 38726062..21c9af87 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -682,7 +682,6 @@ class CTRexInfoGenerator(object): ("-----", []), ("RX Filter Mode", []), ("RX Queueing", []), - ("RX sniffer", []), ("Grat ARP", []), ] ) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index f5dab30c..265c43fb 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -63,6 +63,9 @@ PKT_SIZE SERVICE_OFF +TX_PORT_LIST +RX_PORT_LIST + SRC_IPV4 DST_IPV4 @@ -591,6 +594,24 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': True, 'help': 'Deactivates services on port(s)'}), + TX_PORT_LIST: ArgumentPack(['--tx'], + {'nargs': '+', + 'dest':'tx_port_list', + 'metavar': 'TX', + 'action': 'merge', + 'type': int, + 'help': 'A list of ports to capture on the TX side', + 'default': []}), + + RX_PORT_LIST: ArgumentPack(['--rx'], + {'nargs': '+', + 'dest':'rx_port_list', + 'metavar': 'RX', + 'action': 'merge', + 'type': int, + 'help': 'A list of ports to capture on the RX side', + 'default': []}), + # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], -- cgit From ac2e93d4247b2db94cd07301b274336bb08dec46 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 11 Jan 2017 18:19:47 +0200 Subject: capture - draft commit Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 143 ++++++++++++++++++--- .../stl/trex_stl_lib/utils/common.py | 16 ++- .../stl/trex_stl_lib/utils/parsing_opts.py | 52 +++++++- 3 files changed, 187 insertions(+), 24 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 1b57218f..d75c554e 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -26,6 +26,7 @@ import random import json import traceback import os.path +import argparse ############################ logger ############################# ############################ ############################# @@ -2961,7 +2962,7 @@ class STLClient(object): Resolves ports (ARP resolution) :parameters: - ports - for which ports to apply a unique sniffer (each port gets a unique file) + ports - which ports to resolve retires - how many times to retry on each port (intervals of 100 milliseconds) verbose - log for each request the response :raises: @@ -3022,7 +3023,7 @@ class STLClient(object): self.logger.pre_cmd("Starting PCAP capturing up to {0} packets".format(limit)) - rc = self._transmit("start_capture", params = {'limit': limit, 'tx': tx_ports, 'rx': rx_ports}) + rc = self._transmit("capture", params = {'command': 'start', 'limit': limit, 'tx': tx_ports, 'rx': rx_ports}) self.logger.post_cmd(rc) @@ -3032,24 +3033,82 @@ class STLClient(object): @__api_check(True) - def stop_capture (self, ports = None): + def stop_capture (self, capture_id, output_filename): """ - Removes RX sniffer from port(s) + Stops an active capture + + :parameters: + capture_id - an active capture ID to stop + output_filename - output filename to save capture :raises: + :exe:'STLError' """ - ports = ports if ports is not None else self.get_acquired_ports() - ports = self._validate_port_list(ports) - self.logger.pre_cmd("Removing RX sniffers on port(s) {0}:".format(ports)) - rc = self.__remove_rx_sniffer(ports) + + + # stopping a capture requires: + # 1. stopping + # 2. fetching + # 3. saving to file + + # stop + + self.logger.pre_cmd("Stopping PCAP capture {0}".format(capture_id)) + rc = self._transmit("capture", params = {'command': 'stop', 'capture_id': capture_id}) self.logger.post_cmd(rc) + if not rc: + raise STLError(rc) + + # pkt count + pkt_count = rc.data()['pkt_count'] + + if not output_filename or pkt_count == 0: + return + + self.logger.pre_cmd("Writing {0} packets to '{1}'".format(pkt_count, output_filename)) + + # create a PCAP file + writer = RawPcapWriter(output_filename, linktype = 1) + writer._write_header(None) + + # fetch + while True: + rc = self._transmit("capture", params = {'command': 'fetch', 'capture_id': capture_id, 'pkt_limit': 50}) + if not rc: + self.logger.post_cmd(rc) + raise STLError(rc) + + pkts = rc.data()['pkts'] + for pkt in pkts: + ts = pkt['ts'] + pkt_bin = base64.b64decode(pkt['binary']) + writer._write_packet(pkt_bin, sec = 0, usec = 0) + + if rc.data()['pending'] == 0: + break + + self.logger.post_cmd(rc) + + + # get capture status + @__api_check(True) + def get_capture_status (self): + """ + returns a list of all active captures + each element in the list is an object containing + info about the capture + + """ + + rc = self._transmit("capture", params = {'command': 'status'}) if not rc: raise STLError(rc) + return rc.data() + @__api_check(True) def set_rx_queue (self, ports = None, size = 1000): @@ -3766,23 +3825,71 @@ class STLClient(object): @__console - def start_capture_line (self, line): - '''Starts PCAP recorder on port(s)''' + def capture_line (self, line): + '''Manage PCAP recorders''' - parser = parsing_opts.gen_parser(self, - "capture", - self.start_capture_line.__doc__, - parsing_opts.TX_PORT_LIST, - parsing_opts.RX_PORT_LIST, - parsing_opts.LIMIT) + # default + if not line: + line = "show" + + parser = parsing_opts.gen_parser(self, "capture", self.capture_line.__doc__) + subparsers = parser.add_subparsers(title = "commands", dest="commands") + + # start + start_parser = subparsers.add_parser('start', help = "starts a new capture") + start_parser.add_arg_list(parsing_opts.TX_PORT_LIST, + parsing_opts.RX_PORT_LIST, + parsing_opts.LIMIT) + + # stop + stop_parser = subparsers.add_parser('stop', help = "stops an active capture") + stop_parser.add_arg_list(parsing_opts.CAPTURE_ID, + parsing_opts.OUTPUT_FILENAME) + + # show + show_parser = subparsers.add_parser('show', help = "show all active captures") + + opts = parser.parse_args(line.split()) - opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if not opts: return opts - self.start_capture(opts.tx_port_list, opts.rx_port_list, opts.limit) + # start + if opts.commands == 'start': + if not opts.tx_port_list and not opts.rx_port_list: + start_parser.formatted_error('please provide either --tx or --rx') + return + + self.start_capture(opts.tx_port_list, opts.rx_port_list, opts.limit) + + # stop + elif opts.commands == 'stop': + self.stop_capture(opts.capture_id, opts.output_filename) + + # show + else: + data = self.get_capture_status() + + stats_table = text_tables.TRexTextTable() + stats_table.set_cols_align(["c"] * 6) + stats_table.set_cols_width([15] * 6) + + for elem in data: + row = [elem['id'], + elem['state'], + '[{0}/{1}]'.format(elem['count'], elem['limit']), + format_num(elem['bytes'], suffix = 'B'), + bitfield_to_str(elem['filter']['tx']), + bitfield_to_str(elem['filter']['rx'])] + + stats_table.add_rows([row], header=False) + + stats_table.header(['ID', 'Status', 'Count', 'Bytes', 'TX Ports', 'RX Ports']) + text_tables.print_table_with_header(stats_table, "Captures") + return RC_OK() + @__console diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py index cbbacb27..c386451b 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py @@ -107,4 +107,18 @@ def list_remove_dup (l): return tmp - +def bitfield_to_list (bf): + rc = [] + bitpos = 0 + + while bf > 0: + if bf & 0x1: + rc.append(bitpos) + bitpos += 1 + bf = bf >> 1 + + return rc + +def bitfield_to_str (bf): + lst = bitfield_to_list(bf) + return "-" if not lst else ', '.join([str(x) for x in lst]) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index 265c43fb..cb594ef4 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -69,6 +69,8 @@ RX_PORT_LIST SRC_IPV4 DST_IPV4 +CAPTURE_ID + GLOBAL_STATS PORT_STATS PORT_STATUS @@ -81,12 +83,14 @@ EXTENDED_INC_ZERO_STATS STREAMS_MASK CORE_MASK_GROUP +CAPTURE_PORTS_GROUP # ALL_STREAMS # STREAM_LIST_WITH_ALL # list of ArgumentGroup types MUTEX +NON_MUTEX ''' @@ -392,7 +396,6 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'help': 'Output PCAP filename', 'dest': 'output_filename', 'default': None, - 'required': True, 'type': str}), @@ -612,6 +615,12 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'help': 'A list of ports to capture on the RX side', 'default': []}), + CAPTURE_ID: ArgumentPack(['-i', '--id'], + {'help': "capture ID to remove", + 'dest': "capture_id", + 'type': int, + 'required': True}), + # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], @@ -636,6 +645,7 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], CORE_MASK], {'required': False}), + CAPTURE_PORTS_GROUP: ArgumentGroup(NON_MUTEX, [TX_PORT_LIST, RX_PORT_LIST], {}), } class _MergeAction(argparse._AppendAction): @@ -654,12 +664,30 @@ class _MergeAction(argparse._AppendAction): class CCmdArgParser(argparse.ArgumentParser): - def __init__(self, stateless_client, *args, **kwargs): + def __init__(self, stateless_client = None, x = None, *args, **kwargs): super(CCmdArgParser, self).__init__(*args, **kwargs) self.stateless_client = stateless_client self.cmd_name = kwargs.get('prog') self.register('action', 'merge', _MergeAction) + + def add_arg_list (self, *args): + populate_parser(self, *args) + + def add_subparsers(self, *args, **kwargs): + sub = super(CCmdArgParser, self).add_subparsers(*args, **kwargs) + + add_parser = sub.add_parser + stateless_client = self.stateless_client + + def add_parser_hook (self, *args, **kwargs): + parser = add_parser(self, *args, **kwargs) + parser.stateless_client = stateless_client + return parser + + sub.add_parser = add_parser_hook + return sub + # hook this to the logger def _print_message(self, message, file=None): self.stateless_client.logger.log(message) @@ -730,13 +758,15 @@ class CCmdArgParser(argparse.ArgumentParser): # recover from system exit scenarios, such as "help", or bad arguments. return RC_ERR("'{0}' - {1}".format(self.cmd_name, "no action")) + def formatted_error (self, msg): + self.print_usage() + self.stateless_client.logger.log(msg) + 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) +def populate_parser (parser, *args): for param in args: try: @@ -752,6 +782,12 @@ def gen_parser(stateless_client, op_name, description, *args): for sub_argument in argument.args: group.add_argument(*OPTIONS_DB[sub_argument].name_or_flags, **OPTIONS_DB[sub_argument].options) + + elif argument.type == NON_MUTEX: + group = parser.add_argument_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 @@ -764,6 +800,12 @@ def gen_parser(stateless_client, op_name, description, *args): 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)) + +def gen_parser(stateless_client, op_name, description, *args): + parser = CCmdArgParser(stateless_client, prog=op_name, conflict_handler='resolve', + description=description) + + populate_parser(parser, *args) return parser -- cgit From 951b09ef1b892594840f091f861f11ad274541ec Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 18 Jan 2017 13:08:41 +0200 Subject: many capture modes in Python console Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 172 +++++++++------------ .../stl/trex_stl_lib/utils/parsing_opts.py | 29 +++- .../stl/trex_stl_lib/utils/text_opts.py | 16 +- 3 files changed, 108 insertions(+), 109 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index d75c554e..c632ad7c 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -17,10 +17,12 @@ from .utils.text_opts import * from functools import wraps from texttable import ansi_len + from collections import namedtuple from yaml import YAMLError import time import datetime +import threading import re import random import json @@ -601,7 +603,7 @@ class STLClient(object): self.util_stats, self.xstats, self.async_client.monitor) - + ############# private functions - used by the class itself ########### @@ -2981,7 +2983,7 @@ class STLClient(object): self.logger.post_cmd(rc) if verbose: - for x in filter(bool, rc.data()): + for x in filter(bool, listify(rc.data())): self.logger.log(format_text("{0}".format(x), 'bold')) if not rc: @@ -2999,6 +3001,10 @@ class STLClient(object): tx_ports - on which ports to capture TX rx_ports - on which ports to capture RX limit - limit how many packets will be written + + :returns: + the new capture_id + :raises: + :exe:'STLError' @@ -3018,7 +3024,7 @@ class STLClient(object): non_service_ports = list_difference(set(tx_ports + rx_ports), self.get_service_enabled_ports()) if non_service_ports: - raise STLError("Port(s) {0} are not under service mode. PCAP capturing requires all ports to be in service mode") + raise STLError("Port(s) {0} are not under service mode. PCAP capturing requires all ports to be in service mode".format(non_service_ports)) self.logger.pre_cmd("Starting PCAP capturing up to {0} packets".format(limit)) @@ -3030,8 +3036,39 @@ class STLClient(object): if not rc: raise STLError(rc) + return rc.data()['capture_id'] + + + def __fetch_capture_packets (self, capture_id, output_filename, pkt_count): + self.logger.pre_cmd("Writing {0} packets to '{1}'".format(pkt_count, output_filename)) + + # create a PCAP file + writer = RawPcapWriter(output_filename, linktype = 1) + writer._write_header(None) + + # fetch + pending = pkt_count + rc = RC_OK() + while pending > 0: + rc = self._transmit("capture", params = {'command': 'fetch', 'capture_id': capture_id, 'pkt_limit': 50}) + if not rc: + self.logger.post_cmd(rc) + raise STLError(rc) + + pkts = rc.data()['pkts'] + for pkt in pkts: + ts = pkt['ts'] + pkt_bin = base64.b64decode(pkt['binary']) + writer._write_packet(pkt_bin, sec = 0, usec = 0) + + pending = rc.data()['pending'] + + + self.logger.post_cmd(rc) + + @__api_check(True) def stop_capture (self, capture_id, output_filename): """ @@ -3045,8 +3082,6 @@ class STLClient(object): + :exe:'STLError' """ - - # stopping a capture requires: # 1. stopping @@ -3063,36 +3098,19 @@ class STLClient(object): # pkt count pkt_count = rc.data()['pkt_count'] - - if not output_filename or pkt_count == 0: - return - self.logger.pre_cmd("Writing {0} packets to '{1}'".format(pkt_count, output_filename)) - - # create a PCAP file - writer = RawPcapWriter(output_filename, linktype = 1) - writer._write_header(None) - - # fetch - while True: - rc = self._transmit("capture", params = {'command': 'fetch', 'capture_id': capture_id, 'pkt_limit': 50}) - if not rc: - self.logger.post_cmd(rc) - raise STLError(rc) - - pkts = rc.data()['pkts'] - for pkt in pkts: - ts = pkt['ts'] - pkt_bin = base64.b64decode(pkt['binary']) - writer._write_packet(pkt_bin, sec = 0, usec = 0) - - if rc.data()['pending'] == 0: - break + # fetch packets + if output_filename: + self.__fetch_capture_packets(capture_id, output_filename, pkt_count) + # remove + self.logger.pre_cmd("Removing PCAP capture {0} from server".format(capture_id)) + rc = self._transmit("capture", params = {'command': 'remove', 'capture_id': capture_id}) self.logger.post_cmd(rc) + if not rc: + raise STLError(rc) - # get capture status @__api_check(True) def get_capture_status (self): """ @@ -3109,7 +3127,25 @@ class STLClient(object): return rc.data() - + @__api_check(True) + def remove_all_captures (self): + """ + Removes any existing captures + """ + captures = self.get_capture_status() + + self.logger.pre_cmd("Removing all PCAP captures from server") + + for c in captures: + # remove + rc = self._transmit("capture", params = {'command': 'remove', 'capture_id': c['id']}) + if not rc: + raise STLError(rc) + + self.logger.post_cmd(RC_OK()) + + + @__api_check(True) def set_rx_queue (self, ports = None, size = 1000): """ @@ -3230,6 +3266,7 @@ class STLClient(object): return wrap + @__console def ping_line (self, line): '''pings the server / specific IP''' @@ -3820,77 +3857,9 @@ class STLClient(object): opts.link, opts.led, opts.flow_ctrl) + + - - - - @__console - def capture_line (self, line): - '''Manage PCAP recorders''' - - # default - if not line: - line = "show" - - parser = parsing_opts.gen_parser(self, "capture", self.capture_line.__doc__) - subparsers = parser.add_subparsers(title = "commands", dest="commands") - - # start - start_parser = subparsers.add_parser('start', help = "starts a new capture") - start_parser.add_arg_list(parsing_opts.TX_PORT_LIST, - parsing_opts.RX_PORT_LIST, - parsing_opts.LIMIT) - - # stop - stop_parser = subparsers.add_parser('stop', help = "stops an active capture") - stop_parser.add_arg_list(parsing_opts.CAPTURE_ID, - parsing_opts.OUTPUT_FILENAME) - - # show - show_parser = subparsers.add_parser('show', help = "show all active captures") - - opts = parser.parse_args(line.split()) - - if not opts: - return opts - - # start - if opts.commands == 'start': - if not opts.tx_port_list and not opts.rx_port_list: - start_parser.formatted_error('please provide either --tx or --rx') - return - - self.start_capture(opts.tx_port_list, opts.rx_port_list, opts.limit) - - # stop - elif opts.commands == 'stop': - self.stop_capture(opts.capture_id, opts.output_filename) - - # show - else: - data = self.get_capture_status() - - stats_table = text_tables.TRexTextTable() - stats_table.set_cols_align(["c"] * 6) - stats_table.set_cols_width([15] * 6) - - for elem in data: - row = [elem['id'], - elem['state'], - '[{0}/{1}]'.format(elem['count'], elem['limit']), - format_num(elem['bytes'], suffix = 'B'), - bitfield_to_str(elem['filter']['tx']), - bitfield_to_str(elem['filter']['rx'])] - - stats_table.add_rows([row], header=False) - - stats_table.header(['ID', 'Status', 'Count', 'Bytes', 'TX Ports', 'RX Ports']) - text_tables.print_table_with_header(stats_table, "Captures") - - - return RC_OK() - - @__console def resolve_line (self, line): @@ -4089,3 +4058,4 @@ class STLClient(object): self.set_service_mode(ports = opts.ports, enabled = opts.enabled) + diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index cb594ef4..8d3aedbe 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -85,6 +85,10 @@ STREAMS_MASK CORE_MASK_GROUP CAPTURE_PORTS_GROUP +MONITOR_TYPE_VERBOSE +MONITOR_TYPE_PIPE +MONITOR_TYPE + # ALL_STREAMS # STREAM_LIST_WITH_ALL @@ -606,6 +610,7 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'help': 'A list of ports to capture on the TX side', 'default': []}), + RX_PORT_LIST: ArgumentPack(['--rx'], {'nargs': '+', 'dest':'rx_port_list', @@ -614,7 +619,21 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'type': int, 'help': 'A list of ports to capture on the RX side', 'default': []}), - + + + MONITOR_TYPE_VERBOSE: ArgumentPack(['-v', '--verbose'], + {'action': 'store_true', + 'dest': 'verbose', + 'default': False, + 'help': 'output to screen as verbose'}), + + MONITOR_TYPE_PIPE: ArgumentPack(['-p', '--pipe'], + {'action': 'store_true', + 'dest': 'pipe', + 'default': False, + 'help': 'forward packets to a pipe'}), + + CAPTURE_ID: ArgumentPack(['-i', '--id'], {'help': "capture ID to remove", 'dest': "capture_id", @@ -646,6 +665,12 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'required': False}), CAPTURE_PORTS_GROUP: ArgumentGroup(NON_MUTEX, [TX_PORT_LIST, RX_PORT_LIST], {}), + + + MONITOR_TYPE: ArgumentGroup(MUTEX, [MONITOR_TYPE_VERBOSE, + MONITOR_TYPE_PIPE], + {'required': False}), + } class _MergeAction(argparse._AppendAction): @@ -760,7 +785,7 @@ class CCmdArgParser(argparse.ArgumentParser): def formatted_error (self, msg): self.print_usage() - self.stateless_client.logger.log(msg) + self._print_message(('%s: error: %s\n') % (self.prog, msg)) def get_flags (opt): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py index 63b05bf4..3ffd07e2 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py @@ -133,12 +133,16 @@ def underline(text): # apply attribute on each non-empty line def text_attribute(text, attribute): - return '\n'.join(['{start}{txt}{end}'.format( - start = TEXT_CODES[attribute]['start'], - txt = line, - end = TEXT_CODES[attribute]['end']) - if line else '' for line in ('%s' % text).split('\n')]) - + if isinstance(text, str): + return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], + txt=text, + stop=TEXT_CODES[attribute]['end']) + elif isinstance(text, unicode): + return u"{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], + txt=text, + stop=TEXT_CODES[attribute]['end']) + else: + raise Exception("not a string") FUNC_DICT = {'blue': blue, 'bold': bold, -- cgit From 641fed03d8e407b6dca94f5280b9a1b4c768f601 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 19 Jan 2017 13:30:48 +0200 Subject: fine tune Signed-off-by: imarom --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index c632ad7c..5435619a 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3003,7 +3003,8 @@ class STLClient(object): limit - limit how many packets will be written :returns: - the new capture_id + returns a dictionary containing + {'id: , 'ts': } :raises: + :exe:'STLError' @@ -3036,7 +3037,7 @@ class STLClient(object): if not rc: raise STLError(rc) - return rc.data()['capture_id'] + return {'id': rc.data()['capture_id'], 'ts': rc.data()['ts']} @@ -3070,7 +3071,7 @@ class STLClient(object): @__api_check(True) - def stop_capture (self, capture_id, output_filename): + def stop_capture (self, capture_id, output_filename = None): """ Stops an active capture -- cgit From f5f92b068561dcdf8414494e5daf6d285ea24135 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 22 Jan 2017 15:36:20 +0200 Subject: few tweaks Signed-off-by: imarom --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 14 ++++++++++---- .../stl/trex_stl_lib/trex_stl_jsonrpc_client.py | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 5435619a..c82d77fb 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3058,13 +3058,19 @@ class STLClient(object): self.logger.post_cmd(rc) raise STLError(rc) - pkts = rc.data()['pkts'] + pkts = rc.data()['pkts'] + pending = rc.data()['pending'] + start_ts = rc.data()['start_ts'] + for pkt in pkts: - ts = pkt['ts'] + ts = pkt['ts'] - start_ts + ts_sec = int(ts) + ts_usec = int( (ts - ts_sec) * 1e6 ) + pkt_bin = base64.b64decode(pkt['binary']) - writer._write_packet(pkt_bin, sec = 0, usec = 0) + writer._write_packet(pkt_bin, sec = ts_sec, usec = ts_usec) - pending = rc.data()['pending'] + self.logger.post_cmd(rc) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py index 72c9317a..ff07b59a 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py @@ -184,7 +184,7 @@ class JsonRpcClient(object): break except zmq.Again: tries += 1 - if tries > 5: + if tries > 0: self.disconnect() return RC_ERR("*** [RPC] - Failed to send message to server") @@ -200,7 +200,7 @@ class JsonRpcClient(object): break except zmq.Again: tries += 1 - if tries > 5: + if tries > 0: self.disconnect() return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport)) -- cgit From 19df06349d311377ca1ef10f91ef1f786b41418b Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 24 Jan 2017 14:11:32 +0200 Subject: code review cleanups - C++ Signed-off-by: imarom --- .../automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index f7432107..d81765c6 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3049,7 +3049,7 @@ class STLClient(object): if not rc: raise STLError(rc) - return {'id': rc.data()['capture_id'], 'ts': rc.data()['ts']} + return {'id': rc.data()['capture_id'], 'ts': rc.data()['start_ts']} -- cgit From 3689edf311778c8cb921db61f293db6cd43a9b14 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 25 Jan 2017 13:54:51 +0200 Subject: capture - personal code review Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index d81765c6..654ceaf6 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3005,7 +3005,7 @@ class STLClient(object): @__api_check(True) - def start_capture (self, tx_ports, rx_ports, limit = 1000): + def start_capture (self, tx_ports, rx_ports, limit = 1000, mode = 'fixed'): """ Starts a capture to PCAP on port(s) @@ -3014,6 +3014,11 @@ class STLClient(object): rx_ports - on which ports to capture RX limit - limit how many packets will be written + mode - 'fixed': when full, future packets will be + dropped + 'cyclic: when full, oldest packets will be + dropped + :returns: returns a dictionary containing {'id: , 'ts': } @@ -3023,6 +3028,7 @@ class STLClient(object): """ + # check arguments tx_ports = self._validate_port_list(tx_ports, allow_empty = True) rx_ports = self._validate_port_list(rx_ports, allow_empty = True) merge_ports = set(tx_ports + rx_ports) @@ -3030,28 +3036,29 @@ class STLClient(object): if not merge_ports: raise STLError("start_capture - must get at least one port to capture") - # check arguments validate_type('limit', limit, (int)) if limit <= 0: raise STLError("'limit' must be a positive value") + if mode not in ('fixed', 'cyclic'): + raise STLError("'mode' must be either 'fixed' or 'cyclic'") + + # verify service mode non_service_ports = list_difference(set(tx_ports + rx_ports), self.get_service_enabled_ports()) if non_service_ports: raise STLError("Port(s) {0} are not under service mode. PCAP capturing requires all ports to be in service mode".format(non_service_ports)) + # actual job self.logger.pre_cmd("Starting PCAP capturing up to {0} packets".format(limit)) - - rc = self._transmit("capture", params = {'command': 'start', 'limit': limit, 'tx': tx_ports, 'rx': rx_ports}) + rc = self._transmit("capture", params = {'command': 'start', 'limit': limit, 'mode': mode, 'tx': tx_ports, 'rx': rx_ports}) self.logger.post_cmd(rc) - if not rc: raise STLError(rc) return {'id': rc.data()['capture_id'], 'ts': rc.data()['start_ts']} - def __fetch_capture_packets (self, capture_id, output_filename, pkt_count): -- cgit From acf815dbf67d7a3be8fefd84eea1d25465f71136 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 26 Jan 2017 17:26:00 +0200 Subject: code review - few cleanups Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 114 ++++++++++++--------- .../stl/trex_stl_lib/trex_stl_jsonrpc_client.py | 9 ++ .../stl/trex_stl_lib/trex_stl_port.py | 16 --- .../stl/trex_stl_lib/utils/common.py | 5 + .../stl/trex_stl_lib/utils/parsing_opts.py | 11 +- 5 files changed, 89 insertions(+), 66 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 654ceaf6..571334ee 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -22,13 +22,11 @@ from collections import namedtuple from yaml import YAMLError import time import datetime -import threading import re import random import json import traceback import os.path -import argparse ############################ logger ############################# ############################ ############################# @@ -3007,32 +3005,42 @@ class STLClient(object): @__api_check(True) def start_capture (self, tx_ports, rx_ports, limit = 1000, mode = 'fixed'): """ - Starts a capture to PCAP on port(s) + Starts a low rate packet capturing on the server :parameters: tx_ports - on which ports to capture TX rx_ports - on which ports to capture RX limit - limit how many packets will be written + memory requierment is O(9K * limit) - mode - 'fixed': when full, future packets will be + mode - 'fixed': when full, newer packets will be dropped - 'cyclic: when full, oldest packets will be + + 'cyclic: when full, older packets will be dropped :returns: - returns a dictionary containing + returns a dictionary: {'id: , 'ts': } + where 'id' is the new capture ID for future commands + and 'ts' is that server monotonic timestamp when + the capture was created + :raises: + :exe:'STLError' """ - + # TODO: remove this when TX is implemented + if tx_ports: + raise STLError('TX port capturing is not yet implemented') + # check arguments tx_ports = self._validate_port_list(tx_ports, allow_empty = True) rx_ports = self._validate_port_list(rx_ports, allow_empty = True) merge_ports = set(tx_ports + rx_ports) + # make sure at least one port to capture if not merge_ports: raise STLError("start_capture - must get at least one port to capture") @@ -3044,13 +3052,13 @@ class STLClient(object): raise STLError("'mode' must be either 'fixed' or 'cyclic'") # verify service mode - non_service_ports = list_difference(set(tx_ports + rx_ports), self.get_service_enabled_ports()) + non_service_ports = list_difference(merge_ports, self.get_service_enabled_ports()) if non_service_ports: - raise STLError("Port(s) {0} are not under service mode. PCAP capturing requires all ports to be in service mode".format(non_service_ports)) + raise STLError("Port(s) {0} are not under service mode. packet capturing requires all ports to be in service mode".format(non_service_ports)) # actual job - self.logger.pre_cmd("Starting PCAP capturing up to {0} packets".format(limit)) + self.logger.pre_cmd("Starting packet capturing up to {0} packets".format(limit)) rc = self._transmit("capture", params = {'command': 'start', 'limit': limit, 'mode': mode, 'tx': tx_ports, 'rx': rx_ports}) self.logger.post_cmd(rc) @@ -3059,50 +3067,18 @@ class STLClient(object): return {'id': rc.data()['capture_id'], 'ts': rc.data()['start_ts']} - - - def __fetch_capture_packets (self, capture_id, output_filename, pkt_count): - self.logger.pre_cmd("Writing {0} packets to '{1}'".format(pkt_count, output_filename)) - # create a PCAP file - writer = RawPcapWriter(output_filename, linktype = 1) - writer._write_header(None) - - # fetch - pending = pkt_count - rc = RC_OK() - while pending > 0: - rc = self._transmit("capture", params = {'command': 'fetch', 'capture_id': capture_id, 'pkt_limit': 50}) - if not rc: - self.logger.post_cmd(rc) - raise STLError(rc) - - pkts = rc.data()['pkts'] - pending = rc.data()['pending'] - start_ts = rc.data()['start_ts'] - - for pkt in pkts: - ts = pkt['ts'] - start_ts - ts_sec = int(ts) - ts_usec = int( (ts - ts_sec) * 1e6 ) - - pkt_bin = base64.b64decode(pkt['binary']) - writer._write_packet(pkt_bin, sec = ts_sec, usec = ts_usec) - - - - - self.logger.post_cmd(rc) - @__api_check(True) def stop_capture (self, capture_id, output_filename = None): """ - Stops an active capture + Stops an active capture and optionally save it to a PCAP file :parameters: capture_id - an active capture ID to stop output_filename - output filename to save capture + if None all captured packets + will be discarded :raises: + :exe:'STLError' @@ -3116,7 +3092,7 @@ class STLClient(object): # stop - self.logger.pre_cmd("Stopping PCAP capture {0}".format(capture_id)) + self.logger.pre_cmd("Stopping packet capture {0}".format(capture_id)) rc = self._transmit("capture", params = {'command': 'stop', 'capture_id': capture_id}) self.logger.post_cmd(rc) if not rc: @@ -3137,6 +3113,47 @@ class STLClient(object): raise STLError(rc) + + # fetch packets from the server and save them to a file + def __fetch_capture_packets (self, capture_id, output_filename, pkt_count): + self.logger.pre_cmd("Writing {0} packets to '{1}'".format(pkt_count, output_filename)) + + # create a PCAP file + writer = RawPcapWriter(output_filename, linktype = 1) + writer._write_header(None) + + pending = pkt_count + rc = RC_OK() + + # fetch with iteratios - each iteration up to 50 packets + while pending > 0: + rc = self._transmit("capture", params = {'command': 'fetch', 'capture_id': capture_id, 'pkt_limit': 50}) + if not rc: + self.logger.post_cmd(rc) + raise STLError(rc) + + # make sure we are getting some progress + assert(rc.data()['pending'] < pending) + + pkts = rc.data()['pkts'] + pending = rc.data()['pending'] + start_ts = rc.data()['start_ts'] + + # write packets + for pkt in pkts: + # split the server timestamp relative to the capture start time + ts_sec, ts_usec = sec_split_usec(pkt['ts'] - start_ts) + + pkt_bin = base64.b64decode(pkt['binary']) + writer._write_packet(pkt_bin, sec = ts_sec, usec = ts_usec) + + + + + self.logger.post_cmd(rc) + + + @__api_check(True) def get_capture_status (self): """ @@ -3145,14 +3162,13 @@ class STLClient(object): info about the capture """ - rc = self._transmit("capture", params = {'command': 'status'}) - if not rc: raise STLError(rc) return rc.data() + @__api_check(True) def remove_all_captures (self): """ @@ -3160,7 +3176,7 @@ class STLClient(object): """ captures = self.get_capture_status() - self.logger.pre_cmd("Removing all PCAP captures from server") + self.logger.pre_cmd("Removing all packet captures from server") for c in captures: # remove diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py index db216532..405f76be 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py @@ -10,6 +10,7 @@ import struct from .trex_stl_types import * from .utils.common import random_id_gen from .utils.zipmsg import ZippedMsg +from threading import Lock class bcolors: BLUE = '\033[94m' @@ -72,6 +73,8 @@ class JsonRpcClient(object): self.id_gen = random_id_gen() self.zipper = ZippedMsg() + self.lock = Lock() + def get_connection_details (self): rc = {} rc['server'] = self.server @@ -137,6 +140,12 @@ class JsonRpcClient(object): def send_msg (self, msg, retry = 0): + # REQ/RESP pattern in ZMQ requires no interrupts during the send + with self.lock: + return self.__send_msg(msg, retry) + + + def __send_msg (self, msg, retry = 0): # print before if self.logger.check_verbose(self.logger.VERBOSE_HIGH): self.verbose_msg("Sending Request To Server:\n\n" + self.pretty_json(msg) + "\n") diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 31d752af..1ef3a8ff 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -492,22 +492,6 @@ class Port(object): return self.ok() - @owned - def start_capture (self, pcap_filename, mode, limit): - - if mode != 'tx' and not self.is_service_mode_on(): - return self.err('port service mode must be enabled for performing RX capturing. Please enable service mode') - - params = {"handler": self.handler, - "port_id": self.port_id, - "mode": mode, - "limit": limit} - - rc = self.transmit("start_capture", params) - if rc.bad(): - return self.err(rc.err()) - - return self.ok() @writeable def set_l2_mode (self, dst_mac): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py index c386451b..72d3fa9f 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py @@ -71,6 +71,11 @@ def list_difference (l1, l2): def is_sub_list (l1, l2): return set(l1) <= set(l2) +# splits a timestamp in seconds to sec/usec +def sec_split_usec (ts): + return int(ts), int( (ts - int(ts)) * 1e6 ) + + # a simple passive timer class PassiveTimer(object): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index 8d3aedbe..53db533c 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -689,19 +689,23 @@ class _MergeAction(argparse._AppendAction): class CCmdArgParser(argparse.ArgumentParser): - def __init__(self, stateless_client = None, x = None, *args, **kwargs): + def __init__(self, stateless_client = None, *args, **kwargs): super(CCmdArgParser, self).__init__(*args, **kwargs) self.stateless_client = stateless_client self.cmd_name = kwargs.get('prog') self.register('action', 'merge', _MergeAction) + def add_arg_list (self, *args): populate_parser(self, *args) + + # a simple hook for add subparsers to add stateless client def add_subparsers(self, *args, **kwargs): sub = super(CCmdArgParser, self).add_subparsers(*args, **kwargs) + # save pointer to the original add parser method add_parser = sub.add_parser stateless_client = self.stateless_client @@ -710,13 +714,17 @@ class CCmdArgParser(argparse.ArgumentParser): parser.stateless_client = stateless_client return parser + # override with the hook sub.add_parser = add_parser_hook + return sub + # hook this to the logger def _print_message(self, message, file=None): self.stateless_client.logger.log(message) + def error(self, message): self.print_usage() self._print_message(('%s: error: %s\n') % (self.prog, message)) @@ -783,6 +791,7 @@ class CCmdArgParser(argparse.ArgumentParser): # recover from system exit scenarios, such as "help", or bad arguments. return RC_ERR("'{0}' - {1}".format(self.cmd_name, "no action")) + def formatted_error (self, msg): self.print_usage() self._print_message(('%s: error: %s\n') % (self.prog, msg)) -- cgit From 42664b05103d0f4a7ed272301051d58d6e8f3737 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 29 Jan 2017 17:49:53 +0200 Subject: code review - cont. Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 2 +- .../stl/trex_stl_lib/trex_stl_types.py | 2 +- .../stl/trex_stl_lib/utils/text_opts.py | 16 ++++++---------- 3 files changed, 8 insertions(+), 12 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 571334ee..c46a7d78 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3296,7 +3296,7 @@ class STLClient(object): try: rc = f(*args) except STLError as e: - client.logger.log("\nAction has failed with the following error:\n" + format_text(e.brief() + "\n", 'bold')) + client.logger.log("\nAction has failed with the following error:\n\n" + format_text(e.brief() + "\n", 'bold')) return RC_ERR(e.brief()) # if got true - print time diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py index 0230db23..7ac508a2 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py @@ -64,7 +64,7 @@ class RC(): err_count += 1 if len(err_list) < show_count: err_list.append(format_text(x, 'bold')) - s = '\n' + s = '' if err_count > show_count: s += format_text('Occurred %s errors, showing first %s:\n' % (err_count, show_count), 'bold') s += '\n'.join(err_list) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py index 3ffd07e2..477d81a6 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py @@ -133,16 +133,12 @@ def underline(text): # apply attribute on each non-empty line def text_attribute(text, attribute): - if isinstance(text, str): - return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], - txt=text, - stop=TEXT_CODES[attribute]['end']) - elif isinstance(text, unicode): - return u"{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], - txt=text, - stop=TEXT_CODES[attribute]['end']) - else: - raise Exception("not a string") + return '\n'.join(['{start}{txt}{end}'.format( + start = TEXT_CODES[attribute]['start'], + txt = line, + end = TEXT_CODES[attribute]['end']) + if line else '' for line in ('%s' % text).split('\n')]) + FUNC_DICT = {'blue': blue, 'bold': bold, -- cgit From 78a3270eda09ba24a7f9f795800df3337f8953bf Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 29 Jan 2017 18:35:19 +0200 Subject: documenation errors Signed-off-by: imarom --- .../stl/trex_stl_lib/trex_stl_client.py | 32 ++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index c46a7d78..215c0253 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -3008,16 +3008,18 @@ class STLClient(object): Starts a low rate packet capturing on the server :parameters: - tx_ports - on which ports to capture TX - rx_ports - on which ports to capture RX - limit - limit how many packets will be written - memory requierment is O(9K * limit) - - mode - 'fixed': when full, newer packets will be - dropped - - 'cyclic: when full, older packets will be - dropped + tx_ports: list + on which ports to capture TX + + rx_ports: list + on which ports to capture RX + + limit: int + limit how many packets will be written memory requierment is O(9K * limit) + + mode: str + 'fixed' - when full, newer packets will be dropped + 'cyclic' - when full, older packets will be dropped :returns: returns a dictionary: @@ -3075,10 +3077,12 @@ class STLClient(object): Stops an active capture and optionally save it to a PCAP file :parameters: - capture_id - an active capture ID to stop - output_filename - output filename to save capture - if None all captured packets - will be discarded + capture_id: int + an active capture ID to stop + + output_filename: str + output filename to save capture + if 'None', all captured packets will be discarded :raises: + :exe:'STLError' -- cgit