From a913ed85424bd1ab38c8842dd16dd10b90db12fe Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 25 Jul 2016 11:20:16 +0300 Subject: TUI tweaks --- .../trex_control_plane/stl/console/trex_console.py | 43 +++---- .../trex_control_plane/stl/console/trex_tui.py | 50 ++++++-- .../stl/trex_stl_lib/trex_stl_client.py | 130 +++++++++++---------- .../stl/trex_stl_lib/utils/parsing_opts.py | 31 +++-- 4 files changed, 144 insertions(+), 110 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 9e3f2600..41a04617 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -241,20 +241,7 @@ class TRexConsole(TRexGeneralCmd): def postcmd(self, stop, line): - - if not self.stateless_client.is_connected(): - self.prompt = "trex(offline)>" - self.supported_rpc = None - - elif not self.stateless_client.get_acquired_ports(): - self.prompt = "trex(read-only)>" - - elif self.stateless_client.is_all_ports_acquired(): - self.prompt = "trex>" - - else: - self.prompt = "trex {0}>".format(self.stateless_client.get_acquired_ports()) - + self.prompt = self.stateless_client.generate_prompt(prefix = 'trex') return stop @@ -316,25 +303,21 @@ class TRexConsole(TRexGeneralCmd): self.do_history("-h") def do_shell (self, line): - return self.do_history(line) + self.do_history(line) def do_push (self, line): '''Push a local PCAP file\n''' - return self.stateless_client.push_line(line) - - #def do_push_remote (self, line): - # '''Push a remote accessible PCAP file\n''' - # return self.stateless_client.push_remote_line(line) + self.stateless_client.push_line(line) def help_push (self): - return self.do_push("-h") + self.do_push("-h") def do_portattr (self, line): '''Change/show port(s) attributes\n''' - return self.stateless_client.set_port_attr_line(line) + self.stateless_client.set_port_attr_line(line) def help_portattr (self): - return self.do_portattr("-h") + self.do_portattr("-h") @verify_connected def do_map (self, line): @@ -548,7 +531,7 @@ class TRexConsole(TRexGeneralCmd): def do_events (self, line): '''shows events recieved from server\n''' - return self.stateless_client.get_events_line(line) + self.stateless_client.get_events_line(line) def complete_profile(self, text, line, begidx, endidx): @@ -562,7 +545,6 @@ class TRexConsole(TRexGeneralCmd): @verify_connected def do_tui (self, line): '''Shows a graphical console\n''' - parser = parsing_opts.gen_parser(self, "tui", self.do_tui.__doc__, @@ -581,16 +563,19 @@ class TRexConsole(TRexGeneralCmd): info = self.stateless_client.get_connection_info() exe = './trex-console --top -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port']) - cmd = ['/usr/bin/xterm', '-geometry', '111x49', '-sl', '0', '-title', 'trex_tui', '-e', exe] + cmd = ['/usr/bin/xterm', '-geometry', '{0}x{1}'.format(self.tui.MIN_COLS, self.tui.MIN_ROWS), '-sl', '0', '-title', 'trex_tui', '-e', exe] # detach child self.terminal = subprocess.Popen(cmd, preexec_fn = os.setpgrp) return - - with self.stateless_client.logger.supress(): - self.tui.show(self.stateless_client, locked = opts.locked) + + try: + with self.stateless_client.logger.supress(): + self.tui.show(self.stateless_client, locked = opts.locked) + except self.tui.ScreenSizeException as e: + print(format_text(str(e) + "\n", 'bold')) def help_tui (self): diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py index 385cd098..2e26fdfc 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -5,6 +5,7 @@ import time from collections import OrderedDict, deque import datetime import readline +from texttable import ansi_len if sys.version_info > (3,0): from io import StringIO @@ -449,6 +450,17 @@ class TrexTUI(): STATE_RECONNECT = 2 is_graph = False + MIN_ROWS = 50 + MIN_COLS = 111 + + class ScreenSizeException(Exception): + def __init__ (self, cols, rows): + msg = "TUI requires console screen size of at least {0}x{1}, current is {2}x{3}".format(TrexTUI.MIN_COLS, + TrexTUI.MIN_ROWS, + cols, + rows) + super(TrexTUI.ScreenSizeException, self).__init__(msg) + def __init__ (self, stateless_client): self.stateless_client = stateless_client @@ -472,6 +484,11 @@ class TrexTUI(): def show (self, client, show_log = False, locked = False): + + rows, cols = os.popen('stty size', 'r').read().split() + if (int(rows) < TrexTUI.MIN_ROWS) or (int(cols) < TrexTUI.MIN_COLS): + raise self.ScreenSizeException(rows = rows, cols = cols) + with AsyncKeys(client, locked) as async_keys: sys.stdout.write("\x1bc") self.async_keys = async_keys @@ -691,16 +708,22 @@ class AsyncKeysEngineConsole: self.async = async self.lines = deque(maxlen = 100) - self.ac = {'start' : client.start_line, - 'stop' : client.stop_line, - 'pause' : client.pause_line, - 'resume': client.resume_line, - 'update': client.update_line, - 'quit' : self.action_quit, - 'q' : self.action_quit, - 'exit' : self.action_quit, - 'help' : self.action_help, - '?' : self.action_help} + self.generate_prompt = client.generate_prompt + + self.ac = {'start' : client.start_line, + 'stop' : client.stop_line, + 'pause' : client.pause_line, + 'resume' : client.resume_line, + 'update' : client.update_line, + 'connect' : client.connect_line, + 'disconnect' : client.disconnect_line, + 'acquire' : client.acquire_line, + 'release' : client.release_line, + 'quit' : self.action_quit, + 'q' : self.action_quit, + 'exit' : self.action_quit, + 'help' : self.action_help, + '?' : self.action_help} # fetch readline history and add relevants for i in range(0, readline.get_current_history_length()): @@ -916,6 +939,7 @@ class AsyncKeysEngineConsole: line.invalidate() assert(self.lines[0].modified == False) + color = None if not func: self.last_status = "unknown command: '{0}'".format(format_text(cmd.split()[0], 'bold')) else: @@ -923,12 +947,16 @@ class AsyncKeysEngineConsole: self.last_status = func_rc else: self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') + color = 'red' + # trim too long lines + if ansi_len(self.last_status) > 100: + self.last_status = format_text(self.last_status[:100] + "...", color, 'bold') def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") sys.stdout.write("status: \x1b[0K{0}\n".format(self.last_status)) - sys.stdout.write("\ntui>\x1b[0K") + sys.stdout.write("\n{0}\x1b[0K".format(self.generate_prompt(prefix = 'tui'))) self.lines[self.line_index].draw() 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 7c8a5fbf..a4f26f69 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 @@ -2442,7 +2442,7 @@ class STLClient(object): def ping_line (self, line): '''pings the server''' self.ping() - return True + return RC_OK() @__console def connect_line (self, line): @@ -2454,14 +2454,13 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_all_ports()) - if opts is None: - return + if not opts: + return opts self.connect() self.acquire(ports = opts.ports, force = opts.force) - # true means print time - return True + return RC_OK() @__console @@ -2476,19 +2475,19 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_all_ports()) - if opts is None: - return + if not opts: + return opts # filter out all the already owned ports ports = list_difference(opts.ports, self.get_acquired_ports()) if not ports: - self.logger.log("acquire - all port(s) {0} are already acquired".format(opts.ports)) - return + msg = "acquire - all of port(s) {0} are already acquired".format(opts.ports) + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) self.acquire(ports = ports, force = opts.force) - # true means print time - return True + return RC_OK() # @@ -2502,23 +2501,24 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports()) - if opts is None: - return + if not opts: + return opts ports = list_intersect(opts.ports, self.get_acquired_ports()) if not ports: if not opts.ports: - self.logger.log("release - no acquired ports") - return + msg = "release - no acquired ports" + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) else: - self.logger.log("release - none of port(s) {0} are acquired".format(opts.ports)) - return + msg = "release - none of port(s) {0} are acquired".format(opts.ports) + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) self.release(ports = ports) - # true means print time - return True + return RC_OK() @__console @@ -2530,23 +2530,23 @@ class STLClient(object): self.reacquire_line.__doc__) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts # find all the on-owned ports under your name my_unowned_ports = list_difference([k for k, v in self.ports.items() if v.get_owner() == self.username], self.get_acquired_ports()) if not my_unowned_ports: - self.logger.log("reacquire - no unowned ports under '{0}'".format(self.username)) - return + msg = "reacquire - no unowned ports under '{0}'".format(self.username) + self.logger.log(msg) + return RC_ERR(msg) self.acquire(ports = my_unowned_ports, force = True) - return True + return RC_OK() @__console def disconnect_line (self, line): self.disconnect() - @__console @@ -2559,13 +2559,12 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return + if not opts: + return opts self.reset(ports = opts.ports) - # true means print time - return True + return RC_OK() @@ -2586,8 +2585,8 @@ class STLClient(object): parsing_opts.DRY_RUN) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'start'") + if not opts: + return opts active_ports = list_intersect(self.get_active_ports(), opts.ports) if active_ports: @@ -2663,8 +2662,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'stop'") + if not opts: + return opts # find the relevant ports @@ -2681,7 +2680,6 @@ class STLClient(object): # call API self.stop(ports) - # true means print time return RC_OK() @@ -2697,8 +2695,8 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'update'") + if not opts: + return opts # find the relevant ports @@ -2714,7 +2712,6 @@ class STLClient(object): self.update(ports, opts.mult, opts.total, opts.force) - # true means print time return RC_OK() @@ -2727,8 +2724,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_transmitting_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'pause'") + if not opts: + return opts # check for already paused case if opts.ports and is_sub_list(opts.ports, self.get_paused_ports()): @@ -2749,7 +2746,6 @@ class STLClient(object): self.pause(ports) - # true means print time return RC_OK() @@ -2762,8 +2758,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_paused_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'resume'") + if not opts: + return opts # find the relevant ports ports = list_intersect(opts.ports, self.get_paused_ports()) @@ -2794,8 +2790,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts self.clear_stats(opts.ports) @@ -2814,8 +2810,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts # determine stats mask mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stl_stats.ALL_STATS_OPTS)) @@ -2845,8 +2841,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts streams = self._get_streams(opts.ports, set(opts.streams)) if not streams: @@ -2872,8 +2868,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts self.validate(opts.ports) @@ -2897,8 +2893,8 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts active_ports = list(set(self.get_active_ports()).intersection(opts.ports)) @@ -2906,7 +2902,7 @@ class STLClient(object): if not opts.force: msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports) self.logger.log(format_text(msg, 'bold')) - return + return RC_ERR(msg) else: self.stop(active_ports) @@ -2930,7 +2926,7 @@ class STLClient(object): - return True + return RC_OK() @@ -2945,8 +2941,8 @@ class STLClient(object): parsing_opts.PROMISCUOUS_SWITCH) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return + if not opts: + return opts # if no attributes - fall back to printing the status if opts.prom is None: @@ -2954,7 +2950,7 @@ class STLClient(object): return self.set_port_attr(opts.ports, opts.prom) - + return RC_OK() @__console @@ -2967,8 +2963,8 @@ class STLClient(object): parsing_opts.FILE_PATH) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts info = STLProfile.get_info(opts.file[0]) @@ -3024,8 +3020,8 @@ class STLClient(object): *x) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts ev_type_filter = [] @@ -3046,3 +3042,15 @@ class STLClient(object): if opts.clear: self.clear_events() + def generate_prompt (self, prefix = 'trex'): + if not self.is_connected(): + return "{0}(offline)>".format(prefix) + + elif not self.get_acquired_ports(): + return "{0}(read-only)>".format(prefix) + + elif self.is_all_ports_acquired(): + return "{0}>".format(prefix) + + else: + return "{0} {1}>".format(prefix, self.get_acquired_ports()) 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 caa5aea8..8be154af 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 @@ -1,6 +1,9 @@ import argparse from collections import namedtuple from .common import list_intersect, list_difference +from .text_opts import format_text +from ..trex_stl_types import * + import sys import re import os @@ -400,6 +403,11 @@ class CCmdArgParser(argparse.ArgumentParser): 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)) + raise ValueError(message) + def has_ports_cfg (self, opts): return hasattr(opts, "all_ports") or hasattr(opts, "ports") @@ -407,7 +415,7 @@ class CCmdArgParser(argparse.ArgumentParser): try: opts = super(CCmdArgParser, self).parse_args(args, namespace) if opts is None: - return None + return RC_ERR("'{0}' - invalid arguments".format(self.cmd_name)) if not self.has_ports_cfg(opts): return opts @@ -422,8 +430,9 @@ class CCmdArgParser(argparse.ArgumentParser): # so maybe we have ports configured invalid_ports = list_difference(opts.ports, self.stateless_client.get_all_ports()) if invalid_ports: - self.stateless_client.logger.log("{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)) - return None + msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) # verify acquired ports if verify_acquired: @@ -431,21 +440,25 @@ class CCmdArgParser(argparse.ArgumentParser): diff = list_difference(opts.ports, acquired_ports) if diff: - self.stateless_client.logger.log("{0} - port(s) {1} are not acquired".format(self.cmd_name, diff)) - return None + msg = "{0} - port(s) {1} are not acquired".format(self.cmd_name, diff) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) # no acquire ports at all if not acquired_ports: - self.stateless_client.logger.log("{0} - no acquired ports".format(self.cmd_name)) - return None - + msg = "{0} - no acquired ports".format(self.cmd_name) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) return opts + except ValueError as e: + return RC_ERR("'{0}' - {1}".format(self.cmd_name, str(e))) + except SystemExit: # recover from system exit scenarios, such as "help", or bad arguments. - return None + return RC_ERR("'{0}' - {1}".format(self.cmd_name, "no action")) def get_flags (opt): -- cgit 1.2.3-korg