summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-01-19 08:49:31 -0500
committerimarom <imarom@cisco.com>2016-01-21 10:11:55 -0500
commitc93acc26bf2517c872da716198e76bcf566b836a (patch)
treed8edf7b972eae0d821cee644b720312f97fd5891 /scripts/automation/trex_control_plane
parent2d9d5e147b8f15a8308dad46711390f3b168ec56 (diff)
draft #2
Diffstat (limited to 'scripts/automation/trex_control_plane')
-rwxr-xr-xscripts/automation/trex_control_plane/client/trex_stateless_client.py1056
-rwxr-xr-xscripts/automation/trex_control_plane/client_utils/parsing_opts.py36
-rw-r--r--scripts/automation/trex_control_plane/common/trex_types.py12
-rwxr-xr-xscripts/automation/trex_control_plane/console/trex_console.py18
4 files changed, 651 insertions, 471 deletions
diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
index 43912e55..28e55088 100755
--- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py
+++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
@@ -25,13 +25,10 @@ from trex_port import Port
from common.trex_types import *
from trex_async_client import CTRexAsyncClient
-############################ logger #############################
-############################ #############################
-############################ #############################
-
-class STLFailure(Exception):
- def __init__ (self, rc_or_str):
- self.msg = str(rc_or_str)
+# basic error for API
+class STLError(Exception):
+ def __init__ (self, msg):
+ self.msg = str(msg)
def __str__ (self):
exc_type, exc_obj, exc_tb = sys.exc_info()
@@ -39,11 +36,35 @@ class STLFailure(Exception):
s = "\n******\n"
- s += "Error reported at {0}:{1}\n\n".format(format_text(fname, 'bold'), format_text(exc_tb.tb_lineno), 'bold')
- s += "specific error:\n\n'{0}'\n".format(format_text(self.msg, 'bold'))
+ s += "Error at {0}:{1}\n\n".format(format_text(fname, 'bold'), format_text(exc_tb.tb_lineno), 'bold')
+ s += "specific error:\n\n{0}\n".format(format_text(self.msg, 'bold'))
return s
+ def brief (self):
+ return self.msg
+
+
+# raised when the client state is invalid for operation
+class STLStateError(STLError):
+ def __init__ (self, op, state):
+ self.msg = "Operation '{0}' is not valid while '{1}'".format(op, state)
+
+
+# raised when argument is not valid for operation
+class STLArgumentError(STLError):
+ def __init__ (self, name, got, valid_values = None, extended = None):
+ self.msg = "Argument: '{0}' invalid value: '{1}'".format(name, got)
+ if valid_values:
+ self.msg += " - valid values are '{0}'".format(valid_values)
+
+ if extended:
+ self.msg += "\n{0}".format(extended)
+
+
+############################ logger #############################
+############################ #############################
+############################ #############################
# logger API for the client
class LoggerApi(object):
@@ -734,19 +755,19 @@ class CTRexStatelessClient(object):
self.logger.log(format_text(msg, 'bold'))
return RC_ERR(msg)
else:
- rc = self.cmd_stop(active_ports)
+ rc = self.__stop(active_ports)
if not rc:
return rc
rc = self.__remove_all_streams(port_id_list)
- self.logger.annotate(rc,"Removing all streams from port(s) {0}:".format(port_id_list))
+ self.logger.annotate(rc, "Removing all streams from port(s) {0}:".format(port_id_list))
if rc.bad():
return rc
rc = self.__add_stream_pack(stream_list, port_id_list)
- self.logger.annotate(rc,"Attaching {0} streams to port(s) {1}:".format(len(stream_list.compiled), port_id_list))
+ self.logger.annotate(rc, "Attaching {0} streams to port(s) {1}:".format(len(stream_list.compiled), port_id_list))
if rc.bad():
return rc
@@ -769,35 +790,85 @@ class CTRexStatelessClient(object):
return rc
+ # stop cmd
+ def __stop (self, port_id_list):
- def __verify_port_id_list (self, port_id_list):
- # check arguments
- if not isinstance(port_id_list, list):
- return RC_ERR("ports should be an instance of 'list'")
+ # find the relveant ports
+ active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
- # all ports are valid ports
- if not all([port_id in self.get_all_ports() for port_id in port_id_list]):
- return RC_ERR("Port IDs valid values are '{0}' but provided '{1}'".format(self.get_all_ports(), port_id_list))
+ if not active_ports:
+ msg = "No active traffic on provided ports"
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_WARN(msg)
+
+ rc = self.__stop_traffic(active_ports)
+ self.logger.annotate(rc, "Stopping traffic on port(s) {0}:".format(port_id_list))
+ if not rc:
+ return rc
return RC_OK()
+ #update cmd
+ def __update (self, port_id_list, mult):
- def __verify_mult (self, mult, strict):
- if not isinstance(mult, dict):
- return RC_ERR("mult should be an instance of dict")
+ # find the relevant ports
+ active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
- types = ["raw", "bps", "pps", "percentage"]
- if not mult.get('type', None) in types:
- return RC_ERR("mult should contain 'type' field of one of '{0}'".format(types))
+ if not active_ports:
+ msg = "No active traffic on provided ports"
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_WARN(msg)
- if strict:
- ops = ["abs"]
- else:
- ops = ["abs", "add", "sub"]
- if not mult.get('op', None) in ops:
- return RC_ERR("mult should contain 'op' field of one of '{0}'".format(ops))
+ rc = self.__update_traffic(mult, active_ports)
+ self.logger.annotate(rc, "Updating traffic on port(s) {0}:".format(port_id_list))
+
+ return rc
+
+
+ # pause cmd
+ def __pause (self, port_id_list):
+
+ # find the relevant ports
+ active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
+
+ if not active_ports:
+ msg = "No active traffic on provided ports"
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_WARN(msg)
+
+ rc = self.__pause_traffic(active_ports)
+ self.logger.annotate(rc, "Pausing traffic on port(s) {0}:".format(port_id_list))
+ return rc
+
+
+ # resume cmd
+ def __resume (self, port_id_list):
+
+ # find the relveant ports
+ active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
+
+ if not active_ports:
+ msg = "No active traffic on porvided ports"
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_WARN(msg)
+
+ rc = self.__resume_traffic(active_ports)
+ self.logger.annotate(rc, "Resume traffic on port(s) {0}:".format(port_id_list))
+ return rc
+
+
+ # clear stats
+ def __clear_stats(self, port_id_list):
+
+ for port_id in port_id_list:
+ self.ports[port_id].clear_stats()
+
+ self.global_stats.clear_stats()
+
+ rc = RC_OK()
+ self.logger.annotate(rc, "clearing stats on port(s) {0}:".format(port_id_list))
+ return RC
- return RC_OK()
def __process_profiles (self, profiles, out):
@@ -815,7 +886,8 @@ class CTRexStatelessClient(object):
self.logger.annotate(rc)
return rc
- out += stream_list
+ out.append(stream_list)
+
else:
return RC_ERR("unknown profile '{0}'".format(profile))
@@ -850,14 +922,23 @@ class CTRexStatelessClient(object):
############ functions used by other classes but not users ##############
+ def _verify_port_id_list (self, port_id_list):
+ # check arguments
+ if not isinstance(port_id_list, list):
+ return RC_ERR("ports should be an instance of 'list' not {0}".format(type(port_id_list)))
+
+ # all ports are valid ports
+ if not port_id_list or not all([port_id in self.get_all_ports() for port_id in port_id_list]):
+ return RC_ERR("")
+
+ return RC_OK()
+
def _validate_port_list(self, port_id_list):
if not isinstance(port_id_list, list):
- print type(port_id_list)
return False
# check each item of the sequence
- return all([ (port_id >= 0) and (port_id < self.get_port_count())
- for port_id in port_id_list ])
+ return port_id_list and all([port_id in self.get_all_ports() for port_id in port_id_list])
# transmit request on the RPC link
@@ -870,65 +951,12 @@ class CTRexStatelessClient(object):
############# helper functions section ##############
- # measure time for functions
- def timing(f):
- def wrap(*args):
-
- time1 = time.time()
- ret = f(*args)
-
- # don't want to print on error
- if ret.bad():
- return ret
-
- delta = time.time() - time1
-
- client = args[0]
- client.logger.log(format_time(delta) + "\n")
-
- return ret
-
- return wrap
-
-
-
+
########## port commands ##############
######################### Console (high level) API #########################
- # stop cmd
- def cmd_stop (self, port_id_list):
-
- # find the relveant ports
- active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
-
- if not active_ports:
- msg = "No active traffic on provided ports"
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
-
- rc = self.__stop_traffic(active_ports)
- self.logger.annotate(rc,"Stopping traffic on port(s) {0}:".format(port_id_list))
- if rc.bad():
- return rc
-
- return RC_OK()
-
- # update cmd
- def cmd_update (self, port_id_list, mult):
-
- # find the relevant ports
- active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
-
- if not active_ports:
- msg = "No active traffic on provided ports"
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
-
- rc = self.__update_traffic(mult, active_ports)
- self.logger.annotate(rc,"Updating traffic on port(s) {0}:".format(port_id_list))
-
- return rc
+
# clear stats
def cmd_clear(self, port_id_list):
@@ -949,40 +977,11 @@ class CTRexStatelessClient(object):
return RC_OK()
- # pause cmd
- def cmd_pause (self, port_id_list):
-
- # find the relevant ports
- active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
-
- if not active_ports:
- msg = "No active traffic on provided ports"
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
-
- rc = self.__pause_traffic(active_ports)
- self.logger.annotate(rc,"Pausing traffic on port(s) {0}:".format(port_id_list))
- return rc
-
-
-
- # resume cmd
- def cmd_resume (self, port_id_list):
-
- # find the relveant ports
- active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
-
- if not active_ports:
- msg = "No active traffic on porvided ports"
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
+
- rc = self.__resume_traffic(active_ports)
- self.logger.annotate(rc,"Resume traffic on port(s) {0}:".format(port_id_list))
- return rc
-
+
# validate port(s) profile
@@ -1011,306 +1010,10 @@ class CTRexStatelessClient(object):
############## High Level API With Parser ################
- @timing
- def cmd_start_line (self, line):
- '''Start selected traffic in specified ports on TRex\n'''
- # define a parser
- parser = parsing_opts.gen_parser(self,
- "start",
- self.cmd_start_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.TOTAL,
- parsing_opts.FORCE,
- parsing_opts.STREAM_FROM_PATH_OR_FILE,
- parsing_opts.DURATION,
- parsing_opts.MULTIPLIER_STRICT,
- parsing_opts.DRY_RUN)
-
- opts = parser.parse_args(line.split())
-
-
- if opts is None:
- return RC_ERR("bad command line parameters")
-
-
- if opts.dry:
- self.logger.log(format_text("\n*** DRY RUN ***", 'bold'))
-
- if opts.db:
- stream_list = self.streams_db.get_stream_pack(opts.db)
- rc = RC(stream_list != None)
- self.logger.annotate(rc,"Load stream pack (from DB):")
- if rc.bad():
- return RC_ERR("Failed to load stream pack")
-
- else:
- # load streams from file
- stream_list = None
- try:
- stream_list = self.streams_db.load_yaml_file(opts.file[0])
- except Exception as e:
- s = str(e)
- rc=RC_ERR(s)
- self.logger.annotate(rc)
- return rc
-
- rc = RC(stream_list != None)
- self.logger.annotate(rc,"Load stream pack (from file):")
- if stream_list == None:
- return RC_ERR("Failed to load stream pack")
-
-
- # total has no meaning with percentage - its linear
- if opts.total and (opts.mult['type'] != 'percentage'):
- # if total was set - divide it between the ports
- opts.mult['value'] = opts.mult['value'] / len(opts.ports)
-
- return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration, opts.dry)
-
- @timing
- def cmd_resume_line (self, line):
- '''Resume active traffic in specified ports on TRex\n'''
- parser = parsing_opts.gen_parser(self,
- "resume",
- self.cmd_stop_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_resume(opts.ports)
-
-
- @timing
- def cmd_stop_line (self, line):
- '''Stop active traffic in specified ports on TRex\n'''
- parser = parsing_opts.gen_parser(self,
- "stop",
- self.cmd_stop_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_stop(opts.ports)
-
-
- @timing
- def cmd_pause_line (self, line):
- '''Pause active traffic in specified ports on TRex\n'''
- parser = parsing_opts.gen_parser(self,
- "pause",
- self.cmd_stop_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_pause(opts.ports)
-
-
- @timing
- def cmd_update_line (self, line):
- '''Update port(s) speed currently active\n'''
- parser = parsing_opts.gen_parser(self,
- "update",
- self.cmd_update_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.MULTIPLIER,
- parsing_opts.TOTAL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line paramters")
-
- # total has no meaning with percentage - its linear
- if opts.total and (opts.mult['type'] != 'percentage'):
- # if total was set - divide it between the ports
- opts.mult['value'] = opts.mult['value'] / len(opts.ports)
-
- return self.cmd_update(opts.ports, opts.mult)
-
- @timing
- def cmd_reset_line (self, line):
- return self.cmd_reset()
-
-
- def cmd_clear_line (self, line):
- '''Clear cached local statistics\n'''
- # define a parser
- parser = parsing_opts.gen_parser(self,
- "clear",
- self.cmd_clear_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
-
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_clear(opts.ports)
-
-
- def cmd_stats_line (self, line):
- '''Fetch statistics from TRex server by port\n'''
- # define a parser
- parser = parsing_opts.gen_parser(self,
- "stats",
- self.cmd_stats_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.STATS_MASK)
-
- opts = parser.parse_args(line.split())
-
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- # determine stats mask
- mask = self._get_mask_keys(**self._filter_namespace_args(opts, trex_stats.ALL_STATS_OPTS))
- if not mask:
- # set to show all stats if no filter was given
- mask = trex_stats.ALL_STATS_OPTS
-
- stats = self.cmd_stats(opts.ports, mask)
-
- # print stats to screen
- for stat_type, stat_data in stats.iteritems():
- text_tables.print_table_with_header(stat_data.text_table, stat_type)
-
- return RC_OK()
-
- def cmd_streams_line(self, line):
- '''Fetch streams statistics from TRex server by port\n'''
- # define a parser
- parser = parsing_opts.gen_parser(self,
- "streams",
- self.cmd_streams_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.STREAMS_MASK)#,
- #parsing_opts.FULL_OUTPUT)
-
- opts = parser.parse_args(line.split())
-
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- streams = self.cmd_streams(opts.ports, set(opts.streams))
- if not streams:
- # we got no streams running
-
- self.logger.log(format_text("No streams found with desired filter.\n", "bold", "magenta"))
- return RC_ERR("No streams found with desired filter.")
- else:
- # print stats to screen
- for stream_hdr, port_streams_data in streams.iteritems():
- text_tables.print_table_with_header(port_streams_data.text_table,
- header= stream_hdr.split(":")[0] + ":",
- untouched_header= stream_hdr.split(":")[1])
- return RC_OK()
-
-
-
-
- @timing
- def cmd_validate_line (self, line):
- '''validates port(s) stream configuration\n'''
-
- parser = parsing_opts.gen_parser(self,
- "validate",
- self.cmd_validate_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line paramters")
-
- rc = self.cmd_validate(opts.ports)
- return rc
-
-
- def cmd_exit_line (self, line):
- self.logger.log(format_text("Exiting\n", 'bold'))
- # a way to exit
- return RC_ERR("exit")
-
-
- def cmd_wait_line (self, line):
- '''wait for a period of time\n'''
-
- parser = parsing_opts.gen_parser(self,
- "wait",
- self.cmd_wait_line.__doc__,
- parsing_opts.DURATION)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- delay_sec = opts.duration if (opts.duration > 0) else 1
-
- self.logger.log(format_text("Waiting for {0} seconds...\n".format(delay_sec), 'bold'))
- time.sleep(delay_sec)
-
- return RC_OK()
-
- # run a script of commands
- def run_script_file (self, filename):
-
- self.logger.log(format_text("\nRunning script file '{0}'...".format(filename), 'bold'))
-
- rc = self.cmd_connect()
- if rc.bad():
- return
-
- with open(filename) as f:
- script_lines = f.readlines()
-
- cmd_table = {}
-
- # register all the commands
- cmd_table['start'] = self.cmd_start_line
- cmd_table['stop'] = self.cmd_stop_line
- cmd_table['reset'] = self.cmd_reset_line
- cmd_table['wait'] = self.cmd_wait_line
- cmd_table['exit'] = self.cmd_exit_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 = ""
-
- self.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)
- self.logger.log(format_text("unknown command '{0}'\n".format(cmd), 'bold'))
- return False
-
- rc = cmd_table[cmd](args)
- if rc.bad():
- return False
-
- self.logger.log(format_text("\n[Done]", 'bold'))
-
- return True
-
-
#################################
# ------ private methods ------ #
@staticmethod
- def _get_mask_keys(ok_values={True}, **kwargs):
+ def __get_mask_keys(ok_values={True}, **kwargs):
masked_keys = set()
for key, val in kwargs.iteritems():
if val in ok_values:
@@ -1318,19 +1021,20 @@ class CTRexStatelessClient(object):
return masked_keys
@staticmethod
- def _filter_namespace_args(namespace, ok_values):
+ def __filter_namespace_args(namespace, ok_values):
return {k: v for k, v in namespace.__dict__.items() if k in ok_values}
+
+ # verify decorator - throws exception is client is disconnected
def __verify_connected(f):
- #@wraps(f)
- def wrap(*args):
+ def wrap(*args, **kwargs):
inst = args[0]
func_name = f.__name__
- if not inst.stateless_client.is_connected():
- return RC_ERR("cannot execute '{0}' while client is disconnected".format(func_name))
+ if not inst.is_connected():
+ raise STLStateError(func_name, 'disconnected')
- ret = f(*args)
+ ret = f(*args, **kwargs)
return ret
return wrap
@@ -1381,9 +1085,15 @@ class CTRexStatelessClient(object):
def get_port_count(self):
return len(self.ports)
+
# returns the port object
def get_port (self, port_id):
- return self.ports.get(port_id, RC_ERR("invalid port id"))
+ port_id = self.ports.get(port_id, None)
+ if (port_id != None):
+ return port_id
+ else:
+ raise STLArgumentError('port id', port_id, valid_values = self.get_all_ports())
+
# get all ports as IDs
def get_all_ports (self):
@@ -1432,13 +1142,13 @@ class CTRexStatelessClient(object):
def connect (self, mode = "RW"):
modes = ['RO', 'RW', 'RWF']
if not mode in modes:
- return RC_ERR("invalid mode '{0}'".format(mode))
+ raise STLArgumentError('mode', mode, modes)
rc = self.__connect(mode)
self.logger.annotate(rc)
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
return rc
@@ -1450,67 +1160,81 @@ class CTRexStatelessClient(object):
self.logger.annotate(rc, "Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'],
self.connection_info['sync_port']))
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
return rc
# teardown - call after test is done
- def teardown (self):
- # for now, its only disconnect
+ def teardown (self, stop_traffic = True):
+
+ # stop traffic
+ if stop_traffic:
+ rc = self.stop()
+ if not rc:
+ raise STLError(rc)
+
+ # disconnect
rc = self.__disconnect()
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
+
+
return rc
# pings the server on the RPC channel
+ @__verify_connected
def ping(self):
rc = self.__ping()
self.logger.annotate(rc, "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
self.connection_info['sync_port']))
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
return rc
# reset the server by performing
# force acquire, stop, and remove all streams
+ @__verify_connected
def reset(self):
rc = self.__acquire(force = True)
self.logger.annotate(rc, "Force acquiring all ports:")
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
# force stop all ports
rc = self.__stop_traffic(self.get_all_ports(), True)
self.logger.annotate(rc,"Stop traffic on all ports:")
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
# remove all streams
rc = self.__remove_all_streams(self.get_all_ports())
self.logger.annotate(rc,"Removing all streams from all ports:")
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
# TODO: clear stats
return RC_OK()
+
# start cmd
+ @__verify_connected
def start (self,
profiles,
ports = None,
mult = "1",
force = False,
duration = -1,
- dry = False):
+ dry = False,
+ total = False):
# by default use all ports
@@ -1518,30 +1242,188 @@ class CTRexStatelessClient(object):
ports = self.get_all_ports()
# verify valid port id list
- rc = self.__verify_port_id_list(ports)
+ rc = self._validate_port_list(ports)
if not rc:
- raise STLFailure(rc)
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
# verify multiplier
- try:
- result = parsing_opts.match_multiplier_common(mult)
- except argparse.ArgumentTypeError:
- raise STLFailure("bad format for multiplier: {0}".format(mult))
+ mult_obj = parsing_opts.decode_multiplier(mult,
+ allow_update = False,
+ divide_count = len(ports) if total else 1)
+ if not mult_obj:
+ raise STLArgumentError('mult', mult)
+
+ # some type checkings
+
+ if not type(force) is bool:
+ raise STLArgumentError('force', force)
+
+ if not isinstance(duration, (int, float)):
+ raise STLArgumentError('duration', duration)
+
+ if not type(total) is bool:
+ raise STLArgumentError('total', total)
+
# process profiles
stream_list = []
rc = self.__process_profiles(profiles, stream_list)
if not rc:
- raise STLFailure(rc)
+ raise STLError(rc)
+
+
+ # dry run
+ if dry:
+ self.logger.log(format_text("\n*** DRY RUN ***", 'bold'))
+
+ # call private method to start
+ rc = self.__start(ports, stream_list[0], mult_obj, force, duration, dry)
+ if not rc:
+ raise STLError(rc)
+
+ return rc
+
+
+ # stop traffic on ports
+ @__verify_connected
+ def stop (self, ports = None):
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
+ if not rc:
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+ rc = self.__stop(ports)
+ if not rc:
+ raise STLError(rc)
+
+ return rc
+
+
+ # update traffic
+ @__verify_connected
+ def update (self, ports = None, mult = "1", total = False):
+
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
+ if not rc:
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+
+ # verify multiplier
+ mult_obj = parsing_opts.decode_multiplier(mult,
+ allow_update = True,
+ divide_count = len(ports) if total else 1)
+ if not mult_obj:
+ raise STLArgumentError('mult', mult)
+
+ # verify total
+ if not type(total) is bool:
+ raise STLArgumentError('total', total)
+
+
+ # call low level functions
+ rc = self.__update(ports, mult_obj)
+ if not rc:
+ raise STLError(rc)
+
+ return rc
+
+
+ # pause traffic on ports
+ @__verify_connected
+ def pause (self, ports = None):
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
+ if not rc:
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+ rc = self.__pause(ports)
+ if not rc:
+ raise STLError(rc)
+
+ return rc
+
+
+ # resume traffic on ports
+ @__verify_connected
+ def resume (self, ports = None):
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
+ if not rc:
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+ rc = self.__resume(ports)
+ if not rc:
+ raise STLError(rc)
+
+ return rc
+
+
+ # clear stats
+ def clear_stats (self, ports = None):
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
+ if not rc:
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+ rc = self.__clear_stats(ports)
+ if not rc:
+ raise STLError(rc)
-
############################ Line #############################
############################ Commands #############################
############################ #############################
+ # console decorator
+ def __console(f):
+ def wrap(*args):
+ client = args[0]
+
+ time1 = time.time()
+
+ try:
+ rc = f(*args)
+ except STLError as e:
+ client.logger.log(format_text("\n" + e.brief() + "\n", 'bold'))
+ return
+
+ # don't want to print on error
+ if not rc or rc.warn():
+ return rc
+
+ delta = time.time() - time1
+
+ client.logger.log(format_time(delta) + "\n")
+
+ return rc
+
+ return wrap
+
+
+ @__console
def connect_line (self, line):
'''Connects to the TRex server'''
# define a parser
@@ -1553,17 +1435,287 @@ class CTRexStatelessClient(object):
opts = parser.parse_args(line.split())
if opts is None:
- return RC_ERR("bad command line parameters")
+ return
# call the API
- if opts.force:
- rc = self.connect(mode = "RWF")
- else:
- rc = self.connect(mode = "RW")
+ mode = "RWF" if opts.force else "RW"
+ self.connect(mode)
+ @__console
def disconnect_line (self, line):
- return self.disconnect()
+ self.disconnect()
+
+ @__console
def reset_line (self, line):
- return self.reset()
+ self.reset()
+
+
+ @__console
+ def start_line (self, line):
+ '''Start selected traffic in specified ports on TRex\n'''
+ # define a parser
+ parser = parsing_opts.gen_parser(self,
+ "start",
+ self.start_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.TOTAL,
+ parsing_opts.FORCE,
+ parsing_opts.STREAM_FROM_PATH_OR_FILE,
+ parsing_opts.DURATION,
+ parsing_opts.MULTIPLIER_STRICT,
+ parsing_opts.DRY_RUN)
+
+ opts = parser.parse_args(line.split())
+
+
+ if opts is None:
+ return
+
+ # pack the profile
+ profiles = [opts.file[0]]
+
+ self.start(profiles,
+ opts.ports,
+ opts.mult,
+ opts.force,
+ opts.duration,
+ opts.dry,
+ opts.total)
+
+
+
+ @__console
+ def stop_line (self, line):
+ '''Stop active traffic in specified ports on TRex\n'''
+ parser = parsing_opts.gen_parser(self,
+ "stop",
+ self.stop_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ self.stop(opts.ports)
+
+
+ @__console
+ def update_line (self, line):
+ '''Update port(s) speed currently active\n'''
+ parser = parsing_opts.gen_parser(self,
+ "update",
+ self.update_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.MULTIPLIER,
+ parsing_opts.TOTAL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ self.update(opts.ports, opts.mult, opts.total)
+
+
+ @__console
+ def pause_line (self, line):
+ '''Pause active traffic in specified ports on TRex\n'''
+ parser = parsing_opts.gen_parser(self,
+ "pause",
+ self.pause_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ self.pause(opts.ports)
+
+
+ @__console
+ def resume_line (self, line):
+ '''Resume active traffic in specified ports on TRex\n'''
+ parser = parsing_opts.gen_parser(self,
+ "resume",
+ self.resume_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+ return self.resume(opts.ports)
+
+
+ @__console
+ def clear_stats_line (self, line):
+ '''Clear cached local statistics\n'''
+ # define a parser
+ parser = parsing_opts.gen_parser(self,
+ "clear",
+ self.clear_stats_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+
+ if opts is None:
+ return
+
+ self.clear_stats(opts.ports)
+
+
+
+ @__console
+ def show_stats_line (self, line):
+ '''Fetch statistics from TRex server by port\n'''
+ # define a parser
+ parser = parsing_opts.gen_parser(self,
+ "stats",
+ self.cmd_stats_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.STATS_MASK)
+
+ opts = parser.parse_args(line.split())
+
+ if opts is None:
+ return RC_ERR("bad command line parameters")
+
+ # determine stats mask
+ mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stats.ALL_STATS_OPTS))
+ if not mask:
+ # set to show all stats if no filter was given
+ mask = trex_stats.ALL_STATS_OPTS
+
+ stats = self.cmd_stats(opts.ports, mask)
+
+ # print stats to screen
+ for stat_type, stat_data in stats.iteritems():
+ text_tables.print_table_with_header(stat_data.text_table, stat_type)
+
+ return RC_OK()
+
+ def cmd_streams_line(self, line):
+ '''Fetch streams statistics from TRex server by port\n'''
+ # define a parser
+ parser = parsing_opts.gen_parser(self,
+ "streams",
+ self.cmd_streams_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.STREAMS_MASK)#,
+ #parsing_opts.FULL_OUTPUT)
+
+ opts = parser.parse_args(line.split())
+
+ if opts is None:
+ return RC_ERR("bad command line parameters")
+
+ streams = self.cmd_streams(opts.ports, set(opts.streams))
+ if not streams:
+ # we got no streams running
+
+ self.logger.log(format_text("No streams found with desired filter.\n", "bold", "magenta"))
+ return RC_ERR("No streams found with desired filter.")
+ else:
+ # print stats to screen
+ for stream_hdr, port_streams_data in streams.iteritems():
+ text_tables.print_table_with_header(port_streams_data.text_table,
+ header= stream_hdr.split(":")[0] + ":",
+ untouched_header= stream_hdr.split(":")[1])
+ return RC_OK()
+
+
+
+
+ @__console
+ def cmd_validate_line (self, line):
+ '''validates port(s) stream configuration\n'''
+
+ parser = parsing_opts.gen_parser(self,
+ "validate",
+ self.cmd_validate_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return RC_ERR("bad command line paramters")
+
+ rc = self.cmd_validate(opts.ports)
+ return rc
+
+
+ def cmd_exit_line (self, line):
+ self.logger.log(format_text("Exiting\n", 'bold'))
+ # a way to exit
+ return RC_ERR("exit")
+
+
+ def cmd_wait_line (self, line):
+ '''wait for a period of time\n'''
+
+ parser = parsing_opts.gen_parser(self,
+ "wait",
+ self.cmd_wait_line.__doc__,
+ parsing_opts.DURATION)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return RC_ERR("bad command line parameters")
+
+ delay_sec = opts.duration if (opts.duration > 0) else 1
+
+ self.logger.log(format_text("Waiting for {0} seconds...\n".format(delay_sec), 'bold'))
+ time.sleep(delay_sec)
+
+ return RC_OK()
+
+ # run a script of commands
+ def run_script_file (self, filename):
+
+ self.logger.log(format_text("\nRunning script file '{0}'...".format(filename), 'bold'))
+
+ rc = self.cmd_connect()
+ if rc.bad():
+ return
+
+ with open(filename) as f:
+ script_lines = f.readlines()
+
+ cmd_table = {}
+
+ # register all the commands
+ cmd_table['start'] = self.cmd_start_line
+ cmd_table['stop'] = self.cmd_stop_line
+ cmd_table['reset'] = self.cmd_reset_line
+ cmd_table['wait'] = self.cmd_wait_line
+ cmd_table['exit'] = self.cmd_exit_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 = ""
+
+ self.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)
+ self.logger.log(format_text("unknown command '{0}'\n".format(cmd), 'bold'))
+ return False
+
+ rc = cmd_table[cmd](args)
+ if rc.bad():
+ return False
+
+ self.logger.log(format_text("\n[Done]", 'bold'))
+
+ return True
+
diff --git a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py
index c1afda26..3cc32b1d 100755
--- a/scripts/automation/trex_control_plane/client_utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/client_utils/parsing_opts.py
@@ -69,10 +69,19 @@ match_multiplier_help = """Multiplier should be passed in the following format:
will provide a percentage of the line rate. examples
: '-m 10', '-m 10kbps', '-m 10mpps', '-m 23%%' """
-def match_multiplier_common(val, strict_abs = True):
- # on strict absolute we do not allow +/-
- if strict_abs:
+# 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:
@@ -136,19 +145,32 @@ def match_multiplier_common(val, strict_abs = True):
else:
result['op'] = "abs"
+ if result['op'] != 'percentage':
+ result['value'] = result['value'] / divide_count
+
return result
else:
- raise argparse.ArgumentTypeError(match_multiplier_help)
+ return None
def match_multiplier(val):
'''match some val against multiplier shortcut inputs '''
- return match_multiplier_common(val, strict_abs = False)
+ result = decode_multiplier(val, allow_update = False)
+ if not result:
+ raise argparse.ArgumentTypeError(match_multiplier_help)
+
+ return val
+
def match_multiplier_strict(val):
'''match some val against multiplier shortcut inputs '''
- return match_multiplier_common(val, strict_abs = True)
+ result = decode_multiplier(val, allow_update = True)
+ if not result:
+ raise argparse.ArgumentTypeError(match_multiplier_help)
+
+ return val
+
def is_valid_file(filename):
if not os.path.isfile(filename):
@@ -287,7 +309,7 @@ class CCmdArgParser(argparse.ArgumentParser):
opts.ports = self.stateless_client.get_all_ports()
# so maybe we have ports configured
- elif (getattr(opts, "ports", None) == []):
+ 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))
diff --git a/scripts/automation/trex_control_plane/common/trex_types.py b/scripts/automation/trex_control_plane/common/trex_types.py
index 337f0a70..aada5bfc 100644
--- a/scripts/automation/trex_control_plane/common/trex_types.py
+++ b/scripts/automation/trex_control_plane/common/trex_types.py
@@ -14,16 +14,17 @@ class RpcResponseStatus(namedtuple('RpcResponseStatus', ['success', 'id', 'msg']
# simple class to represent complex return value
class RC():
- def __init__ (self, rc = None, data = None):
+ def __init__ (self, rc = None, data = None, is_warn = False):
self.rc_list = []
if (rc != None):
- tuple_rc = namedtuple('RC', ['rc', 'data'])
- self.rc_list.append(tuple_rc(rc, data))
+ tuple_rc = namedtuple('RC', ['rc', 'data', 'is_warn'])
+ self.rc_list.append(tuple_rc(rc, data, is_warn))
def __nonzero__ (self):
return self.good()
+
def add (self, rc):
self.rc_list += rc.rc_list
@@ -33,6 +34,9 @@ class RC():
def bad (self):
return not self.good()
+ def warn (self):
+ return any([x.is_warn for x in self.rc_list])
+
def data (self):
d = [x.data if x.rc else "" for x in self.rc_list]
return (d if len(d) != 1 else d[0])
@@ -83,3 +87,5 @@ def RC_OK(data = ""):
def RC_ERR (err):
return RC(False, err)
+def RC_WARN (warn):
+ return RC(True, warn, is_warn = True)
diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py
index 72cdcb0d..8f070959 100755
--- a/scripts/automation/trex_control_plane/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/console/trex_console.py
@@ -382,7 +382,7 @@ class TRexConsole(TRexGeneralCmd):
def do_start(self, line):
'''Start selected traffic in specified port(s) on TRex\n'''
- self.stateless_client.cmd_start_line(line)
+ self.stateless_client.start_line(line)
@@ -395,7 +395,7 @@ class TRexConsole(TRexGeneralCmd):
def do_stop(self, line):
'''stops port(s) transmitting traffic\n'''
- self.stateless_client.cmd_stop_line(line)
+ self.stateless_client.stop_line(line)
def help_stop(self):
self.do_stop("-h")
@@ -405,7 +405,7 @@ class TRexConsole(TRexGeneralCmd):
def do_update(self, line):
'''update speed of port(s)currently transmitting traffic\n'''
- self.stateless_client.cmd_update_line(line)
+ self.stateless_client.update_line(line)
def help_update (self):
self.do_update("-h")
@@ -415,14 +415,14 @@ class TRexConsole(TRexGeneralCmd):
def do_pause(self, line):
'''pause port(s) transmitting traffic\n'''
- self.stateless_client.cmd_pause_line(line)
+ 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.cmd_resume_line(line)
+ self.stateless_client.resume_line(line)
@@ -444,7 +444,7 @@ class TRexConsole(TRexGeneralCmd):
@verify_connected
def do_stats(self, line):
'''Fetch statistics from TRex server by port\n'''
- self.stateless_client.cmd_stats_line(line)
+ self.stateless_client.stats_line(line)
def help_stats(self):
@@ -453,7 +453,7 @@ class TRexConsole(TRexGeneralCmd):
@verify_connected
def do_streams(self, line):
'''Fetch statistics from TRex server by port\n'''
- self.stateless_client.cmd_streams_line(line)
+ self.stateless_client.show_streams_line(line)
def help_streams(self):
@@ -462,7 +462,7 @@ class TRexConsole(TRexGeneralCmd):
@verify_connected
def do_clear(self, line):
'''Clear cached local statistics\n'''
- self.stateless_client.cmd_clear_line(line)
+ self.stateless_client.clear_stats_line(line)
def help_clear(self):
@@ -710,7 +710,7 @@ def main():
print "\n\n*** Caught Ctrl + C... Exiting...\n\n"
finally:
- stateless_client.teardown()
+ stateless_client.teardown(stop_traffic = False)
if __name__ == '__main__':