summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-01-27 10:47:40 -0500
committerimarom <imarom@cisco.com>2016-01-27 11:01:44 -0500
commit7351ddb52a2162fc2ac303c687a2ed50cbb0843e (patch)
treedfba74936e5f671f7fa764e16efc6735d8defe43 /scripts/automation/trex_control_plane
parent73d9481b063d851ba16dc853f7309e3080970408 (diff)
API: example for bi-directional flows
Diffstat (limited to 'scripts/automation/trex_control_plane')
-rwxr-xr-xscripts/automation/trex_control_plane/client/trex_stateless_client.py19
-rwxr-xr-xscripts/automation/trex_control_plane/client_utils/packet_builder.py45
-rwxr-xr-xscripts/automation/trex_control_plane/common/trex_streams.py197
-rwxr-xr-xscripts/automation/trex_control_plane/console/trex_console.py27
4 files changed, 185 insertions, 103 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 65e69938..3d4dbc93 100755
--- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py
+++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
@@ -110,6 +110,10 @@ class LoggerApi(object):
# default logger - to stdout
class DefaultLogger(LoggerApi):
+
+ def __init__ (self):
+ super(DefaultLogger, self).__init__()
+
def write (self, msg, newline = True):
if newline:
print msg
@@ -972,17 +976,22 @@ class STLClient(object):
Sets verbose level
:parameters:
- level : enum
- LoggerApi.VERBOSE_QUIET
- LoggerApi.VERBOSE_NORMAL
- LoggerApi.VERBOSE_HIGH
+ level : str
+ "high"
+ "low"
+ "normal"
:raises:
None
"""
def set_verbose (self, level):
- self.logger.set_verbose(level)
+ modes = {'low' : LoggerApi.VERBOSE_QUIET, 'normal': LoggerApi.VERBOSE_REGULAR, 'high': LoggerApi.VERBOSE_HIGH}
+
+ if not level in modes.keys():
+ raise STLArgumentError('level', level)
+
+ self.logger.set_verbose(modes[level])
"""
diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py
index c7d3308e..e2a1b116 100755
--- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py
+++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py
@@ -36,6 +36,9 @@ class CTRexPktBuilder(object):
self.vm = CTRexPktBuilder.CTRexVM()
self.metadata = ""
+ def clone (self):
+ return copy.deepcopy(self)
+
def add_pkt_layer(self, layer_name, pkt_layer):
"""
This method adds additional header to the already existing packet
@@ -331,10 +334,8 @@ class CTRexPktBuilder(object):
def load_packet_from_byte_list(self, byte_list):
- # convert byte array into buffer
- byte_list = [ord(c) for c in base64.b64decode(byte_list)]
- buf = struct.pack('B'*len(byte_list), *byte_list)
+ buf = base64.b64decode(byte_list)
# thn, load it based on dpkt parsing
self.load_packet(dpkt.ethernet.Ethernet(buf))
@@ -381,11 +382,15 @@ class CTRexPktBuilder(object):
layer = self._pkt_by_hdr.get(layer_name)
return copy.copy(layer) if layer else None
+
# VM access methods
def set_vm_ip_range(self, ip_layer_name, ip_field,
- ip_init, ip_start, ip_end, add_value,
- operation, is_big_endian=False, val_size=4,
- ip_type="ipv4", add_checksum_inst=True):
+ ip_start, ip_end, operation,
+ ip_init = None, add_value = 0,
+ is_big_endian=True, val_size=4,
+ ip_type="ipv4", add_checksum_inst=True,
+ split = False):
+
if ip_field not in ["src", "dst"]:
raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses")
# set differences between IPv4 and IPv6
@@ -400,11 +405,18 @@ class CTRexPktBuilder(object):
self._verify_layer_prop(ip_layer_name, ip_class)
trim_size = ip_addr_size*2
- init_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16)
start_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16)
end_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16)
+
+ if ip_init == None:
+ init_val = start_val
+ else:
+ init_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16)
+
+
# All validations are done, start adding VM instructions
flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field)
+
hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field, ip_addr_size)
self.vm.add_flow_man_inst(flow_var_name, size=ip_addr_size, operation=operation,
init_value=init_val,
@@ -416,6 +428,10 @@ class CTRexPktBuilder(object):
if ip_type == "ipv4" and add_checksum_inst:
self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset)
+ if split:
+ self.vm.set_split_by_var(flow_var_name)
+
+
def set_vm_eth_range(self, eth_layer_name, eth_field,
mac_init, mac_start, mac_end, add_value,
operation, val_size=4, is_big_endian=False):
@@ -440,7 +456,7 @@ class CTRexPktBuilder(object):
def set_vm_custom_range(self, layer_name, hdr_field,
init_val, start_val, end_val, add_val, val_size,
- operation, is_big_endian=False, range_name="",
+ operation, is_big_endian=True, range_name="",
add_checksum_inst=True):
# verify input validity for init/start/end values
for val in [init_val, start_val, end_val]:
@@ -835,6 +851,13 @@ class CTRexPktBuilder(object):
else:
raise CTRexPktBuilder.VMVarNameExistsError(flow_obj.name)
+ def set_split_by_var (self, var_name):
+ if var_name not in self.vm_variables:
+ raise KeyError("cannot set split by var to an unknown VM var ('{0}')".
+ format(var_name))
+
+ self.split_by_var = var_name
+
def dump(self):
"""
dumps a VM variables (instructions) and split_by_var into a dict data structure.
@@ -958,9 +981,9 @@ class CTRexPktBuilder(object):
"size": self.size,
"op": self.operation,
# "split_by_core": self.split_by_core,
- "init_value": str(self.init_value),
- "min_value": str(self.min_value),
- "max_value": str(self.max_value)}
+ "init_value": self.init_value,
+ "min_value": self.min_value,
+ "max_value": self.max_value}
class CTRexVMChecksumInst(CVMAbstractInstruction):
diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py
index 90cb812d..0d77b457 100755
--- a/scripts/automation/trex_control_plane/common/trex_streams.py
+++ b/scripts/automation/trex_control_plane/common/trex_streams.py
@@ -10,6 +10,8 @@ import struct
import copy
import os
import random
+import yaml
+import base64
StreamPack = namedtuple('StreamPack', ['stream_id', 'stream'])
LoadedStreamList = namedtuple('LoadedStreamList', ['name', 'loaded', 'compiled'])
@@ -208,13 +210,17 @@ class CStream(object):
"should not be supplied")
else:
binary = kwargs[k]["binary"]
- if isinstance(binary, list):
- setattr(self, k, kwargs[k])
+ if isinstance(binary, str):
+
# TODO: load to _pkt_bld_obj also when passed as byte array!
- elif isinstance(binary, str) and binary.endswith(".pcap"):
- self._pkt_bld_obj.load_packet_from_pcap(binary)
- self._pkt_bld_obj.metadata = kwargs[k]["meta"]
- self.packet = self._pkt_bld_obj.dump_pkt()
+ if binary.endswith(".pcap"):
+ self._pkt_bld_obj.load_packet_from_pcap(binary)
+ self._pkt_bld_obj.metadata = kwargs[k]["meta"]
+ self.packet = self._pkt_bld_obj.dump_pkt()
+ else:
+ self.packet = {}
+ self.packet['binary'] = binary
+ self.packet['meta'] = ""
else:
raise ValueError("Packet binary attribute has been loaded with unsupported value."
@@ -329,11 +335,82 @@ class CStreamsDB(object):
########################### Simple Streams ###########################
from trex_stl_exceptions import *
+# base class for TX mode
+class STLTXMode(object):
+ def __init__ (self):
+ self.fields = {}
+
+ def to_json (self):
+ return self.fields
+
+
+# continuous mode
+class STLTXCont(STLTXMode):
+
+ def __init__ (self, pps = 1):
+
+ if not isinstance(pps, (int, float)):
+ raise STLArgumentError('pps', pps)
+
+ super(STLTXCont, self).__init__()
+
+ self.fields['type'] = 'continuous'
+ self.fields['pps'] = pps
+
+
+# single burst mode
+class STLTXSingleBurst(STLTXMode):
+
+ def __init__ (self, pps = 1, total_pkts = 1):
+
+ if not isinstance(pps, (int, float)):
+ raise STLArgumentError('pps', pps)
+
+ if not isinstance(total_pkts, int):
+ raise STLArgumentError('total_pkts', total_pkts)
+
+ super(STLTXSingleBurst, self).__init__()
+
+ self.fields['type'] = 'single_burst'
+ self.fields['pps'] = pps
+ self.fields['total_pkts'] = total_pkts
+
+
+# multi burst mode
+class STLTXMultiBurst(STLTXMode):
+
+ def __init__ (self,
+ pps = 1,
+ pkts_per_burst = 1,
+ ibg = 0.0,
+ count = 1):
+
+ if not isinstance(pps, (int, float)):
+ raise STLArgumentError('pps', pps)
+
+ if not isinstance(pkts_per_burst, int):
+ raise STLArgumentError('pkts_per_burst', pkts_per_burst)
+
+ if not isinstance(ibg, (int, float)):
+ raise STLArgumentError('ibg', ibg)
+
+ if not isinstance(count, int):
+ raise STLArgumentError('count', count)
+
+ super(STLTXMultiBurst, self).__init__()
+
+ self.fields['type'] = 'multi_burst'
+ self.fields['pps'] = pps
+ self.fields['pkts_per_burst'] = pkts_per_burst
+ self.fields['ibg'] = ibg
+ self.fields['count'] = count
+
+
class STLStream(object):
def __init__ (self,
packet,
- pps = 1,
+ mode = STLTXCont(1),
enabled = True,
self_start = True,
isg = 0.0,
@@ -341,8 +418,8 @@ class STLStream(object):
next_stream_id = -1):
# type checking
- if not isinstance(pps, (int, float)):
- raise STLArgumentError('pps', pps)
+ if not isinstance(mode, STLTXMode):
+ raise STLArgumentError('mode', mode)
if not isinstance(packet, CTRexPktBuilder):
raise STLArgumentError('packet', packet)
@@ -356,6 +433,9 @@ class STLStream(object):
if not isinstance(isg, (int, float)):
raise STLArgumentError('isg', isg)
+ if (type(mode) == STLTXCont) and (next_stream_id != -1):
+ raise STLError("continuous stream cannot have a next stream ID")
+
# use a random 31 bit for ID
self.stream_id = random.getrandbits(31)
@@ -369,8 +449,7 @@ class STLStream(object):
self.fields['next_stream_id'] = next_stream_id
# mode
- self.fields['mode'] = {}
- self.fields['mode']['pps'] = pps
+ self.fields['mode'] = mode.to_json()
# packet and VM
self.fields['packet'] = packet.dump_pkt()
@@ -390,88 +469,44 @@ class STLStream(object):
def get_id (self):
return self.stream_id
+ @staticmethod
+ def dump_to_yaml (yaml_file, stream_list):
-# continuous stream
-class STLContStream(STLStream):
- def __init__ (self,
- packet,
- pps = 1,
- enabled = True,
- self_start = True,
- isg = 0.0,
- rx_stats = None):
-
- super(STLContStream, self).__init__(packet,
- pps,
- enabled,
- self_start,
- isg,
- rx_stats,
- next_stream_id = -1)
-
- # type
- self.fields['mode']['type'] = "continuous"
-
-
-
-# single burst
-class STLSingleBurstStream(STLStream):
- def __init__ (self,
- packet,
- total_pkts,
- pps = 1,
- enabled = True,
- self_start = True,
- isg = 0.0,
- rx_stats = None,
- next_stream_id = -1):
-
+ # type check
+ if isinstance(stream_list, STLStream):
+ stream_list = [stream_list]
- if not isinstance(total_pkts, int):
- raise STLArgumentError('total_pkts', total_pkts)
+ if not all([isinstance(stream, STLStream) for stream in stream_list]):
+ raise STLArgumentError('stream_list', stream_list)
- super(STLSingleBurstStream, self).__init__(packet,
- pps,
- enabled,
- self_start,
- isg,
- rx_stats,
- next_stream_id)
- self.fields['mode']['type'] = "single_burst"
- self.fields['mode']['total_pkts'] = total_pkts
+ names = {}
+ for i, stream in enumerate(stream_list):
+ names[stream.get_id()] = "stream-{0}".format(i)
+ yaml_lst = []
+ for stream in stream_list:
-# multi burst stream
-class STLMultiBurstStream(STLStream):
- def __init__ (self,
- packet,
- pkts_per_burst = 1,
- pps = 1,
- ibg = 0.0,
- count = 1,
- enabled = True,
- self_start = True,
- isg = 0.0,
- rx_stats = None,
- next_stream_id = -1):
+ fields = dict(stream.fields)
+ # handle the next stream id
+ if fields['next_stream_id'] == -1:
+ del fields['next_stream_id']
- if not isinstance(pkts_per_burst, int):
- raise STLArgumentError('pkts_per_burst', pkts_per_burst)
+ else:
+ if not stream.get_id() in names:
+ raise STLError('broken dependencies in stream list')
- if not isinstance(count, int):
- raise STLArgumentError('count', count)
+ fields['next_stream'] = names[stream.get_id()]
- if not isinstance(ibg, (int, float)):
- raise STLArgumentError('ibg', ibg)
+ # add to list
+ yaml_lst.append({'name': names[stream.get_id()], 'stream': fields})
- super(STLMultiBurstStream, self).__init__(packet, enabled, self_start, isg, rx_stats)
+ # write to file
+ x = yaml.dump(yaml_lst, default_flow_style=False)
+ with open(yaml_file, 'w') as f:
+ f.write(x)
- self.fields['mode']['type'] = "single_burst"
- self.fields['mode']['pkts_per_burst'] = pkts_per_burst
- self.fields['mode']['ibg'] = ibg
- self.fields['mode']['count'] = count
# REMOVE ME when can - convert from stream pack to a simple stream
diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py
index 1defc6b2..6a4af350 100755
--- a/scripts/automation/trex_control_plane/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/console/trex_console.py
@@ -221,7 +221,7 @@ class TRexConsole(TRexGeneralCmd):
def get_console_identifier(self):
- return "{context}_{server}".format(context=self.__class__.__name__,
+ return "{context}_{server}".format(context=get_current_user(),
server=self.stateless_client.get_connection_info()['server'])
def register_main_console_methods(self):
@@ -305,12 +305,12 @@ class TRexConsole(TRexGeneralCmd):
elif line == "on":
self.verbose = True
- self.stateless_client.set_verbose(self.stateless_client.logger.VERBOSE_HIGH)
+ 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(self.stateless_client.logger.VERBOSE_REGULAR)
+ self.stateless_client.set_verbose("normal")
print format_text("\nverbose set to off\n", 'green', 'bold')
else:
@@ -515,7 +515,7 @@ class TRexConsole(TRexGeneralCmd):
info = self.stateless_client.get_connection_info()
- exe = './trex-console -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port'])
+ 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)
@@ -686,6 +686,13 @@ def setParserOptions():
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",
@@ -698,6 +705,14 @@ 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
@@ -742,9 +757,9 @@ def main():
console = TRexConsole(stateless_client, options.verbose)
logger.prompt_redraw = console.prompt_redraw
+ # TUI
if options.tui:
- set_window_always_on_top('trex_tui')
- console.do_tui("")
+ console.do_tui("-x" if options.xtui else "")
else:
console.start()