summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane/stl
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-02-08 10:55:20 -0500
committerimarom <imarom@cisco.com>2016-02-08 10:55:20 -0500
commit6107c1ca4aa485c5971ff3326513b8f4934f7ac1 (patch)
treea109d80501bd087e3219f68c186fb55bc17e090a /scripts/automation/trex_control_plane/stl
parentf5a5e50bfe046148a20f6ce578d6082119dec2c0 (diff)
huge refactor - again
Diffstat (limited to 'scripts/automation/trex_control_plane/stl')
-rw-r--r--scripts/automation/trex_control_plane/stl/console/__init__.py (renamed from scripts/automation/trex_control_plane/stl/__init__.py)0
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_console.py784
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_root_path.py15
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py464
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py114
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_imix.py101
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_path.py4
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_run_udp_simple.py219
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py65
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py0
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/api.py (renamed from scripts/automation/trex_control_plane/stl/api.py)2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_async_client.py)2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_client.py)12
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_exceptions.py)2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py74
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_jsonrpc_client.py)5
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_interface.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_interface.py)0
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_scapy.py)8
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_port.py)0
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_sim.py)28
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_stats.py)4
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_std.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_std.py)0
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_streams.py)3
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py (renamed from scripts/automation/trex_control_plane/stl/trex_stl_types.py)2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/__init__.py0
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py47
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py362
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py192
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_tables.py31
29 files changed, 2519 insertions, 21 deletions
diff --git a/scripts/automation/trex_control_plane/stl/__init__.py b/scripts/automation/trex_control_plane/stl/console/__init__.py
index e69de29b..e69de29b 100644
--- a/scripts/automation/trex_control_plane/stl/__init__.py
+++ b/scripts/automation/trex_control_plane/stl/console/__init__.py
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py
new file mode 100755
index 00000000..789ad4ab
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py
@@ -0,0 +1,784 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Dan Klein, Itay Marom
+Cisco Systems, Inc.
+
+Copyright (c) 2015-2015 Cisco Systems, Inc.
+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.
+"""
+import subprocess
+import cmd
+import json
+import ast
+import argparse
+import random
+import readline
+import string
+import os
+import sys
+import tty, termios
+
+from trex_stl_lib.api import *
+
+from trex_stl_lib.utils.text_opts import *
+from trex_stl_lib.utils.common import user_input, get_current_user
+from trex_stl_lib.utils import parsing_opts
+
+
+import trex_tui
+
+from functools import wraps
+
+__version__ = "1.1"
+
+# console custom logger
+class ConsoleLogger(LoggerApi):
+ def __init__ (self):
+ self.prompt_redraw = None
+
+ def write (self, msg, newline = True):
+ if newline:
+ print msg
+ else:
+ print msg,
+
+ def flush (self):
+ sys.stdout.flush()
+
+ # override this for the prompt fix
+ def async_log (self, msg, level = LoggerApi.VERBOSE_REGULAR, newline = True):
+ self.log(msg, level, newline)
+ if ( (self.level >= LoggerApi.VERBOSE_REGULAR) and self.prompt_redraw ):
+ self.prompt_redraw()
+ self.flush()
+
+
+def set_window_always_on_top (title):
+ # we need the GDK module, if not available - ignroe this command
+ try:
+ import gtk.gdk
+ except ImportError:
+ return
+
+ # search the window and set it as above
+ root = gtk.gdk.get_default_root_window()
+
+ for id in root.property_get('_NET_CLIENT_LIST')[2]:
+ w = gtk.gdk.window_foreign_new(id)
+ if w:
+ name = w.property_get('WM_NAME')[2]
+ if name == title:
+ w.set_keep_above(True)
+ gtk.gdk.window_process_all_updates()
+ break
+
+
+class TRexGeneralCmd(cmd.Cmd):
+ def __init__(self):
+ cmd.Cmd.__init__(self)
+ # configure history behaviour
+ self._history_file_dir = "/tmp/trex/console/"
+ self._history_file = self.get_history_file_full_path()
+ readline.set_history_length(100)
+ # load history, if any
+ self.load_console_history()
+
+
+ def get_console_identifier(self):
+ return self.__class__.__name__
+
+ def get_history_file_full_path(self):
+ return "{dir}{filename}.hist".format(dir=self._history_file_dir,
+ filename=self.get_console_identifier())
+
+ def load_console_history(self):
+ if os.path.exists(self._history_file):
+ readline.read_history_file(self._history_file)
+ return
+
+ def save_console_history(self):
+ if not os.path.exists(self._history_file_dir):
+ # make the directory available for every user
+ try:
+ original_umask = os.umask(0)
+ os.makedirs(self._history_file_dir, mode = 0777)
+ finally:
+ os.umask(original_umask)
+
+
+ # os.mknod(self._history_file)
+ readline.write_history_file(self._history_file)
+ return
+
+ def print_history (self):
+
+ length = readline.get_current_history_length()
+
+ for i in xrange(1, length + 1):
+ cmd = readline.get_history_item(i)
+ print "{:<5} {:}".format(i, cmd)
+
+ def get_history_item (self, index):
+ length = readline.get_current_history_length()
+ if index > length:
+ print format_text("please select an index between {0} and {1}".format(0, length))
+ return None
+
+ return readline.get_history_item(index)
+
+
+ def emptyline(self):
+ """Called when an empty line is entered in response to the prompt.
+
+ This overriding is such that when empty line is passed, **nothing happens**.
+ """
+ return
+
+ def completenames(self, text, *ignored):
+ """
+ This overriding is such that a space is added to name completion.
+ """
+ dotext = 'do_'+text
+ return [a[3:]+' ' for a in self.get_names() if a.startswith(dotext)]
+
+
+#
+# main console object
+class TRexConsole(TRexGeneralCmd):
+ """Trex Console"""
+
+ def __init__(self, stateless_client, verbose = False):
+
+ self.stateless_client = stateless_client
+
+ TRexGeneralCmd.__init__(self)
+
+ self.tui = trex_tui.TrexTUI(stateless_client)
+ self.terminal = None
+
+ self.verbose = verbose
+
+ self.intro = "\n-=TRex Console v{ver}=-\n".format(ver=__version__)
+ self.intro += "\nType 'help' or '?' for supported actions\n"
+
+ self.postcmd(False, "")
+
+
+ ################### internal section ########################
+
+ def prompt_redraw (self):
+ self.postcmd(False, "")
+ sys.stdout.write("\n" + self.prompt + readline.get_line_buffer())
+ sys.stdout.flush()
+
+
+ def verify_connected(f):
+ @wraps(f)
+ def wrap(*args):
+ inst = args[0]
+ func_name = f.__name__
+ if func_name.startswith("do_"):
+ func_name = func_name[3:]
+
+ if not inst.stateless_client.is_connected():
+ print format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold')
+ return
+
+ ret = f(*args)
+ return ret
+
+ return wrap
+
+ # TODO: remove this ugly duplication
+ def verify_connected_and_rw (f):
+ @wraps(f)
+ def wrap(*args):
+ inst = args[0]
+ func_name = f.__name__
+ if func_name.startswith("do_"):
+ func_name = func_name[3:]
+
+ if not inst.stateless_client.is_connected():
+ print format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold')
+ return
+
+ if inst.stateless_client.is_all_ports_acquired():
+ print format_text("\n'{0}' cannot be executed on read only mode\n".format(func_name), 'bold')
+ return
+
+ rc = f(*args)
+ return rc
+
+ return wrap
+
+
+ def get_console_identifier(self):
+ return "{context}_{server}".format(context=get_current_user(),
+ server=self.stateless_client.get_connection_info()['server'])
+
+ def register_main_console_methods(self):
+ main_names = set(self.trex_console.get_names()).difference(set(dir(self.__class__)))
+ for name in main_names:
+ for prefix in 'do_', 'help_', 'complete_':
+ if name.startswith(prefix):
+ self.__dict__[name] = getattr(self.trex_console, name)
+
+ def precmd(self, line):
+ # before doing anything, save history snapshot of the console
+ # this is done before executing the command in case of ungraceful application exit
+ self.save_console_history()
+
+ lines = line.split(';')
+
+ for line in lines:
+ stop = self.onecmd(line)
+ stop = self.postcmd(stop, line)
+ if stop:
+ return "quit"
+
+ return ""
+
+
+ def postcmd(self, stop, line):
+
+ if not self.stateless_client.is_connected():
+ self.prompt = "TRex (offline) > "
+ self.supported_rpc = None
+ return stop
+
+ if self.stateless_client.is_all_ports_acquired():
+ self.prompt = "TRex (read only) > "
+ return stop
+
+
+ self.prompt = "TRex > "
+
+ return stop
+
+ def default(self, line):
+ print "'{0}' is an unrecognized command. type 'help' or '?' for a list\n".format(line)
+
+ @staticmethod
+ def tree_autocomplete(text):
+ dir = os.path.dirname(text)
+ if dir:
+ path = dir
+ else:
+ path = "."
+
+
+ start_string = os.path.basename(text)
+
+ targets = []
+
+ for x in os.listdir(path):
+ if x.startswith(start_string):
+ y = os.path.join(path, x)
+ if os.path.isfile(y):
+ targets.append(x + ' ')
+ elif os.path.isdir(y):
+ targets.append(x + '/')
+
+ return targets
+
+
+ ####################### shell commands #######################
+ @verify_connected
+ def do_ping (self, line):
+ '''Ping the server\n'''
+ self.stateless_client.ping()
+
+
+ # set verbose on / off
+ def do_verbose(self, line):
+ '''Shows or set verbose mode\n'''
+ if line == "":
+ print "\nverbose is " + ("on\n" if self.verbose else "off\n")
+
+ elif line == "on":
+ self.verbose = True
+ self.stateless_client.set_verbose("high")
+ print format_text("\nverbose set to on\n", 'green', 'bold')
+
+ elif line == "off":
+ self.verbose = False
+ self.stateless_client.set_verbose("normal")
+ print format_text("\nverbose set to off\n", 'green', 'bold')
+
+ else:
+ print format_text("\nplease specify 'on' or 'off'\n", 'bold')
+
+ # show history
+ def help_history (self):
+ self.do_history("-h")
+
+ def do_shell (self, line):
+ return self.do_history(line)
+
+
+ def do_history (self, line):
+ '''Manage the command history\n'''
+
+ item = parsing_opts.ArgumentPack(['item'],
+ {"nargs": '?',
+ 'metavar': 'item',
+ 'type': parsing_opts.check_negative,
+ 'help': "an history item index",
+ 'default': 0})
+
+ parser = parsing_opts.gen_parser(self,
+ "history",
+ self.do_history.__doc__,
+ item)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ if opts.item == 0:
+ self.print_history()
+ else:
+ cmd = self.get_history_item(opts.item)
+ if cmd == None:
+ return
+
+ print "Executing '{0}'".format(cmd)
+
+ return self.onecmd(cmd)
+
+
+
+ ############### connect
+ def do_connect (self, line):
+ '''Connects to the server\n'''
+
+ self.stateless_client.connect_line(line)
+
+
+ def do_disconnect (self, line):
+ '''Disconnect from the server\n'''
+
+ self.stateless_client.disconnect_line(line)
+
+
+ ############### start
+
+ def complete_start(self, text, line, begidx, endidx):
+ s = line.split()
+ l = len(s)
+
+ file_flags = parsing_opts.get_flags(parsing_opts.FILE_PATH)
+
+ if (l > 1) and (s[l - 1] in file_flags):
+ return TRexConsole.tree_autocomplete("")
+
+ if (l > 2) and (s[l - 2] in file_flags):
+ return TRexConsole.tree_autocomplete(s[l - 1])
+
+ @verify_connected_and_rw
+ def do_start(self, line):
+ '''Start selected traffic in specified port(s) on TRex\n'''
+
+ self.stateless_client.start_line(line)
+
+
+
+
+ def help_start(self):
+ self.do_start("-h")
+
+ ############# stop
+ @verify_connected_and_rw
+ def do_stop(self, line):
+ '''stops port(s) transmitting traffic\n'''
+
+ self.stateless_client.stop_line(line)
+
+ def help_stop(self):
+ self.do_stop("-h")
+
+ ############# update
+ @verify_connected_and_rw
+ def do_update(self, line):
+ '''update speed of port(s)currently transmitting traffic\n'''
+
+ self.stateless_client.update_line(line)
+
+ def help_update (self):
+ self.do_update("-h")
+
+ ############# pause
+ @verify_connected_and_rw
+ def do_pause(self, line):
+ '''pause port(s) transmitting traffic\n'''
+
+ self.stateless_client.pause_line(line)
+
+ ############# resume
+ @verify_connected_and_rw
+ def do_resume(self, line):
+ '''resume port(s) transmitting traffic\n'''
+
+ self.stateless_client.resume_line(line)
+
+
+
+ ########## reset
+ @verify_connected_and_rw
+ def do_reset (self, line):
+ '''force stop all ports\n'''
+ self.stateless_client.reset_line(line)
+
+
+ ######### validate
+ @verify_connected
+ def do_validate (self, line):
+ '''validates port(s) stream configuration\n'''
+
+ self.stateless_client.validate_line(line)
+
+
+ @verify_connected
+ def do_stats(self, line):
+ '''Fetch statistics from TRex server by port\n'''
+ self.stateless_client.show_stats_line(line)
+
+
+ def help_stats(self):
+ self.do_stats("-h")
+
+ @verify_connected
+ def do_streams(self, line):
+ '''Fetch statistics from TRex server by port\n'''
+ self.stateless_client.show_streams_line(line)
+
+
+ def help_streams(self):
+ self.do_streams("-h")
+
+ @verify_connected
+ def do_clear(self, line):
+ '''Clear cached local statistics\n'''
+ self.stateless_client.clear_stats_line(line)
+
+
+ def help_clear(self):
+ self.do_clear("-h")
+
+
+ def help_events (self):
+ self.do_events("-h")
+
+ def do_events (self, line):
+ '''shows events recieved from server\n'''
+
+ x = parsing_opts.ArgumentPack(['-c','--clear'],
+ {'action' : "store_true",
+ 'default': False,
+ 'help': "clear the events log"})
+
+ parser = parsing_opts.gen_parser(self,
+ "events",
+ self.do_events.__doc__,
+ x)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ events = self.stateless_client.get_events()
+ for ev in events:
+ print ev
+
+ if opts.clear:
+ self.stateless_client.clear_events()
+ print format_text("\n\nEvent log was cleared\n\n")
+
+
+ # tui
+ @verify_connected
+ def do_tui (self, line):
+ '''Shows a graphical console\n'''
+
+ parser = parsing_opts.gen_parser(self,
+ "tui",
+ self.do_tui.__doc__,
+ parsing_opts.XTERM)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ if opts.xterm:
+
+ 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 = ['xterm', '-geometry', '111x42', '-sl', '0', '-title', 'trex_tui', '-e', exe]
+ self.terminal = subprocess.Popen(cmd)
+
+ return
+
+
+ with self.stateless_client.logger.supress():
+ self.tui.show()
+
+
+ def help_tui (self):
+ do_tui("-h")
+
+
+ # quit function
+ def do_quit(self, line):
+ '''Exit the client\n'''
+ return True
+
+
+ def do_help (self, line):
+ '''Shows This Help Screen\n'''
+ if line:
+ try:
+ func = getattr(self, 'help_' + line)
+ except AttributeError:
+ try:
+ doc = getattr(self, 'do_' + line).__doc__
+ if doc:
+ self.stdout.write("%s\n"%str(doc))
+ return
+ except AttributeError:
+ pass
+ self.stdout.write("%s\n"%str(self.nohelp % (line,)))
+ return
+ func()
+ return
+
+ print "\nSupported Console Commands:"
+ print "----------------------------\n"
+
+ cmds = [x[3:] for x in self.get_names() if x.startswith("do_")]
+ for cmd in cmds:
+ if ( (cmd == "EOF") or (cmd == "q") or (cmd == "exit") or (cmd == "h")):
+ continue
+
+ try:
+ doc = getattr(self, 'do_' + cmd).__doc__
+ if doc:
+ help = str(doc)
+ else:
+ help = "*** Undocumented Function ***\n"
+ except AttributeError:
+ help = "*** Undocumented Function ***\n"
+
+ print "{:<30} {:<30}".format(cmd + " - ", help)
+
+ # a custorm cmdloop wrapper
+ def start(self):
+ while True:
+ try:
+ self.cmdloop()
+ break
+ except KeyboardInterrupt as e:
+ if not readline.get_line_buffer():
+ raise KeyboardInterrupt
+ else:
+ print ""
+ self.intro = None
+ continue
+
+ if self.terminal:
+ self.terminal.kill()
+
+ # aliases
+ do_exit = do_EOF = do_q = do_quit
+ do_h = do_history
+
+
+# run a script of commands
+def run_script_file (self, filename, stateless_client):
+
+ self.logger.log(format_text("\nRunning script file '{0}'...".format(filename), 'bold'))
+
+ with open(filename) as f:
+ script_lines = f.readlines()
+
+ cmd_table = {}
+
+ # register all the commands
+ cmd_table['start'] = stateless_client.start_line
+ cmd_table['stop'] = stateless_client.stop_line
+ cmd_table['reset'] = stateless_client.reset_line
+
+ for index, line in enumerate(script_lines, start = 1):
+ line = line.strip()
+ if line == "":
+ continue
+ if line.startswith("#"):
+ continue
+
+ sp = line.split(' ', 1)
+ cmd = sp[0]
+ if len(sp) == 2:
+ args = sp[1]
+ else:
+ args = ""
+
+ stateless_client.logger.log(format_text("Executing line {0} : '{1}'\n".format(index, line)))
+
+ if not cmd in cmd_table:
+ print "\n*** Error at line {0} : '{1}'\n".format(index, line)
+ stateless_client.logger.log(format_text("unknown command '{0}'\n".format(cmd), 'bold'))
+ return False
+
+ cmd_table[cmd](args)
+
+ stateless_client.logger.log(format_text("\n[Done]", 'bold'))
+
+ return True
+
+
+#
+def is_valid_file(filename):
+ if not os.path.isfile(filename):
+ raise argparse.ArgumentTypeError("The file '%s' does not exist" % filename)
+
+ return filename
+
+
+
+def setParserOptions():
+ parser = argparse.ArgumentParser(prog="trex_console.py")
+
+ parser.add_argument("-s", "--server", help = "TRex Server [default is localhost]",
+ default = "localhost",
+ type = str)
+
+ parser.add_argument("-p", "--port", help = "TRex Server Port [default is 4501]\n",
+ default = 4501,
+ type = int)
+
+ parser.add_argument("--async_port", help = "TRex ASync Publisher Port [default is 4500]\n",
+ default = 4500,
+ dest='pub',
+ type = int)
+
+ parser.add_argument("-u", "--user", help = "User Name [default is currently logged in user]\n",
+ default = get_current_user(),
+ type = str)
+
+ parser.add_argument("-v", "--verbose", dest="verbose",
+ action="store_true", help="Switch ON verbose option. Default is: OFF.",
+ default = False)
+
+
+ parser.add_argument("--no_acquire", dest="acquire",
+ action="store_false", help="Acquire all ports on connect. Default is: ON.",
+ default = True)
+
+ parser.add_argument("--batch", dest="batch",
+ nargs = 1,
+ type = is_valid_file,
+ help = "Run the console in a batch mode with file",
+ default = None)
+
+ parser.add_argument("-t", "--tui", dest="tui",
+ action="store_true", help="Starts with TUI mode",
+ default = False)
+
+ parser.add_argument("-x", "--xtui", dest="xtui",
+ action="store_true", help="Starts with XTERM TUI mode",
+ default = False)
+
+ parser.add_argument("--top", dest="top",
+ action="store_true", help="Set the window as always on top",
+ default = False)
+
+ parser.add_argument("-q", "--quiet", dest="quiet",
+ action="store_true", help="Starts with all outputs suppressed",
+ default = False)
+
+ return parser
+
+
+def main():
+ parser = setParserOptions()
+ options = parser.parse_args()
+
+ if options.xtui:
+ options.tui = True
+
+ # always on top
+ if options.top:
+ set_window_always_on_top('trex_tui')
+
+
+ # Stateless client connection
+ if options.quiet:
+ verbose_level = LoggerApi.VERBOSE_QUIET
+ elif options.verbose:
+ verbose_level = LoggerApi.VERBOSE_HIGH
+ else:
+ verbose_level = LoggerApi.VERBOSE_REGULAR
+
+ # Stateless client connection
+ logger = ConsoleLogger()
+ stateless_client = STLClient(username = options.user,
+ server = options.server,
+ sync_port = options.port,
+ async_port = options.pub,
+ verbose_level = verbose_level,
+ logger = logger)
+
+ # TUI or no acquire will give us READ ONLY mode
+ try:
+ stateless_client.connect()
+ except STLError as e:
+ logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
+ return
+
+ if not options.tui and options.acquire:
+ try:
+ # acquire all ports
+ stateless_client.acquire()
+ except STLError as e:
+ logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
+ logger.log(format_text("\nSwitching to read only mode - only few commands will be available", 'bold'))
+
+
+ # a script mode
+ if options.batch:
+ cont = run_script_file(options.batch[0], stateless_client)
+ if not cont:
+ return
+
+ # console
+ try:
+ console = TRexConsole(stateless_client, options.verbose)
+ logger.prompt_redraw = console.prompt_redraw
+
+ # TUI
+ if options.tui:
+ console.do_tui("-x" if options.xtui else "")
+ else:
+ console.start()
+
+ except KeyboardInterrupt as e:
+ print "\n\n*** Caught Ctrl + C... Exiting...\n\n"
+
+ finally:
+ with stateless_client.logger.supress():
+ stateless_client.disconnect(stop_traffic = False)
+
+if __name__ == '__main__':
+
+ main()
+
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_root_path.py b/scripts/automation/trex_control_plane/stl/console/trex_root_path.py
new file mode 100755
index 00000000..de4ec03b
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/console/trex_root_path.py
@@ -0,0 +1,15 @@
+#!/router/bin/python
+
+import os
+import sys
+
+def add_root_to_path ():
+ """adds trex_control_plane root dir to script path, up to `depth` parent dirs"""
+ root_dirname = 'trex_control_plane'
+ file_path = os.path.dirname(os.path.realpath(__file__))
+
+ components = file_path.split(os.sep)
+ sys.path.append( str.join(os.sep, components[:components.index(root_dirname)+1]) )
+ return
+
+add_root_to_path()
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
new file mode 100644
index 00000000..f972b905
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
@@ -0,0 +1,464 @@
+import termios
+import sys
+import os
+import time
+from collections import OrderedDict
+import datetime
+from cStringIO import StringIO
+
+from trex_stl_lib.utils.text_opts import *
+from trex_stl_lib.utils import text_tables
+
+# for STL exceptions
+from trex_stl_lib.api import *
+
+class SimpleBar(object):
+ def __init__ (self, desc, pattern):
+ self.desc = desc
+ self.pattern = pattern
+ self.pattern_len = len(pattern)
+ self.index = 0
+
+ def show (self):
+ if self.desc:
+ print format_text("{0} {1}".format(self.desc, self.pattern[self.index]), 'bold')
+ else:
+ print format_text("{0}".format(self.pattern[self.index]), 'bold')
+
+ self.index = (self.index + 1) % self.pattern_len
+
+
+# base type of a panel
+class TrexTUIPanel(object):
+ def __init__ (self, mng, name):
+
+ self.mng = mng
+ self.name = name
+ self.stateless_client = mng.stateless_client
+
+ def show (self):
+ raise NotImplementedError("must implement this")
+
+ def get_key_actions (self):
+ raise NotImplementedError("must implement this")
+
+ def get_name (self):
+ return self.name
+
+
+# dashboard panel
+class TrexTUIDashBoard(TrexTUIPanel):
+ def __init__ (self, mng):
+ super(TrexTUIDashBoard, self).__init__(mng, "dashboard")
+
+ self.key_actions = OrderedDict()
+
+ self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True}
+ self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True}
+ self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True}
+ self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True}
+ self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True}
+
+ self.ports = self.stateless_client.get_all_ports()
+
+
+ def show (self):
+ stats = self.stateless_client._get_formatted_stats(self.ports)
+ # print stats to screen
+ for stat_type, stat_data in stats.iteritems():
+ text_tables.print_table_with_header(stat_data.text_table, stat_type)
+
+
+ def get_key_actions (self):
+ allowed = {}
+
+ allowed['c'] = self.key_actions['c']
+
+ if self.stateless_client.is_all_ports_acquired():
+ return allowed
+
+ if len(self.stateless_client.get_transmitting_ports()) > 0:
+ allowed['p'] = self.key_actions['p']
+ allowed['+'] = self.key_actions['+']
+ allowed['-'] = self.key_actions['-']
+
+
+ if len(self.stateless_client.get_paused_ports()) > 0:
+ allowed['r'] = self.key_actions['r']
+
+ return allowed
+
+
+ ######### actions
+ def action_pause (self):
+ try:
+ rc = self.stateless_client.pause(ports = self.mng.ports)
+ except STLError:
+ pass
+
+ return ""
+
+
+
+ def action_resume (self):
+ try:
+ self.stateless_client.resume(ports = self.mng.ports)
+ except STLError:
+ pass
+
+ return ""
+
+
+ def action_raise (self):
+ try:
+ self.stateless_client.update(mult = "5%+", ports = self.mng.ports)
+ except STLError:
+ pass
+
+ return ""
+
+
+ def action_lower (self):
+ try:
+ self.stateless_client.update(mult = "5%-", ports = self.mng.ports)
+ except STLError:
+ pass
+
+ return ""
+
+
+ def action_clear (self):
+ self.stateless_client.clear_stats(self.mng.ports)
+ return "cleared all stats"
+
+
+# port panel
+class TrexTUIPort(TrexTUIPanel):
+ def __init__ (self, mng, port_id):
+ super(TrexTUIPort, self).__init__(mng, "port {0}".format(port_id))
+
+ self.port_id = port_id
+ self.port = self.mng.stateless_client.get_port(port_id)
+
+ self.key_actions = OrderedDict()
+
+ self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True}
+ self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True}
+ self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True}
+ self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True}
+ self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True}
+
+
+ def show (self):
+ stats = self.stateless_client._get_formatted_stats([self.port_id])
+ # print stats to screen
+ for stat_type, stat_data in stats.iteritems():
+ text_tables.print_table_with_header(stat_data.text_table, stat_type)
+
+ def get_key_actions (self):
+
+ allowed = {}
+
+ allowed['c'] = self.key_actions['c']
+
+ if self.stateless_client.is_all_ports_acquired():
+ return allowed
+
+ if self.port.state == self.port.STATE_TX:
+ allowed['p'] = self.key_actions['p']
+ allowed['+'] = self.key_actions['+']
+ allowed['-'] = self.key_actions['-']
+
+ elif self.port.state == self.port.STATE_PAUSE:
+ allowed['r'] = self.key_actions['r']
+
+
+ return allowed
+
+ # actions
+ def action_pause (self):
+ try:
+ self.stateless_client.pause(ports = [self.port_id])
+ except STLError:
+ pass
+
+ return ""
+
+ def action_resume (self):
+ try:
+ self.stateless_client.resume(ports = [self.port_id])
+ except STLError:
+ pass
+
+ return ""
+
+
+ def action_raise (self):
+ mult = {'type': 'percentage', 'value': 5, 'op': 'add'}
+
+ try:
+ self.stateless_client.update(mult = mult, ports = [self.port_id])
+ except STLError:
+ pass
+
+ return ""
+
+ def action_lower (self):
+ mult = {'type': 'percentage', 'value': 5, 'op': 'sub'}
+
+ try:
+ self.stateless_client.update(mult = mult, ports = [self.port_id])
+ except STLError:
+ pass
+
+ return ""
+
+ def action_clear (self):
+ self.stateless_client.clear_stats([self.port_id])
+ return "port {0}: cleared stats".format(self.port_id)
+
+# log
+class TrexTUILog():
+ def __init__ (self):
+ self.log = []
+
+ def add_event (self, msg):
+ self.log.append("[{0}] {1}".format(str(datetime.datetime.now().time()), msg))
+
+ def show (self, max_lines = 4):
+
+ cut = len(self.log) - max_lines
+ if cut < 0:
+ cut = 0
+
+ print format_text("\nLog:", 'bold', 'underline')
+
+ for msg in self.log[cut:]:
+ print msg
+
+
+# Panels manager (contains server panels)
+class TrexTUIPanelManager():
+ def __init__ (self, tui):
+ self.tui = tui
+ self.stateless_client = tui.stateless_client
+ self.ports = self.stateless_client.get_all_ports()
+
+
+ self.panels = {}
+ self.panels['dashboard'] = TrexTUIDashBoard(self)
+
+ self.key_actions = OrderedDict()
+ self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True}
+ self.key_actions['g'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True}
+
+ for port_id in self.ports:
+ self.key_actions[str(port_id)] = {'action': self.action_show_port(port_id), 'legend': 'port {0}'.format(port_id), 'show': False}
+ self.panels['port {0}'.format(port_id)] = TrexTUIPort(self, port_id)
+
+ # start with dashboard
+ self.main_panel = self.panels['dashboard']
+
+ # log object
+ self.log = TrexTUILog()
+
+ self.generate_legend()
+
+ self.conn_bar = SimpleBar('status: ', ['|','/','-','\\'])
+ self.dis_bar = SimpleBar('status: ', ['X', ' '])
+ self.show_log = False
+
+
+ def generate_legend (self):
+ self.legend = "\n{:<12}".format("browse:")
+
+ for k, v in self.key_actions.iteritems():
+ if v['show']:
+ x = "'{0}' - {1}, ".format(k, v['legend'])
+ self.legend += "{:}".format(x)
+
+ self.legend += "'0-{0}' - port display".format(len(self.ports) - 1)
+
+
+ self.legend += "\n{:<12}".format(self.main_panel.get_name() + ":")
+ for k, v in self.main_panel.get_key_actions().iteritems():
+ if v['show']:
+ x = "'{0}' - {1}, ".format(k, v['legend'])
+ self.legend += "{:}".format(x)
+
+
+ def print_connection_status (self):
+ if self.tui.get_state() == self.tui.STATE_ACTIVE:
+ self.conn_bar.show()
+ else:
+ self.dis_bar.show()
+
+ def print_legend (self):
+ print format_text(self.legend, 'bold')
+
+
+ # on window switch or turn on / off of the TUI we call this
+ def init (self, show_log = False):
+ self.show_log = show_log
+ self.generate_legend()
+
+ def show (self):
+ self.main_panel.show()
+ self.print_connection_status()
+ self.print_legend()
+
+ if self.show_log:
+ self.log.show()
+
+
+ def handle_key (self, ch):
+ # check for the manager registered actions
+ if ch in self.key_actions:
+ msg = self.key_actions[ch]['action']()
+
+ # check for main panel actions
+ elif ch in self.main_panel.get_key_actions():
+ msg = self.main_panel.get_key_actions()[ch]['action']()
+
+ else:
+ msg = ""
+
+ self.generate_legend()
+
+ if msg == None:
+ return False
+ else:
+ if msg:
+ self.log.add_event(msg)
+ return True
+
+
+ # actions
+
+ def action_quit (self):
+ return None
+
+ def action_show_dash (self):
+ self.main_panel = self.panels['dashboard']
+ self.init(self.show_log)
+ return ""
+
+ def action_show_port (self, port_id):
+ def action_show_port_x ():
+ self.main_panel = self.panels['port {0}'.format(port_id)]
+ self.init()
+ return ""
+
+ return action_show_port_x
+
+
+
+# shows a textual top style window
+class TrexTUI():
+
+ STATE_ACTIVE = 0
+ STATE_LOST_CONT = 1
+ STATE_RECONNECT = 2
+
+ def __init__ (self, stateless_client):
+ self.stateless_client = stateless_client
+
+ self.pm = TrexTUIPanelManager(self)
+
+
+
+ def handle_key_input (self):
+ # try to read a single key
+ ch = os.read(sys.stdin.fileno(), 1)
+ if ch != None and len(ch) > 0:
+ return (self.pm.handle_key(ch), True)
+
+ else:
+ return (True, False)
+
+
+ def clear_screen (self):
+ #os.system('clear')
+ # maybe this is faster ?
+ sys.stdout.write("\x1b[2J\x1b[H")
+
+
+
+ def show (self, show_log = False):
+ # init termios
+ old_settings = termios.tcgetattr(sys.stdin)
+ new_settings = termios.tcgetattr(sys.stdin)
+ new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags
+ new_settings[6][termios.VMIN] = 0 # cc
+ new_settings[6][termios.VTIME] = 0 # cc
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings)
+
+ self.pm.init(show_log)
+
+ self.state = self.STATE_ACTIVE
+ self.draw_policer = 0
+
+ try:
+ while True:
+ # draw and handle user input
+ cont, force_draw = self.handle_key_input()
+ self.draw_screen(force_draw)
+ if not cont:
+ break
+ time.sleep(0.1)
+
+ # regular state
+ if self.state == self.STATE_ACTIVE:
+ # if no connectivity - move to lost connecitivty
+ if not self.stateless_client.async_client.is_alive():
+ self.stateless_client._invalidate_stats(self.pm.ports)
+ self.state = self.STATE_LOST_CONT
+
+
+ # lost connectivity
+ elif self.state == self.STATE_LOST_CONT:
+ # got it back
+ if self.stateless_client.async_client.is_alive():
+ # move to state reconnect
+ self.state = self.STATE_RECONNECT
+
+
+ # restored connectivity - try to reconnect
+ elif self.state == self.STATE_RECONNECT:
+
+ try:
+ self.stateless_client.connect("RO")
+ self.state = self.STATE_ACTIVE
+ except STLError:
+ self.state = self.STATE_LOST_CONT
+
+
+ finally:
+ # restore
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
+
+ print ""
+
+
+ # draw once
+ def draw_screen (self, force_draw = False):
+
+ if (self.draw_policer >= 5) or (force_draw):
+
+ # capture stdout to a string
+ old_stdout = sys.stdout
+ sys.stdout = mystdout = StringIO()
+ self.pm.show()
+ sys.stdout = old_stdout
+
+ self.clear_screen()
+ print mystdout.getvalue()
+ sys.stdout.flush()
+
+ self.draw_policer = 0
+ else:
+ self.draw_policer += 1
+
+ def get_state (self):
+ return self.state
+
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py b/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py
new file mode 100644
index 00000000..46b84c6e
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_bi_dir_flows.py
@@ -0,0 +1,114 @@
+import stl_path
+from trex_control_plane.stl.api import *
+
+import time
+import json
+
+# simple packet creation
+def create_pkt (size, direction):
+
+ ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"},
+ 'dst': {'start': "8.0.0.1", 'end': "8.0.0.254"}}
+
+ if (direction == 0):
+ src = ip_range['src']
+ dst = ip_range['dst']
+ else:
+ src = ip_range['dst']
+ dst = ip_range['src']
+
+ vm = [
+ # src
+ STLVmFlowVar(name="src",min_value=src['start'],max_value=src['end'],size=4,op="inc"),
+ STLVmWriteFlowVar(fv_name="src",pkt_offset= "IP.src"),
+
+ # dst
+ STLVmFlowVar(name="dst",min_value=dst['start'],max_value=dst['end'],size=4,op="inc"),
+ STLVmWriteFlowVar(fv_name="dst",pkt_offset= "IP.dst"),
+
+ # checksum
+ STLVmFixIpv4(offset = "IP")
+ ]
+
+
+ base = Ether()/IP()/UDP()
+ pad = max(0, len(base)) * 'x'
+
+ return STLPktBuilder(pkt = base/pad,
+ vm = vm)
+
+
+def simple_burst ():
+
+
+ # create client
+ c = STLClient()
+ passed = True
+
+ try:
+ # turn this on for some information
+ #c.set_verbose("high")
+
+ # create two streams
+ s1 = STLStream(packet = create_pkt(200, 0),
+ mode = STLTXCont(pps = 100))
+
+ # second stream with a phase of 1ms (inter stream gap)
+ s2 = STLStream(packet = create_pkt(200, 1),
+ isg = 1000,
+ mode = STLTXCont(pps = 100))
+
+
+ # connect to server
+ c.connect()
+
+ # prepare our ports (my machine has 0 <--> 1 with static route)
+ c.reset(ports = [0, 1])
+
+ # add both streams to ports
+ c.add_streams(s1, ports = [0])
+ c.add_streams(s2, ports = [1])
+
+ # clear the stats before injecting
+ c.clear_stats()
+
+ # choose rate and start traffic for 10 seconds on 5 mpps
+ print "Running 5 Mpps on ports 0, 1 for 10 seconds..."
+ c.start(ports = [0, 1], mult = "5mpps", duration = 10)
+
+ # block until done
+ c.wait_on_traffic(ports = [0, 1])
+
+ # read the stats after the test
+ stats = c.get_stats()
+
+ print json.dumps(stats[0], indent = 4, separators=(',', ': '), sort_keys = True)
+ print json.dumps(stats[1], indent = 4, separators=(',', ': '), sort_keys = True)
+
+ lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
+ lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
+
+ print "\npackets lost from 0 --> 1: {0} pkts".format(lost_a)
+ print "packets lost from 1 --> 0: {0} pkts".format(lost_b)
+
+ if (lost_a == 0) and (lost_b == 0):
+ passed = True
+ else:
+ passed = False
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nTest has passed :-)\n"
+ else:
+ print "\nTest has failed :-(\n"
+
+
+# run the tests
+simple_burst()
+
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py
new file mode 100644
index 00000000..c083a207
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py
@@ -0,0 +1,101 @@
+import stl_path
+from trex_control_plane.stl.api import *
+
+import time
+import json
+from pprint import pprint
+
+# IMIX test
+# it maps the ports to sides
+# then it load a predefind profile 'IMIX'
+# and attach it to both sides and inject
+# at a certain rate for some time
+# finally it checks that all packets arrived
+def imix_test ():
+
+
+ # create client
+ c = STLClient()
+ passed = True
+
+
+ try:
+
+ # connect to server
+ c.connect()
+
+ # take all the ports
+ c.reset()
+
+ # map ports - identify the routes
+ table = stl_map_ports(c)
+
+ print "Mapped ports to sides {0} <--> {1}".format(table['dir'][0], table['dir'][1])
+ dir_0 = table['dir'][0]
+ dir_1 = table['dir'][1]
+
+ # load IMIX profile
+ streams = c.load_profile('../../../stl/profiles/imix.py')
+
+ # add both streams to ports
+ c.add_streams(streams, ports = dir_0)
+ c.add_streams(streams, ports = dir_1)
+
+ # clear the stats before injecting
+ c.clear_stats()
+
+ # choose rate and start traffic for 10 seconds on 5 mpps
+ duration = 10
+ mult = "5mpps"
+ print "Injecting {0} <--> {1} on total rate of '{2}' for {3} seconds".format(dir_0, dir_1, mult, duration)
+
+ c.start(ports = (dir_0 + dir_1), mult = mult, duration = duration, total = True)
+
+ # block until done
+ c.wait_on_traffic(ports = (dir_0 + dir_1))
+
+ # read the stats after the test
+ stats = c.get_stats()
+
+ # use this for debug info on all the stats
+ #pprint(stats)
+
+ # sum dir 0
+ dir_0_opackets = sum([stats[i]["opackets"] for i in dir_0])
+ dir_0_ipackets = sum([stats[i]["ipackets"] for i in dir_0])
+
+ # sum dir 1
+ dir_1_opackets = sum([stats[i]["opackets"] for i in dir_1])
+ dir_1_ipackets = sum([stats[i]["ipackets"] for i in dir_1])
+
+
+ lost_0 = dir_0_opackets - dir_1_ipackets
+ lost_1 = dir_1_opackets - dir_0_ipackets
+
+ print "\nPackets injected from {0}: {1:,}".format(dir_0, dir_0_opackets)
+ print "Packets injected from {0}: {1:,}".format(dir_1, dir_1_opackets)
+
+ print "\npackets lost from {0} --> {1}: {2:,} pkts".format(dir_0, dir_0, lost_0)
+ print "packets lost from {0} --> {1}: {2:,} pkts".format(dir_0, dir_0, lost_0)
+
+ if (lost_0 == 0) and (lost_0 == 0):
+ passed = True
+ else:
+ passed = False
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nTest has passed :-)\n"
+ else:
+ print "\nTest has failed :-(\n"
+
+
+# run the tests
+imix_test()
+
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_path.py b/scripts/automation/trex_control_plane/stl/examples/stl_path.py
new file mode 100644
index 00000000..a5b8102b
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_path.py
@@ -0,0 +1,4 @@
+import sys
+
+# FIXME to the write path for trex_control_plane
+sys.path.insert(0, "../../../automation")
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_run_udp_simple.py b/scripts/automation/trex_control_plane/stl/examples/stl_run_udp_simple.py
new file mode 100644
index 00000000..47db1b5a
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_run_udp_simple.py
@@ -0,0 +1,219 @@
+#!/usr/bin/python
+import sys, getopt
+import argparse;
+"""
+Sample API application,
+Connect to TRex
+Send UDP packet in specific length
+Each direction has its own IP range
+Compare Rx-pkts to TX-pkts assuming ports are loopback
+
+"""
+
+import stl_path
+from trex_control_plane.stl.api import *
+
+H_VER = "trex-x v0.1 "
+
+class t_global(object):
+ args=None;
+
+
+import dpkt
+import time
+import json
+import string
+
+def generate_payload(length):
+ word = ''
+ alphabet_size = len(string.letters)
+ for i in range(length):
+ word += string.letters[(i % alphabet_size)]
+ return word
+
+# simple packet creation
+def create_pkt (frame_size = 9000, direction=0):
+
+ ip_range = {'src': {'start': "10.0.0.1", 'end': "10.0.0.254"},
+ 'dst': {'start': "8.0.0.1", 'end': "8.0.0.254"}}
+
+ if (direction == 0):
+ src = ip_range['src']
+ dst = ip_range['dst']
+ else:
+ src = ip_range['dst']
+ dst = ip_range['src']
+
+ vm = [
+ # src
+ STLVmFlowVar(name="src",min_value=src['start'],max_value=src['end'],size=4,op="inc"),
+ STLVmWriteFlowVar(fv_name="src",pkt_offset= "IP.src"),
+
+ # dst
+ STLVmFlowVar(name="dst",min_value=dst['start'],max_value=dst['end'],size=4,op="inc"),
+ STLVmWriteFlowVar(fv_name="dst",pkt_offset= "IP.dst"),
+
+ # checksum
+ STLVmFixIpv4(offset = "IP")
+ ]
+
+ pkt_base = Ether(src="00:00:00:00:00:01",dst="00:00:00:00:00:02")/IP()/UDP(dport=12,sport=1025)
+ pyld_size = frame_size - len(pkt_base);
+ pkt_pyld = generate_payload(pyld_size)
+
+ return STLPktBuilder(pkt = pkt_base/pkt_pyld,
+ vm = vm)
+
+
+def simple_burst (duration = 10, frame_size = 9000, speed = '1gbps'):
+
+ if (frame_size < 60):
+ frame_size = 60
+
+ pkt_dir_0 = create_pkt (frame_size, 0)
+
+ pkt_dir_1 = create_pkt (frame_size, 1)
+
+ # create client
+ c = STLClient(server = t_global.args.ip)
+
+ passed = True
+
+ try:
+ # turn this on for some information
+ #c.set_verbose("high")
+
+ # create two streams
+ s1 = STLStream(packet = pkt_dir_0,
+ mode = STLTXCont(pps = 100))
+
+ # second stream with a phase of 1ms (inter stream gap)
+ s2 = STLStream(packet = pkt_dir_1,
+ isg = 1000,
+ mode = STLTXCont(pps = 100))
+
+ if t_global.args.debug:
+ STLStream.dump_to_yaml ("example.yaml", [s1,s2]) # export to YAML so you can run it on simulator ./stl-sim -f example.yaml -o o.pcap
+
+ # connect to server
+ c.connect()
+
+ # prepare our ports (my machine has 0 <--> 1 with static route)
+ c.reset(ports = [0, 1])
+
+ # add both streams to ports
+ c.add_streams(s1, ports = [0])
+ c.add_streams(s2, ports = [1])
+
+ # clear the stats before injecting
+ c.clear_stats()
+
+ # choose rate and start traffic for 10 seconds on 5 mpps
+ print "Running {0} on ports 0, 1 for 10 seconds, UDP {1}...".format(speed,frame_size+4)
+ c.start(ports = [0, 1], mult = speed, duration = duration)
+
+ # block until done
+ c.wait_on_traffic(ports = [0, 1])
+
+ # read the stats after the test
+ stats = c.get_stats()
+
+ #print stats
+ print json.dumps(stats[0], indent = 4, separators=(',', ': '), sort_keys = True)
+ print json.dumps(stats[1], indent = 4, separators=(',', ': '), sort_keys = True)
+
+ lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
+ lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
+
+ print "\npackets lost from 0 --> 1: {0} pkts".format(lost_a)
+ print "packets lost from 1 --> 0: {0} pkts".format(lost_b)
+
+ if (lost_a == 0) and (lost_b == 0):
+ passed = True
+ else:
+ passed = False
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nPASSED\n"
+ else:
+ print "\nFAILED\n"
+
+def process_options ():
+ parser = argparse.ArgumentParser(usage="""
+ connect to TRex and send burst of packets
+
+ examples
+
+ stl_run_udp_simple.py -s 9001
+
+ stl_run_udp_simple.py -s 9000 -d 2
+
+ stl_run_udp_simple.py -s 3000 -d 3 -m 10mbps
+
+ stl_run_udp_simple.py -s 3000 -d 3 -m 10mbps --debug
+
+ then run the simulator on the output
+ ./stl-sim -f example.yaml -o a.pcap ==> a.pcap include the packet
+
+ """,
+ description="example for TRex api",
+ epilog=" written by hhaim");
+
+ parser.add_argument("-s", "--frame-size",
+ dest="frame_size",
+ help='L2 frame size in bytes without FCS',
+ default=60,
+ type = int,
+ )
+
+ parser.add_argument("--ip",
+ dest="ip",
+ help='remote trex ip default local',
+ default="127.0.0.1",
+ type = str
+ )
+
+
+ parser.add_argument('-d','--duration',
+ dest='duration',
+ help='duration in second ',
+ default=10,
+ type = int,
+ )
+
+
+ parser.add_argument('-m','--multiplier',
+ dest='mul',
+ help='speed in gbps/pps for example 1gbps, 1mbps, 1mpps ',
+ default="1mbps"
+ )
+
+ parser.add_argument('--debug',
+ action='store_true',
+ help='see debug into ')
+
+ parser.add_argument('--version', action='version',
+ version=H_VER )
+
+ t_global.args = parser.parse_args();
+ print t_global.args
+
+
+
+def main():
+ process_options ()
+ simple_burst(duration = t_global.args.duration,
+ frame_size = t_global.args.frame_size,
+ speed = t_global.args.mul
+ )
+
+if __name__ == "__main__":
+ main()
+
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py b/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py
new file mode 100644
index 00000000..1e3e7695
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_simple_burst.py
@@ -0,0 +1,65 @@
+import stl_path
+from trex_control_plane.stl.api import *
+
+import time
+
+def simple_burst ():
+
+
+ # create client
+ c = STLClient()
+ passed = True
+
+ try:
+ pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP()/'a_payload_example')
+
+ # create two bursts and link them
+ s1 = STLStream(name = 'A',
+ packet = pkt,
+ mode = STLTXSingleBurst(total_pkts = 5000),
+ next = 'B')
+
+ s2 = STLStream(name = 'B',
+ self_start = False,
+ packet = pkt,
+ mode = STLTXSingleBurst(total_pkts = 3000))
+
+ # connect to server
+ c.connect()
+
+ # prepare our ports
+ c.reset(ports = [0, 3])
+
+ # add both streams to ports
+ stream_ids = c.add_streams([s1, s2], ports = [0, 3])
+
+ # run 5 times
+ for i in xrange(1, 6):
+ c.clear_stats()
+ c.start(ports = [0, 3], mult = "1gbps")
+ c.wait_on_traffic(ports = [0, 3])
+
+ stats = c.get_stats()
+ ipackets = stats['total']['ipackets']
+
+ print "Test iteration {0} - Packets Received: {1} ".format(i, ipackets)
+ # (5000 + 3000) * 2 ports = 16,000
+ if (ipackets != (16000)):
+ passed = False
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nTest has passed :-)\n"
+ else:
+ print "\nTest has failed :-(\n"
+
+
+# run the tests
+simple_burst()
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py
diff --git a/scripts/automation/trex_control_plane/stl/api.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/api.py
index c12628b5..4c0c10fa 100644
--- a/scripts/automation/trex_control_plane/stl/api.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/api.py
@@ -1,6 +1,6 @@
# get external libs
-import trex_control_plane.client_utils.external_packages
+import trex_stl_ext
# client and exceptions
from trex_stl_exceptions import *
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
index 9b3b9577..410482b9 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_async_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
@@ -10,7 +10,7 @@ import random
from trex_stl_jsonrpc_client import JsonRpcClient, BatchMessage
-from common.text_opts import *
+from utils.text_opts import *
from trex_stl_stats import *
from trex_stl_types import *
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
index 08f640b5..ed11791b 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
@@ -1,9 +1,8 @@
#!/router/bin/python
# for API usage the path name must be full
-from trex_control_plane.stl.trex_stl_exceptions import *
-#from trex_control_plane.stl.trex_stl_streams import *
-from trex_stl_streams import *
+from trex_stl_lib.trex_stl_exceptions import *
+from trex_stl_lib.trex_stl_streams import *
from trex_stl_jsonrpc_client import JsonRpcClient, BatchMessage
import trex_stl_stats
@@ -12,8 +11,8 @@ from trex_stl_port import Port
from trex_stl_types import *
from trex_stl_async_client import CTRexAsyncClient
-from trex_control_plane.client_utils import parsing_opts, text_tables, general_utils
-from trex_control_plane.common.text_opts import *
+from utils import parsing_opts, text_tables, common
+from utils.text_opts import *
from collections import namedtuple
@@ -23,6 +22,7 @@ import datetime
import re
import random
import json
+import traceback
############################ logger #############################
############################ #############################
@@ -376,7 +376,7 @@ class STLClient(object):
"""docstring for STLClient"""
def __init__(self,
- username = general_utils.get_current_user(),
+ username = common.get_current_user(),
server = "localhost",
sync_port = 4501,
async_port = 4500,
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_exceptions.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py
index d5b3885d..45acc72e 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_exceptions.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py
@@ -1,7 +1,7 @@
import os
import sys
-from trex_control_plane.common.text_opts import *
+from utils.text_opts import *
# basic error for API
class STLError(Exception):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py
new file mode 100644
index 00000000..1092679a
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py
@@ -0,0 +1,74 @@
+import sys
+import os
+import warnings
+
+# if not set - set it to default
+if not 'TREX_STL_EXT_PATH' in globals():
+ CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
+ # ../../../../external_libs
+ TREX_STL_EXT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, os.pardir, os.pardir, 'external_libs'))
+
+# check path exists
+if not os.path.exists(TREX_STL_EXT_PATH):
+ print "Unable to find external packages path: '{0}'".format(TREX_STL_EXT_PATH)
+ print "Please provide the correct path using TREX_STL_EXT_PATH variable"
+ exit(0)
+
+# the modules required
+CLIENT_UTILS_MODULES = ['dpkt-1.8.6',
+ 'yaml-3.11',
+ 'texttable-0.8.4',
+ 'scapy-2.3.1'
+ ]
+
+def import_client_utils_modules():
+ import_module_list(CLIENT_UTILS_MODULES)
+
+
+def import_module_list(modules_list):
+ assert(isinstance(modules_list, list))
+ for p in modules_list:
+ full_path = os.path.join(TREX_STL_EXT_PATH, p)
+ fix_path = os.path.normcase(full_path)
+ sys.path.insert(1, full_path)
+
+
+ import_platform_dirs()
+
+
+
+def import_platform_dirs ():
+ # handle platform dirs
+
+ # try fedora 18 first and then cel5.9
+ # we are using the ZMQ module to determine the right platform
+
+ full_path = os.path.join(TREX_STL_EXT_PATH, 'platform/fedora18')
+ fix_path = os.path.normcase(full_path)
+ sys.path.insert(0, full_path)
+ try:
+ # try to import and delete it from the namespace
+ import zmq
+ del zmq
+ return
+ except:
+ sys.path.pop(0)
+ pass
+
+ full_path = os.path.join(TREX_STL_EXT_PATH, 'platform/cel59')
+ fix_path = os.path.normcase(full_path)
+ sys.path.insert(0, full_path)
+ try:
+ # try to import and delete it from the namespace
+ import zmq
+ del zmq
+ return
+
+ except:
+ sys.path.pop(0)
+ sys.modules['zmq'] = None
+ warnings.warn("unable to determine platform type for ZMQ import")
+
+
+import_client_utils_modules()
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
index 887681a7..ab3c7282 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_jsonrpc_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
@@ -2,11 +2,11 @@
import zmq
import json
-import client_utils.general_utils
import re
from time import sleep
from collections import namedtuple
from trex_stl_types import *
+from utils.common import random_id_gen
class bcolors:
BLUE = '\033[94m'
@@ -48,7 +48,8 @@ class JsonRpcClient(object):
# default values
self.port = default_port
self.server = default_server
- self.id_gen = client_utils.general_utils.random_id_gen()
+
+ self.id_gen = random_id_gen()
def get_connection_details (self):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_interface.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_interface.py
index b6e7c026..b6e7c026 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_interface.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_interface.py
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_scapy.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
index 74946c12..0811209a 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_packet_builder_scapy.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
@@ -585,7 +585,7 @@ class CTRexVmDescTupleGen(CTRexVmDescBase):
################################################################################################
-lass CScapyTRexPktBuilder(CTrexPktBuilderInterface):
+class CScapyTRexPktBuilder(CTrexPktBuilderInterface):
"""
This class defines the TRex API of building a packet using dpkt package.
@@ -794,11 +794,11 @@ lass CScapyTRexPktBuilder(CTrexPktBuilderInterface):
return p_utl.get_field_offet_by_str(field_name)
def _get_pkt_as_str(self):
- if self.pkt :
- str(self.pkt)
+ if self.pkt:
+ return str(self.pkt)
if self.pkt_raw:
return self.pkt_raw
- raise CTRexPacketBuildException(-11,('empty packet') % (var_name) );
+ raise CTRexPacketBuildException(-11, 'empty packet');
def _add_tuple_gen(self,tuple_gen):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
index b2cf1c90..b2cf1c90 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_port.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_sim.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py
index d61e04bf..1252b752 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_sim.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_sim.py
@@ -16,10 +16,13 @@ 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.
"""
+# simulator can be run as a standalone
+import trex_stl_ext
+
from trex_stl_exceptions import *
from yaml import YAMLError
from trex_stl_streams import *
-from client_utils import parsing_opts
+from utils import parsing_opts
from trex_stl_client import STLClient
import re
@@ -142,6 +145,29 @@ class STLSim(object):
# load streams
cmds_json = []
+
+ id = 1
+
+ lookup = {}
+ # allocate IDs
+ for stream in stream_list:
+ if stream.get_id() == None:
+ stream.set_id(id)
+ id += 1
+
+ lookup[stream.get_name()] = stream.get_id()
+
+ # resolve names
+ for stream in stream_list:
+ next_id = -1
+ next = stream.get_next()
+ if next:
+ if not next in lookup:
+ raise STLError("stream dependency error - unable to find '{0}'".format(next))
+ next_id = lookup[next]
+
+ stream.fields['next_stream_id'] = next_id
+
for stream in stream_list:
cmd = {"id":1,
"jsonrpc": "2.0",
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
index f880a914..3f09e47c 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_stats.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
@@ -1,7 +1,7 @@
#!/router/bin/python
-from client_utils import text_tables
-from common.text_opts import format_text, format_threshold, format_num
+from utils import text_tables
+from utils.text_opts import format_text, format_threshold, format_num
from trex_stl_async_client import CTRexAsyncStats
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_std.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_std.py
index 72a5ea52..72a5ea52 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_std.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_std.py
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
index 798b2f67..d8e86fef 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_streams.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
@@ -5,8 +5,6 @@ from trex_stl_packet_builder_interface import CTrexPktBuilderInterface
from trex_stl_packet_builder_scapy import CScapyTRexPktBuilder, Ether, IP
from collections import OrderedDict, namedtuple
-from trex_control_plane.client_utils.yaml_utils import *
-
from dpkt import pcap
import random
import yaml
@@ -143,6 +141,7 @@ class STLStream(object):
# packet builder
packet.compile()
+
# packet and VM
self.fields['packet'] = packet.dump_pkt()
self.fields['vm'] = packet.get_vm_data()
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
index a7ddacea..1164076b 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_types.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
@@ -1,6 +1,6 @@
from collections import namedtuple
-from common.text_opts import *
+from utils.text_opts import *
RpcCmdData = namedtuple('RpcCmdData', ['method', 'params'])
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/__init__.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/__init__.py
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
new file mode 100644
index 00000000..117017c3
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
@@ -0,0 +1,47 @@
+import os
+import sys
+import string
+import random
+
+try:
+ import pwd
+except ImportError:
+ import getpass
+ pwd = None
+
+using_python_3 = True if sys.version_info.major == 3 else False
+
+def get_current_user():
+ if pwd:
+ return pwd.getpwuid(os.geteuid()).pw_name
+ else:
+ return getpass.getuser()
+
+
+def user_input():
+ if using_python_3:
+ return input()
+ else:
+ # using python version 2
+ return raw_input()
+
+
+def random_id_gen(length=8):
+ """
+ A generator for creating a random chars id of specific length
+
+ :parameters:
+ length : int
+ the desired length of the generated id
+
+ default: 8
+
+ :return:
+ a random id with each next() request.
+ """
+ id_chars = string.ascii_lowercase + string.digits
+ while True:
+ return_id = ''
+ for i in range(length):
+ return_id += random.choice(id_chars)
+ yield return_id
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
new file mode 100755
index 00000000..968bbb7e
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -0,0 +1,362 @@
+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
+MULTIPLIER_STRICT = 2
+PORT_LIST = 3
+ALL_PORTS = 4
+PORT_LIST_WITH_ALL = 5
+FILE_PATH = 6
+FILE_FROM_DB = 7
+SERVER_IP = 8
+STREAM_FROM_PATH_OR_FILE = 9
+DURATION = 10
+FORCE = 11
+DRY_RUN = 12
+XTERM = 13
+TOTAL = 14
+FULL_OUTPUT = 15
+
+GLOBAL_STATS = 50
+PORT_STATS = 51
+PORT_STATUS = 52
+STATS_MASK = 53
+
+STREAMS_MASK = 60
+# ALL_STREAMS = 61
+# STREAM_LIST_WITH_ALL = 62
+
+
+
+# list of ArgumentGroup types
+MUTEX = 1
+
+def check_negative(value):
+ ivalue = int(value)
+ if ivalue < 0:
+ raise argparse.ArgumentTypeError("non positive value provided: '{0}'".format(value))
+ return ivalue
+
+def match_time_unit(val):
+ '''match some val against time shortcut inputs '''
+ match = re.match("^(\d+(\.\d+)?)([m|h]?)$", val)
+ if match:
+ digit = float(match.group(1))
+ unit = match.group(3)
+ 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")
+
+match_multiplier_help = """Multiplier should be passed in the following format:
+ [number][<empty> | bps | kbps | mbps | gbps | pps | kpps | mpps | %% ].
+ no suffix will provide an absoulute factor and percentage
+ will provide a percentage of the line rate. examples
+ : '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """
+
+
+# decodes multiplier
+# if allow_update - no +/- is allowed
+# divide states between how many entities the
+# value should be divided
+def decode_multiplier(val, allow_update = False, divide_count = 1):
+
+ # must be string
+ if not isinstance(val, str):
+ return None
+
+ # do we allow updates ? +/-
+ if not allow_update:
+ match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val)
+ op = None
+ else:
+ match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)([\+\-])?$", val)
+ if match:
+ op = match.group(4)
+ else:
+ op = None
+
+ result = {}
+
+ if match:
+
+ value = float(match.group(1))
+ unit = match.group(3)
+
+
+
+ # raw type (factor)
+ if not unit:
+ result['type'] = 'raw'
+ result['value'] = value
+
+ elif unit == 'bps':
+ result['type'] = 'bps'
+ result['value'] = value
+
+ elif unit == 'kbps':
+ result['type'] = 'bps'
+ result['value'] = value * 1000
+
+ elif unit == 'mbps':
+ result['type'] = 'bps'
+ result['value'] = value * 1000 * 1000
+
+ elif unit == 'gbps':
+ result['type'] = 'bps'
+ result['value'] = value * 1000 * 1000 * 1000
+
+ elif unit == 'pps':
+ result['type'] = 'pps'
+ result['value'] = value
+
+ elif unit == "kpps":
+ result['type'] = 'pps'
+ result['value'] = value * 1000
+
+ elif unit == "mpps":
+ result['type'] = 'pps'
+ result['value'] = value * 1000 * 1000
+
+ elif unit == "%":
+ result['type'] = 'percentage'
+ result['value'] = value
+
+
+ if op == "+":
+ result['op'] = "add"
+ elif op == "-":
+ result['op'] = "sub"
+ else:
+ result['op'] = "abs"
+
+ if result['op'] != 'percentage':
+ result['value'] = result['value'] / divide_count
+
+ return result
+
+ else:
+ return None
+
+
+def match_multiplier(val):
+ '''match some val against multiplier shortcut inputs '''
+ result = decode_multiplier(val, allow_update = True)
+ if not result:
+ raise argparse.ArgumentTypeError(match_multiplier_help)
+
+ return val
+
+
+def match_multiplier_strict(val):
+ '''match some val against multiplier shortcut inputs '''
+ result = decode_multiplier(val, allow_update = False)
+ if not result:
+ raise argparse.ArgumentTypeError(match_multiplier_help)
+
+ return val
+
+
+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': match_multiplier_help,
+ 'dest': "mult",
+ 'default': "1",
+ 'type': match_multiplier}),
+
+ MULTIPLIER_STRICT: ArgumentPack(['-m', '--multiplier'],
+ {'help': match_multiplier_help,
+ 'dest': "mult",
+ 'default': "1",
+ 'type': match_multiplier_strict}),
+
+ TOTAL: ArgumentPack(['-t', '--total'],
+ {'help': "traffic will be divided between all ports specified",
+ 'dest': "total",
+ 'default': False,
+ 'action': "store_true"}),
+
+ 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",
+ 'default': False},),
+
+ 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"}),
+
+ DRY_RUN: ArgumentPack(['-n', '--dry'],
+ {'action': 'store_true',
+ 'dest': 'dry',
+ 'default': False,
+ 'help': "Dry run - no traffic will be injected"}),
+
+
+ XTERM: ArgumentPack(['-x', '--xterm'],
+ {'action': 'store_true',
+ 'dest': 'xterm',
+ 'default': False,
+ 'help': "Starts TUI in xterm window"}),
+
+
+ FULL_OUTPUT: ArgumentPack(['--full'],
+ {'action': 'store_true',
+ 'help': "Prompt full info in a JSON format"}),
+
+ 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"}),
+
+ STREAMS_MASK: ArgumentPack(['--streams'],
+ {"nargs": '+',
+ 'dest':'streams',
+ 'metavar': 'STREAMS',
+ 'type': int,
+ 'help': "A list of stream IDs to query about. Default: analyze all streams",
+ 'default': []}),
+
+
+ # advanced options
+ PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST,
+ ALL_PORTS],
+ {'required': False}),
+
+ 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 all ports are marked or
+ if (getattr(opts, "all_ports", None) == True) or (getattr(opts, "ports", None) == []):
+ opts.ports = self.stateless_client.get_all_ports()
+
+ # so maybe we have ports configured
+ elif 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/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
new file mode 100644
index 00000000..78a0ab1f
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
@@ -0,0 +1,192 @@
+import json
+import re
+
+TEXT_CODES = {'bold': {'start': '\x1b[1m',
+ 'end': '\x1b[22m'},
+ 'cyan': {'start': '\x1b[36m',
+ 'end': '\x1b[39m'},
+ 'blue': {'start': '\x1b[34m',
+ 'end': '\x1b[39m'},
+ 'red': {'start': '\x1b[31m',
+ 'end': '\x1b[39m'},
+ 'magenta': {'start': '\x1b[35m',
+ 'end': '\x1b[39m'},
+ 'green': {'start': '\x1b[32m',
+ 'end': '\x1b[39m'},
+ 'yellow': {'start': '\x1b[33m',
+ 'end': '\x1b[39m'},
+ 'underline': {'start': '\x1b[4m',
+ 'end': '\x1b[24m'}}
+
+class TextCodesStripper:
+ keys = [re.escape(v['start']) for k,v in TEXT_CODES.iteritems()]
+ keys += [re.escape(v['end']) for k,v in TEXT_CODES.iteritems()]
+ pattern = re.compile("|".join(keys))
+
+ @staticmethod
+ def strip (s):
+ return re.sub(TextCodesStripper.pattern, '', s)
+
+def format_num (size, suffix = "", compact = True, opts = ()):
+ txt = "NaN"
+
+ if type(size) == str:
+ return "N/A"
+
+ u = ''
+
+ if compact:
+ for unit in ['','K','M','G','T','P']:
+ if abs(size) < 1000.0:
+ u = unit
+ break
+ size /= 1000.0
+
+ if isinstance(size, float):
+ txt = "%3.2f" % (size)
+ else:
+ txt = "{:,}".format(size)
+
+ if u or suffix:
+ txt += " {:}{:}".format(u, suffix)
+
+ if isinstance(opts, tuple):
+ return format_text(txt, *opts)
+ else:
+ return format_text(txt, (opts))
+
+
+
+def format_time (t_sec):
+ if t_sec < 0:
+ return "infinite"
+
+ if t_sec < 1:
+ # low numbers
+ for unit in ['ms', 'usec', 'ns']:
+ t_sec *= 1000.0
+ if t_sec >= 1.0:
+ return '{:,.2f} [{:}]'.format(t_sec, unit)
+
+ return "NaN"
+
+ else:
+ # seconds
+ if t_sec < 60.0:
+ return '{:,.2f} [{:}]'.format(t_sec, 'sec')
+
+ # minutes
+ t_sec /= 60.0
+ if t_sec < 60.0:
+ return '{:,.2f} [{:}]'.format(t_sec, 'minutes')
+
+ # hours
+ t_sec /= 60.0
+ if t_sec < 24.0:
+ return '{:,.2f} [{:}]'.format(t_sec, 'hours')
+
+ # days
+ t_sec /= 24.0
+ return '{:,.2f} [{:}]'.format(t_sec, 'days')
+
+
+def format_percentage (size):
+ return "%0.2f %%" % (size)
+
+def bold(text):
+ return text_attribute(text, 'bold')
+
+
+def cyan(text):
+ return text_attribute(text, 'cyan')
+
+
+def blue(text):
+ return text_attribute(text, 'blue')
+
+
+def red(text):
+ return text_attribute(text, 'red')
+
+
+def magenta(text):
+ return text_attribute(text, 'magenta')
+
+
+def green(text):
+ return text_attribute(text, 'green')
+
+def yellow(text):
+ return text_attribute(text, 'yellow')
+
+def underline(text):
+ return text_attribute(text, 'underline')
+
+
+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")
+
+
+FUNC_DICT = {'blue': blue,
+ 'bold': bold,
+ 'green': green,
+ 'yellow': yellow,
+ 'cyan': cyan,
+ 'magenta': magenta,
+ 'underline': underline,
+ 'red': red}
+
+
+def format_text(text, *args):
+ return_string = text
+ for i in args:
+ func = FUNC_DICT.get(i)
+ if func:
+ return_string = func(return_string)
+
+ return return_string
+
+
+def format_threshold (value, red_zone, green_zone):
+ if value >= red_zone[0] and value <= red_zone[1]:
+ return format_text("{0}".format(value), 'red')
+
+ if value >= green_zone[0] and value <= green_zone[1]:
+ return format_text("{0}".format(value), 'green')
+
+ return "{0}".format(value)
+
+# pretty print for JSON
+def pretty_json (json_str, use_colors = True):
+ pretty_str = json.dumps(json.loads(json_str), indent = 4, separators=(',', ': '), sort_keys = True)
+
+ if not use_colors:
+ return pretty_str
+
+ try:
+ # int numbers
+ pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*[^.])',r'\1{0}'.format(blue(r'\2')), pretty_str)
+ # float
+ pretty_str = re.sub(r'([ ]*:[ ]+)(\-?[1-9][0-9]*\.[0-9]+)',r'\1{0}'.format(magenta(r'\2')), pretty_str)
+ # # strings
+ #
+ pretty_str = re.sub(r'([ ]*:[ ]+)("[^"]*")',r'\1{0}'.format(red(r'\2')), pretty_str)
+ pretty_str = re.sub(r"('[^']*')", r'{0}\1{1}'.format(TEXT_CODES['magenta']['start'],
+ TEXT_CODES['red']['start']), pretty_str)
+ except :
+ pass
+
+ return pretty_str
+
+
+if __name__ == "__main__":
+ pass
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_tables.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_tables.py
new file mode 100644
index 00000000..07753fda
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_tables.py
@@ -0,0 +1,31 @@
+from texttable import Texttable
+from 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="", untouched_header=""):
+ header = header.replace("_", " ").title() + untouched_header
+ print format_text(header, 'cyan', 'underline') + "\n"
+
+ print (texttable_obj.draw() + "\n").encode('utf-8')
+
+if __name__ == "__main__":
+ pass
+