summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHanoh Haim <hhaim@cisco.com>2016-01-27 13:03:51 +0200
committerHanoh Haim <hhaim@cisco.com>2016-01-27 13:03:51 +0200
commitc581c5bb6314b5f80d315c23354f8866ed2a71ff (patch)
tree8e1e4e041e027f3c14a542730ffd688882c69522
parent52f599e870f0a3d98cebdf4d49607a6f50cf3380 (diff)
parent1e69b27f272fef2e3124693b865521a4927418f5 (diff)
Merge to latest
-rw-r--r--api/stl/examples/stl_simple_burst.py76
-rw-r--r--api/stl/trex_stl_api.py18
-rwxr-xr-xlinux/ws_main.py26
-rwxr-xr-xlinux_dpdk/ws_main.py1
-rwxr-xr-xscripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py2
-rw-r--r--scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py1
-rw-r--r--scripts/automation/trex_control_plane/client/trex_port.py61
-rwxr-xr-xscripts/automation/trex_control_plane/client/trex_stateless_client.py724
-rw-r--r--scripts/automation/trex_control_plane/client/trex_stateless_sim.py2
-rwxr-xr-xscripts/automation/trex_control_plane/client_utils/jsonrpc_client.py7
-rwxr-xr-xscripts/automation/trex_control_plane/client_utils/packet_builder.py10
-rwxr-xr-xscripts/automation/trex_control_plane/common/trex_stats.py3
-rw-r--r--scripts/automation/trex_control_plane/common/trex_stl_exceptions.py53
-rwxr-xr-xscripts/automation/trex_control_plane/common/trex_streams.py162
-rwxr-xr-xscripts/automation/trex_control_plane/console/trex_console.py12
-rw-r--r--src/common/base64.cpp123
-rw-r--r--src/common/base64.h9
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_stream.cpp32
-rw-r--r--src/stateless/cp/trex_stream.h2
-rw-r--r--src/stub/trex_stateless_stub.cpp23
-rw-r--r--src/stub/zmq_stub.c81
21 files changed, 995 insertions, 433 deletions
diff --git a/api/stl/examples/stl_simple_burst.py b/api/stl/examples/stl_simple_burst.py
index 7efb574a..ff159289 100644
--- a/api/stl/examples/stl_simple_burst.py
+++ b/api/stl/examples/stl_simple_burst.py
@@ -1,53 +1,83 @@
import sys
sys.path.insert(0, "../")
-import trex_stl_api
-
-from trex_stl_api import STLClient, STLError
-
+from trex_stl_api import *
+import dpkt
import time
-# define a simple burst test
def simple_burst ():
-
+
+ # build a simple packet
+
+ pkt_bld = STLPktBuilder()
+ pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet())
+ # set Ethernet layer attributes
+ pkt_bld.set_eth_layer_addr("l2", "src", "00:15:17:a7:75:a3")
+ pkt_bld.set_eth_layer_addr("l2", "dst", "e0:5f:b9:69:e9:22")
+ pkt_bld.set_layer_attr("l2", "type", dpkt.ethernet.ETH_TYPE_IP)
+ # set IP layer attributes
+ pkt_bld.add_pkt_layer("l3_ip", dpkt.ip.IP())
+ pkt_bld.set_ip_layer_addr("l3_ip", "src", "21.0.0.2")
+ pkt_bld.set_ip_layer_addr("l3_ip", "dst", "22.0.0.12")
+ pkt_bld.set_layer_attr("l3_ip", "p", dpkt.ip.IP_PROTO_TCP)
+ # set TCP layer attributes
+ pkt_bld.add_pkt_layer("l4_tcp", dpkt.tcp.TCP())
+ pkt_bld.set_layer_attr("l4_tcp", "sport", 13311)
+ pkt_bld.set_layer_attr("l4_tcp", "dport", 80)
+ pkt_bld.set_layer_attr("l4_tcp", "flags", 0)
+ pkt_bld.set_layer_attr("l4_tcp", "win", 32768)
+ pkt_bld.set_layer_attr("l4_tcp", "seq", 0)
+ #pkt_bld.set_pkt_payload("abcdefgh")
+ pkt_bld.set_layer_attr("l3_ip", "len", len(pkt_bld.get_layer('l3_ip')))
+
+
+ # create client
+ c = STLClient()
passed = True
try:
- with STLClient() as c:
+ #c.logger.set_verbose(c.logger.VERBOSE_NORMAL)
- # activate this for some logging information
- #c.logger.set_verbose(c.logger.VERBOSE_REGULAR)
+ # create two bursts and link them
+ s1 = STLSingleBurstStream(packet = pkt_bld, total_pkts = 5000)
+ s2 = STLSingleBurstStream(packet = pkt_bld, total_pkts = 3000, next_stream_id = s1.get_id())
- # repeat for 5 times
- for i in xrange(1, 6):
+ # connect to server
+ c.connect()
- # read the stats before
- before_ipackets = c.get_stats()['total']['ipackets']
+ # prepare our ports
+ c.reset(ports = [0, 1])
- # inject burst profile on two ports and block until done
- c.start(profiles = '../profiles/burst.yaml', ports = [0, 1], mult = "1gbps")
- c.wait_on_traffic(ports = [0, 1])
+ # add both streams to ports
+ c.add_streams([s1, s2], ports = [0, 1])
- after_ipackets = c.get_stats()['total']['ipackets']
+ # run 5 times
+ for i in xrange(1, 6):
+ c.clear_stats()
+ c.start(ports = [0, 1], mult = "1gbps")
+ c.wait_on_traffic(ports = [0, 1])
- print "Test iteration {0} - Packets Received: {1} ".format(i, (after_ipackets - before_ipackets))
+ stats = c.get_stats()
+ ipackets = stats['total']['ipackets']
- # we have 600 packets in the burst and two ports
- if (after_ipackets - before_ipackets) != (600 * 2):
- passed = False
+ print "Test iteration {0} - Packets Received: {1} ".format(i, ipackets)
+ # (5000 + 3000) * 2 ports = 16,000
+ if (ipackets != (16000)):
+ passed = False
- # error handling
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/api/stl/trex_stl_api.py b/api/stl/trex_stl_api.py
index aad39916..63a0963b 100644
--- a/api/stl/trex_stl_api.py
+++ b/api/stl/trex_stl_api.py
@@ -6,12 +6,18 @@ import time
# update the import path to include the stateless client
root_path = os.path.dirname(os.path.abspath(__file__))
-sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client/'))
-sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client_utils/'))
-sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client_utils/'))
+sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/'))
# aliasing
-import trex_stateless_client
-STLClient = trex_stateless_client.STLClient
-STLError = trex_stateless_client.STLError
+import common.trex_streams
+from client_utils.packet_builder import CTRexPktBuilder
+import common.trex_stl_exceptions
+import client.trex_stateless_client
+
+STLClient = client.trex_stateless_client.STLClient
+STLError = common.trex_stl_exceptions.STLError
+STLContStream = common.trex_streams.STLContStream
+STLSingleBurstStream = common.trex_streams.STLSingleBurstStream
+STLMultiBurstStream = common.trex_streams.STLMultiBurstStream
+STLPktBuilder = CTRexPktBuilder
diff --git a/linux/ws_main.py b/linux/ws_main.py
index a41fab1e..22c52eea 100755
--- a/linux/ws_main.py
+++ b/linux/ws_main.py
@@ -133,7 +133,8 @@ cmn_src = SrcGroup(dir='src/common',
'basic_utils.cpp',
'captureFile.cpp',
'erf.cpp',
- 'pcap.cpp'
+ 'pcap.cpp',
+ 'base64.cpp'
]);
@@ -223,8 +224,13 @@ yaml_src = SrcGroup(dir='external_libs/yaml-cpp/src/',
'tag.cpp']);
+# stubs
+stubs = SrcGroup(dir='/src/stub/',
+ src_list=['zmq_stub.c'])
+
rpc_server_mock = SrcGroups([
main_src,
+ stubs,
cmn_src,
rpc_server_src,
rpc_server_mock_src,
@@ -234,25 +240,17 @@ rpc_server_mock = SrcGroups([
net_src,
])
-# REMOVE ME - need to decide if stateless is part of bp sim or not
-bp_hack_for_compile = SrcGroup(dir='/src/stub/',
- src_list=['trex_stateless_stub.cpp'
- ])
-
bp =SrcGroups([
bp_sim_main,
bp_sim_gtest,
main_src,
cmn_src ,
-
+ stubs,
net_src ,
yaml_src,
json_src,
stateless_src,
rpc_server_src
- #rpc_server_mock_src,
-
- #bp_hack_for_compile,
]);
@@ -406,15 +404,15 @@ class build_option:
build_types = [
- build_option(name = "bp-sim", src = bp, use = ['zmq'],debug_mode= DEBUG_, platform = PLATFORM_64, is_pie = False,
+ build_option(name = "bp-sim", src = bp, use = [''],debug_mode= DEBUG_, platform = PLATFORM_64, is_pie = False,
flags = ['-Wall', '-Werror', '-Wno-sign-compare', '-Wno-strict-aliasing'],
rpath = ['.']),
- build_option(name = "bp-sim", src = bp, use = ['zmq'],debug_mode= RELEASE_,platform = PLATFORM_64, is_pie = False,
+ build_option(name = "bp-sim", src = bp, use = [''],debug_mode= RELEASE_,platform = PLATFORM_64, is_pie = False,
flags = ['-Wall', '-Werror', '-Wno-sign-compare', '-Wno-strict-aliasing'],
rpath = ['.']),
- build_option(name = "mock-rpc-server", use = ['zmq'], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False,
+ build_option(name = "mock-rpc-server", use = [''], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False,
flags = ['-DTREX_RPC_MOCK_SERVER', '-Wall', '-Werror', '-Wno-sign-compare'],
rpath = ['.']),
]
@@ -422,8 +420,6 @@ build_types = [
def build_prog (bld, build_obj):
- zmq_lib_path='external_libs/zmq/'
- bld.read_shlib( name='zmq' , paths=[top + zmq_lib_path] )
bld.program(features='cxx cxxprogram',
includes =includes_path,
diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py
index e2c93aa3..51298ba6 100755
--- a/linux_dpdk/ws_main.py
+++ b/linux_dpdk/ws_main.py
@@ -124,6 +124,7 @@ cmn_src = SrcGroup(dir='src/common',
'captureFile.cpp',
'erf.cpp',
'pcap.cpp',
+ 'base64.cpp'
]);
net_src = SrcGroup(dir='src/common/Network/Packet',
diff --git a/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py b/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py
index b8831c04..c2946798 100755
--- a/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py
+++ b/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py
@@ -251,7 +251,7 @@ class CTRexPktBuilder_Test(pkt_bld_general_test.CGeneralPktBld_Test):
self.print_packet(self.pkt_bld.get_packet())
assert_equal(self.pkt_bld.dump_pkt(), {
- 'binary': [224, 95, 185, 105, 233, 34, 0, 21, 23, 167, 117, 163, 8, 0, 69, 0, 0, 39, 0, 0, 0, 0, 64, 1, 79, 201, 21, 0, 0, 2, 22, 0, 0, 12, 8, 0, 217, 134, 95, 208, 44, 218, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100],
+ 'binary': '4F+5aekiABUXp3WjCABFAAAnAAAAAEABT8kVAAACFgAADAgA2YZf0CzaaGVsbG8gd29ybGQ=',
'meta': '',
})
diff --git a/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py b/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py
index ab4a8a87..125866ba 100644
--- a/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py
+++ b/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py
@@ -13,7 +13,6 @@ import os
import subprocess
# should be set to run explicitly, not as part of all regression tests
-@nottest
class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
def setUp (self):
self.test_path = os.path.abspath(os.getcwd())
diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py
index dcb03da3..c8147faf 100644
--- a/scripts/automation/trex_control_plane/client/trex_port.py
+++ b/scripts/automation/trex_control_plane/client/trex_port.py
@@ -3,6 +3,7 @@ from collections import namedtuple, OrderedDict
from common.trex_types import *
from common import trex_stats
from client_utils import packet_builder
+
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
########## utlity ############
@@ -48,7 +49,6 @@ class Port(object):
self.streams = {}
self.profile = None
self.session_id = session_id
- self.loaded_stream_pack = None
self.port_stats = trex_stats.CPortStats(self)
@@ -138,63 +138,44 @@ class Port(object):
# operations on port can be done on state idle or state streams
return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS))
- # add stream to the port
- def add_stream (self, stream_id, stream_obj):
-
- if not self.is_port_writable():
- return self.err("Please stop port before attempting to add streams")
+ # add streams
+ def add_streams (self, streams_list):
- params = {"handler": self.handler,
- "port_id": self.port_id,
- "stream_id": stream_id,
- "stream": stream_obj}
-
- rc = self.transmit("add_stream", params)
- if rc.bad():
- return self.err(rc.err())
-
- # add the stream
- self.streams[stream_id] = StreamOnPort(stream_obj, Port._generate_stream_metadata(stream_id, stream_obj))
-
- # the only valid state now
- self.state = self.STATE_STREAMS
+ if not self.is_acquired():
+ return self.err("port is not owned")
- return self.ok()
+ if not self.is_port_writable():
+ return self.err("Please stop port before attempting to add streams")
- # add multiple streams
- def add_streams (self, LoadedStreamList_obj):
batch = []
+ for stream in (streams_list if isinstance(streams_list, list) else [streams_list]):
- self.loaded_stream_pack = LoadedStreamList_obj
- compiled_stream_list = LoadedStreamList_obj.compiled
-
- for stream_pack in compiled_stream_list:
params = {"handler": self.handler,
"port_id": self.port_id,
- "stream_id": stream_pack.stream_id,
- "stream": stream_pack.stream}
+ "stream_id": stream.get_id(),
+ "stream": stream.to_json()}
cmd = RpcCmdData('add_stream', params)
batch.append(cmd)
+ # meta data for show streams
+ self.streams[stream.get_id()] = StreamOnPort(stream.to_json(),
+ Port._generate_stream_metadata(stream.get_id(), stream.to_json()))
+
rc = self.transmit_batch(batch)
- if rc.bad():
+ if not rc:
return self.err(rc.err())
- # validate that every action succeeded
-
- # add the stream
- for stream_pack in compiled_stream_list:
- self.streams[stream_pack.stream_id] = StreamOnPort(stream_pack.stream,
- Port._generate_stream_metadata(stream_pack.stream_id,
- stream_pack.stream))
+
# the only valid state now
self.state = self.STATE_STREAMS
return self.ok()
+
+
# remove stream from port
def remove_stream (self, stream_id):
@@ -460,10 +441,6 @@ class Port(object):
def generate_loaded_streams_sum(self, stream_id_list):
if self.state == self.STATE_DOWN or self.state == self.STATE_STREAMS:
return {}
- elif self.loaded_stream_pack is None:
- # avoid crashing when sync with remote server isn't operational
- # TODO: MAKE SURE TO HANDLE THIS CASE FOR BETTER UX
- return {}
streams_data = {}
if not stream_id_list:
@@ -476,7 +453,7 @@ class Port(object):
if stream_id in self.streams}
- return {"referring_file" : self.loaded_stream_pack.name,
+ return {"referring_file" : "",
"streams" : streams_data}
@staticmethod
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 32618a05..65e69938 100755
--- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py
+++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
@@ -23,56 +23,9 @@ import re
import random
from trex_port import Port
from common.trex_types import *
+from common.trex_stl_exceptions import *
from trex_async_client import CTRexAsyncClient
-# 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()
- fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
-
-
- s = "\n******\n"
- 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)
-
-
-# port state error
-class STLPortStateError(STLError):
- def __init__ (self, port, op, state):
- self.msg = "Operation '{0}' on port(s) '{1}' is not valid while port(s) '{2}'".format(op, port, 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)
-
-# raised when timeout occurs
-class STLTimeoutError(STLError):
- def __init__ (self, timeout):
- self.msg = "Timeout: operation took more than '{0}' seconds".format(timeout)
-
-
############################ logger #############################
############################ #############################
@@ -541,14 +494,14 @@ class STLClient(object):
return rc
- def __add_stream(self, stream_id, stream_obj, port_id_list = None):
+ def __add_streams(self, stream_list, port_id_list = None):
port_id_list = self.__ports(port_id_list)
rc = RC()
for port_id in port_id_list:
- rc.add(self.ports[port_id].add_stream(stream_id, stream_obj))
+ rc.add(self.ports[port_id].add_streams(stream_list))
return rc
@@ -605,7 +558,7 @@ class STLClient(object):
return self.ports[port_id].get_stream_id_list()
- def __start_traffic (self, multiplier, duration, port_id_list = None, force = False):
+ def __start (self, multiplier, duration, port_id_list = None, force = False):
port_id_list = self.__ports(port_id_list)
@@ -617,7 +570,7 @@ class STLClient(object):
return rc
- def __resume_traffic (self, port_id_list = None, force = False):
+ def __resume (self, port_id_list = None, force = False):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -627,7 +580,7 @@ class STLClient(object):
return rc
- def __pause_traffic (self, port_id_list = None, force = False):
+ def __pause (self, port_id_list = None, force = False):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -638,7 +591,7 @@ class STLClient(object):
return rc
- def __stop_traffic (self, port_id_list = None, force = False):
+ def __stop (self, port_id_list = None, force = False):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -649,7 +602,7 @@ class STLClient(object):
return rc
- def __update_traffic (self, mult, port_id_list = None, force = False):
+ def __update (self, mult, port_id_list = None, force = False):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -660,7 +613,7 @@ class STLClient(object):
return rc
- def __validate_traffic (self, port_id_list = None):
+ def __validate (self, port_id_list = None):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -672,7 +625,6 @@ class STLClient(object):
-
# connect to server
def __connect(self):
@@ -684,26 +636,19 @@ class STLClient(object):
self.connected = False
# connect sync channel
- self.logger.pre_cmd("connecting to RPC server on {0}:{1}".format(self.connection_info['server'], self.connection_info['sync_port']))
+ self.logger.pre_cmd("Connecting to RPC server on {0}:{1}".format(self.connection_info['server'], self.connection_info['sync_port']))
rc = self.comm_link.connect()
self.logger.post_cmd(rc)
if not rc:
return rc
- # connect async channel
- self.logger.pre_cmd("connecting to publisher server on {0}:{1}".format(self.connection_info['server'], self.connection_info['async_port']))
- rc = self.async_client.connect()
- self.logger.post_cmd(rc)
-
- if not rc:
- return rc
-
# version
rc = self._transmit("get_version")
if not rc:
return rc
+
self.server_version = rc.data()
self.global_stats.server_version = rc.data()
@@ -740,7 +685,16 @@ class STLClient(object):
return rc
+ # connect async channel
+ self.logger.pre_cmd("connecting to publisher server on {0}:{1}".format(self.connection_info['server'], self.connection_info['async_port']))
+ rc = self.async_client.connect()
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ return rc
+
self.connected = True
+
return RC_OK()
@@ -758,101 +712,6 @@ class STLClient(object):
return RC_OK()
- # ping server
- def __ping (self):
- return self._transmit("ping")
-
-
- # start command
- def __start (self, port_id_list, stream_list, mult, force, duration, dry):
-
-
- self.logger.pre_cmd("Removing all streams from port(s) {0}:".format(port_id_list))
- rc = self.__remove_all_streams(port_id_list)
- self.logger.post_cmd(rc)
-
- if not rc:
- return rc
-
-
- self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(stream_list.compiled), port_id_list))
- rc = self.__add_stream_pack(stream_list, port_id_list)
- self.logger.post_cmd(rc)
-
- if not rc:
- return rc
-
- # when not on dry - start the traffic , otherwise validate only
- if not dry:
-
- self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(port_id_list))
- rc = self.__start_traffic(mult, duration, port_id_list, force)
- self.logger.post_cmd(rc)
-
- return rc
- else:
-
- rc = self.__validate(port_id_list)
- if rc.bad():
- return rc
-
- # show a profile on one port for illustration
- self.ports[port_id_list[0]].print_profile(mult, duration)
-
- return rc
-
-
- # stop cmd
- def __stop (self, port_id_list):
-
- self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(port_id_list))
- rc = self.__stop_traffic(port_id_list)
- self.logger.post_cmd(rc)
-
- if not rc:
- return rc
-
- return RC_OK()
-
- #update cmd
- def __update (self, port_id_list, mult, force):
-
- self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(port_id_list))
- rc = self.__update_traffic(mult, port_id_list, force)
- self.logger.post_cmd(rc)
-
- return rc
-
-
- # pause cmd
- def __pause (self, port_id_list):
-
- self.logger.pre_cmd("Pausing traffic on port(s) {0}:".format(port_id_list))
- rc = self.__pause_traffic(port_id_list)
- self.logger.post_cmd(rc)
-
- return rc
-
-
- # resume cmd
- def __resume (self, port_id_list):
-
- self.logger.pre_cmd("Resume traffic on port(s) {0}:".format(port_id_list))
- rc = self.__resume_traffic(port_id_list)
- self.logger.post_cmd(rc)
-
- return rc
-
-
- # validate port(s) profile
- def __validate (self, port_id_list):
- self.logger.pre_cmd("Validating streams on port(s) {0}:".format(port_id_list))
- rc = self.__validate_traffic(port_id_list)
- self.logger.post_cmd(rc)
-
- return rc
-
-
# clear stats
def __clear_stats(self, port_id_list, clear_global):
@@ -862,9 +721,7 @@ class STLClient(object):
if clear_global:
self.global_stats.clear_stats()
- self.logger.pre_cmd("clearing stats on port(s) {0}:".format(port_id_list))
- rc = RC_OK()
- self.logger.post_cmd(rc)
+ self.logger.log_cmd("clearing stats on port(s) {0}:".format(port_id_list))
return RC
@@ -891,30 +748,6 @@ class STLClient(object):
return stats
- def __process_profiles (self, profiles, out):
-
- for profile in (profiles if isinstance(profiles, list) else [profiles]):
- # filename
- if isinstance(profile, str):
-
- if not os.path.isfile(profile):
- return RC_ERR("file '{0}' does not exists".format(profile))
-
- try:
- stream_list = self.streams_db.load_yaml_file(profile)
- except Exception as e:
- rc = RC_ERR(str(e))
- return rc
-
- out.append(stream_list)
-
- else:
- return RC_ERR("unknown profile '{0}'".format(profile))
-
-
- return RC_OK()
-
-
############ functions used by other classes but not users ##############
def _verify_port_id_list (self, port_id_list):
@@ -1014,7 +847,8 @@ class STLClient(object):
############################ #############################
############################ #############################
def __enter__ (self):
- self.connect(mode = "RWF")
+ self.connect()
+ self.acquire(force = True)
self.reset()
return self
@@ -1134,36 +968,79 @@ class STLClient(object):
############################ #############################
- # set the log on verbose level
+ """
+ Sets verbose level
+
+ :parameters:
+ level : enum
+ LoggerApi.VERBOSE_QUIET
+ LoggerApi.VERBOSE_NORMAL
+ LoggerApi.VERBOSE_HIGH
+
+ :raises:
+ None
+
+ """
def set_verbose (self, level):
self.logger.set_verbose(level)
- # connects to the server
- # mode can be:
- # 'RO' - read only
- # 'RW' - read/write
- # 'RWF' - read write forced (take ownership)
+ """
+ Connects to the TRex server
+
+ :parameters:
+ None
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(False)
- def connect (self, mode = "RW"):
- modes = ['RO', 'RW', 'RWF']
- if not mode in modes:
- raise STLArgumentError('mode', mode, modes)
-
+ def connect (self):
rc = self.__connect()
if not rc:
raise STLError(rc)
- # acquire all ports for 'RW' or 'RWF'
- if (mode == "RW") or (mode == "RWF"):
- self.acquire(ports = self.get_all_ports(), force = True if mode == "RWF" else False)
+ """
+ Disconnects from the server
+
+ :parameters:
+ stop_traffic : bool
+ tries to stop traffic before disconnecting
+
+
+ """
+ @__api_check(False)
+ def disconnect (self, stop_traffic = True):
+
+ # try to stop ports but do nothing if not possible
+ if stop_traffic:
+ try:
+ self.stop()
+ except STLError:
+ pass
+
+ self.logger.pre_cmd("Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'],
+ self.connection_info['sync_port']))
+ rc = self.__disconnect()
+ self.logger.post_cmd(rc)
+
+
+
+ """
+ Acquires ports for executing commands
+ :parameters:
+ ports : list
+ ports to execute the command
+ force : bool
+ force acquire the ports
+ :raises:
+ + :exc:`STLError`
- # acquire ports
- # this is not needed if connect was called with "RW" or "RWF"
- # but for "RO" this might be needed
+ """
@__api_check(True)
def acquire (self, ports = None, force = False):
# by default use all ports
@@ -1186,102 +1063,218 @@ class STLClient(object):
self.logger.post_cmd(rc)
if not rc:
+ # cleanup
self.__release(ports)
raise STLError(rc)
- # force connect syntatic sugar
- @__api_check(False)
- def fconnect (self):
- self.connect(mode = "RWF")
+ """
+ Pings the server
+ :parameters:
+ None
+
+
+ :raises:
+ + :exc:`STLError`
+
+ """
+ @__api_check(True)
+ def ping(self):
+ self.logger.pre_cmd( "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
+ self.connection_info['sync_port']))
+ rc = self._transmit("ping")
+
+ self.logger.post_cmd(rc)
- # disconnects from the server
- @__api_check(False)
- def disconnect (self, log = True):
- rc = self.__disconnect()
- if log:
- self.logger.log_cmd("Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'],
- self.connection_info['sync_port']))
if not rc:
raise STLError(rc)
- # teardown - call after test is done
- # NEVER throws an exception
- @__api_check(False)
- def teardown (self, stop_traffic = True):
-
- # try to stop traffic
- if stop_traffic and self.get_active_ports():
- try:
- self.stop()
- except STLError:
- pass
-
- # disconnect
- self.__disconnect()
+ """
+ force acquire ports, stop the traffic, remove all streams and clear stats
+ :parameters:
+ ports : list
+ ports to execute the command
+
+ :raises:
+ + :exc:`STLError`
- # pings the server on the RPC channel
+ """
@__api_check(True)
- def ping(self):
- self.logger.pre_cmd( "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
- self.connection_info['sync_port']))
- rc = self.__ping()
-
- self.logger.post_cmd(rc)
+ def reset(self, ports = None):
+
+ # by default use all ports
+ if ports == None:
+ ports = self.get_all_ports()
+ # verify ports
+ rc = self._validate_port_list(ports)
if not rc:
- raise STLError(rc)
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+
+ self.acquire(ports, force = True)
+ self.stop(ports)
+ self.remove_all_streams(ports)
+ self.clear_stats(ports)
+
+
+ """
+ remove all streams from port(s)
+ :parameters:
+ ports : list
+ ports to execute the command
+
+ :raises:
+ + :exc:`STLError`
- # reset the server by performing
- # force acquire, stop, and remove all streams
+ """
@__api_check(True)
- def reset(self):
+ def remove_all_streams (self, ports = None):
- self.logger.pre_cmd("Force acquiring all ports:")
- rc = self.__acquire(force = True)
+ # by default use all ports
+ if ports == None:
+ ports = self.get_acquired_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())
+
+ self.logger.pre_cmd("Removing all streams from port(s) {0}:".format(ports))
+ rc = self.__remove_all_streams(ports)
self.logger.post_cmd(rc)
if not rc:
raise STLError(rc)
+
+ """
+ add a list of streams to port(s)
- # force stop all ports
- self.logger.pre_cmd("Stop traffic on all ports:")
- rc = self.__stop_traffic(self.get_all_ports(), True)
- self.logger.post_cmd(rc)
+ :parameters:
+ ports : list
+ ports to execute the command
+ streams: list
+ streams to attach
+
+ :raises:
+ + :exc:`STLError`
+
+ """
+ @__api_check(True)
+ def add_streams (self, streams, ports = None):
+ # by default use all ports
+ if ports == None:
+ ports = self.get_acquired_ports()
+ # verify valid port id list
+ rc = self._validate_port_list(ports)
if not rc:
- raise STLError(rc)
+ raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+ # transform single stream
+ if not isinstance(streams, list):
+ streams = [streams]
- # remove all streams
- self.logger.pre_cmd("Removing all streams from all ports:")
- rc = self.__remove_all_streams(self.get_all_ports())
+ # check streams
+ if not all([isinstance(stream, STLStream) for stream in streams]):
+ raise STLArgumentError('streams', streams)
+
+ self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams), ports))
+ rc = self.__add_streams(streams, ports)
self.logger.post_cmd(rc)
if not rc:
raise STLError(rc)
- self.clear_stats()
+ """
+ load a profile file to port(s)
+
+ :parameters:
+ filename : str
+ filename to load
+ ports : list
+ ports to execute the command
+
- # start cmd
+ :raises:
+ + :exc:`STLError`
+
+ """
+ @__api_check(True)
+ def load_profile (self, filename, ports = None):
+
+ # check filename
+ if not os.path.isfile(filename):
+ raise STLError("file '{0}' does not exists".format(filename))
+
+ # by default use all ports
+ if ports == None:
+ ports = self.get_acquired_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())
+
+ # load the YAML
+ try:
+ streams_pack = self.streams_db.load_yaml_file(filename)
+ except Exception as e:
+ raise STLError(str(e))
+
+ # HACK - convert the stream pack to simple streams
+ streams = []
+ for stream in streams_pack.compiled:
+ s = HACKSTLStream(stream)
+ streams.append(s)
+
+ self.add_streams(streams, ports)
+
+
+
+ """
+ start traffic on port(s)
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ mult : str
+ multiplier in a form of pps, bps, or line util in %
+ examples: "5kpps", "10gbps", "85%", "32mbps"
+
+ force : bool
+ imply stopping the port of active and also
+ forces a profile that exceeds the L1 BW
+
+ duration : int
+ limit the run for time in seconds
+ -1 means unlimited
+
+ total : bool
+ should the B/W be divided by the ports
+ or duplicated for each
+
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
def start (self,
- profiles,
ports = None,
mult = "1",
force = False,
duration = -1,
- dry = False,
total = False):
@@ -1313,57 +1306,87 @@ class STLClient(object):
raise STLArgumentError('total', total)
- # process profiles
- stream_list = []
- rc = self.__process_profiles(profiles, stream_list)
- if not rc:
- raise STLError(rc)
-
-
# verify ports are stopped or force stop them
active_ports = list(set(self.get_active_ports()).intersection(ports))
if active_ports:
if not force:
- msg = "Port(s) {0} are active - please stop them or specify 'force'".format(active_ports)
- raise STLError(msg)
+ raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
else:
- rc = self.__stop(active_ports)
+ rc = self.stop(active_ports)
if not rc:
raise STLError(rc)
- # dry run
- if dry:
- self.logger.log(format_text("\n*** DRY RUN ***", 'bold'))
-
- # call private method to start
+ # start traffic
+ self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports))
+ rc = self.__start(mult_obj, duration, ports, force)
+ self.logger.post_cmd(rc)
- rc = self.__start(ports, stream_list[0], mult_obj, force, duration, dry)
if not rc:
raise STLError(rc)
- # stop traffic on ports
+
+ """
+ stop port(s)
+
+ :parameters:
+ ports : list
+ ports to execute the command
+
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
def stop (self, ports = None):
# by default the user means all the active ports
if ports == None:
ports = self.get_active_ports()
+ if not ports:
+ return
# verify valid port id list
rc = self._validate_port_list(ports)
if not rc:
raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+ self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
rc = self.__stop(ports)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
- # update traffic
+ """
+ update traffic on port(s)
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ mult : str
+ multiplier in a form of pps, bps, or line util in %
+ and also with +/-
+ examples: "5kpps+", "10gbps-", "85%", "32mbps", "20%+"
+
+ force : bool
+ forces a profile that exceeds the L1 BW
+
+ total : bool
+ should the B/W be divided by the ports
+ or duplicated for each
+
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
def update (self, ports = None, mult = "1", total = False, force = False):
@@ -1389,13 +1412,26 @@ class STLClient(object):
# call low level functions
- rc = self.__update(ports, mult_obj, force)
+ self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(ports))
+ rc = self.__update(mult, ports, force)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
- # pause traffic on ports
+ """
+ pause traffic on port(s)
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
def pause (self, ports = None):
@@ -1408,13 +1444,26 @@ class STLClient(object):
if not rc:
raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+ self.logger.pre_cmd("Pausing traffic on port(s) {0}:".format(ports))
rc = self.__pause(ports)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
- # resume traffic on ports
+ """
+ resume traffic on port(s)
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
def resume (self, ports = None):
@@ -1427,13 +1476,39 @@ class STLClient(object):
if not rc:
raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
+ self.logger.pre_cmd("Resume traffic on port(s) {0}:".format(ports))
rc = self.__resume(ports)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
+ """
+ validate port(s) configuration
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ mult : str
+ multiplier in a form of pps, bps, or line util in %
+ examples: "5kpps", "10gbps", "85%", "32mbps"
+
+ duration : int
+ limit the run for time in seconds
+ -1 means unlimited
+
+ total : bool
+ should the B/W be divided by the ports
+ or duplicated for each
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(True)
- def validate (self, ports = None):
+ def validate (self, ports = None, mult = "1", duration = "-1", total = False):
if ports == None:
ports = self.get_acquired_ports()
@@ -1442,18 +1517,47 @@ class STLClient(object):
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)
+
+
+ if not isinstance(duration, (int, float)):
+ raise STLArgumentError('duration', duration)
+
+
+ self.logger.pre_cmd("Validating streams on port(s) {0}:".format(ports))
rc = self.__validate(ports)
- if not rc:
- raise STLError(rc)
+ self.logger.post_cmd(rc)
- # clear stats
+ for port in ports:
+ self.ports[port].print_profile(mult_obj, duration)
+
+
+ """
+ clear stats on port(s)
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ clear_global : bool
+ clear the global stats
+
+ :raises:
+ + :exc:`STLError`
+
+ """
@__api_check(False)
def clear_stats (self, ports = None, clear_global = True):
# by default use all ports
if ports == None:
- ports = self.get_acquired_ports()
+ ports = self.get_all_ports()
# verify valid port id list
rc = self._validate_port_list(ports)
@@ -1473,7 +1577,21 @@ class STLClient(object):
- # wait while traffic is on, on timeout throw STLTimeoutError
+ """
+ block until specify port(s) traffic has ended
+
+ :parameters:
+ ports : list
+ ports to execute command
+
+ timeout : int
+ timeout in seconds
+
+ :raises:
+ + :exc:`STLTimeoutError` - in case timeout has expired
+ + :exe:'STLError'
+
+ """
@__api_check(True)
def wait_on_traffic (self, ports = None, timeout = 60):
@@ -1495,13 +1613,24 @@ class STLClient(object):
raise STLTimeoutError(timeout)
- # clear all async events
+ """
+ clear all events
+
+ :parameters:
+ None
+
+ :raises:
+ None
+
+ """
def clear_events (self):
self.event_handler.clear_events()
+
############################ Line #############################
############################ Commands #############################
############################ #############################
+
# console decorator
def __console(f):
def wrap(*args):
@@ -1539,7 +1668,8 @@ class STLClient(object):
return
# call the API
- self.connect("RWF" if opts.force else "RW")
+ self.connect()
+ self.acquire(force = opts.force)
# true means print time
return True
@@ -1587,18 +1717,24 @@ class STLClient(object):
msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports)
self.logger.log(format_text(msg, 'bold'))
return
+ else:
+ self.stop(active_ports)
+ # remove all streams
+ self.remove_all_streams(opts.ports)
+
# pack the profile
- profiles = [opts.file[0]]
-
- self.start(profiles,
- opts.ports,
- opts.mult,
- opts.force,
- opts.duration,
- opts.dry,
- opts.total)
+ self.load_profile(opts.file[0], opts.ports)
+
+ if opts.dry:
+ self.validate(opts.ports, opts.mult, opts.duration, opts.total)
+ else:
+ self.start(opts.ports,
+ opts.mult,
+ opts.force,
+ opts.duration,
+ opts.total)
# true means print time
return True
diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_sim.py b/scripts/automation/trex_control_plane/client/trex_stateless_sim.py
index 3196c666..a8f24d65 100644
--- a/scripts/automation/trex_control_plane/client/trex_stateless_sim.py
+++ b/scripts/automation/trex_control_plane/client/trex_stateless_sim.py
@@ -247,7 +247,7 @@ def setParserOptions():
parser.add_argument('-m', '--multiplier',
help = parsing_opts.match_multiplier_help,
dest = 'mult',
- default = {'type':'raw', 'value':1, 'op': 'abs'},
+ default = "1",
type = parsing_opts.match_multiplier_strict)
group = parser.add_mutually_exclusive_group()
diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py
index 05a32bc4..9c351175 100755
--- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py
+++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py
@@ -81,7 +81,7 @@ class JsonRpcClient(object):
return pretty_str
def verbose_msg (self, msg):
- self.logger.log("[verbose] " + msg, level = self.logger.VERBOSE_HIGH)
+ self.logger.log("\n\n[verbose] " + msg, level = self.logger.VERBOSE_HIGH)
# batch messages
@@ -215,6 +215,11 @@ class JsonRpcClient(object):
self.connected = True
+ rc = self.invoke_rpc_method('ping')
+ if not rc:
+ self.connected = False
+ return rc
+
return RC_OK()
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 fe96789e..c7d3308e 100755
--- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py
+++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py
@@ -12,7 +12,7 @@ import re
import itertools
from abc import ABCMeta, abstractmethod
from collections import namedtuple
-
+import base64
class CTRexPktBuilder(object):
"""
@@ -332,6 +332,7 @@ 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)
# thn, load it based on dpkt parsing
@@ -480,11 +481,10 @@ class CTRexPktBuilder(object):
"""
if self._packet is None:
raise CTRexPktBuilder.EmptyPacketError()
- pkt_in_hex = binascii.hexlify(str(self._packet))
- return {"binary": [int(pkt_in_hex[i:i+2], 16)
- for i in range(0, len(pkt_in_hex), 2)],
+
+ return {"binary": base64.b64encode(str(self._packet)),
"meta": self.metadata}
- # return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)]
+
def dump_pkt_to_pcap(self, file_path, ts=None):
"""
diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py
index 52c0c0a1..464ee56a 100755
--- a/scripts/automation/trex_control_plane/common/trex_stats.py
+++ b/scripts/automation/trex_control_plane/common/trex_stats.py
@@ -223,7 +223,8 @@ class CTRexInfoGenerator(object):
info_table = text_tables.TRexTextTable()
info_table.set_cols_align(["c"] + ["l"] + ["r"] + ["c"] + ["r"] + ["c"])
- info_table.set_cols_width([4] + [20] + [8] + [16] + [10] + [12])
+ info_table.set_cols_width([10] + [20] + [8] + [16] + [10] + [12])
+ info_table.set_cols_dtype(["t"] + ["t"] + ["t"] + ["t"] + ["t"] + ["t"])
info_table.add_rows([v.values()
for k, v in return_streams_data['streams'].iteritems()],
diff --git a/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py b/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py
new file mode 100644
index 00000000..9be20db9
--- /dev/null
+++ b/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py
@@ -0,0 +1,53 @@
+import os
+import sys
+from common.text_opts import *
+
+# 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()
+ fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+
+
+ s = "\n******\n"
+ 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)
+
+
+# port state error
+class STLPortStateError(STLError):
+ def __init__ (self, port, op, state):
+ self.msg = "Operation '{0}' on port(s) '{1}' is not valid while port(s) '{2}'".format(op, port, 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)
+
+# raised when timeout occurs
+class STLTimeoutError(STLError):
+ def __init__ (self, timeout):
+ self.msg = "Timeout: operation took more than '{0}' seconds".format(timeout)
+
+
+
diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py
index ea3d71d1..90cb812d 100755
--- a/scripts/automation/trex_control_plane/common/trex_streams.py
+++ b/scripts/automation/trex_control_plane/common/trex_streams.py
@@ -4,10 +4,12 @@ import external_packages
from client_utils.packet_builder import CTRexPktBuilder
from collections import OrderedDict, namedtuple
from client_utils.yaml_utils import *
+import trex_stl_exceptions
import dpkt
import struct
import copy
import os
+import random
StreamPack = namedtuple('StreamPack', ['stream_id', 'stream'])
LoadedStreamList = namedtuple('LoadedStreamList', ['name', 'loaded', 'compiled'])
@@ -323,3 +325,163 @@ class CStreamsDB(object):
else:
return self.stream_packs.get(name)
+
+########################### Simple Streams ###########################
+from trex_stl_exceptions import *
+
+class STLStream(object):
+
+ def __init__ (self,
+ packet,
+ pps = 1,
+ enabled = True,
+ self_start = True,
+ isg = 0.0,
+ rx_stats = None,
+ next_stream_id = -1):
+
+ # type checking
+ if not isinstance(pps, (int, float)):
+ raise STLArgumentError('pps', pps)
+
+ if not isinstance(packet, CTRexPktBuilder):
+ raise STLArgumentError('packet', packet)
+
+ if not isinstance(enabled, bool):
+ raise STLArgumentError('enabled', enabled)
+
+ if not isinstance(self_start, bool):
+ raise STLArgumentError('self_start', self_start)
+
+ if not isinstance(isg, (int, float)):
+ raise STLArgumentError('isg', isg)
+
+ # use a random 31 bit for ID
+ self.stream_id = random.getrandbits(31)
+
+ self.fields = {}
+
+ # basic fields
+ self.fields['enabled'] = enabled
+ self.fields['self_start'] = self_start
+ self.fields['isg'] = isg
+
+ self.fields['next_stream_id'] = next_stream_id
+
+ # mode
+ self.fields['mode'] = {}
+ self.fields['mode']['pps'] = pps
+
+ # packet and VM
+ self.fields['packet'] = packet.dump_pkt()
+ self.fields['vm'] = packet.get_vm_data()
+
+ self.fields['rx_stats'] = {}
+ if not rx_stats:
+ self.fields['rx_stats']['enabled'] = False
+
+
+ def __str__ (self):
+ return json.dumps(self.fields, indent = 4, separators=(',', ': '), sort_keys = True)
+
+ def to_json (self):
+ return self.fields
+
+ def get_id (self):
+ return self.stream_id
+
+
+# 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):
+
+
+ if not isinstance(total_pkts, int):
+ raise STLArgumentError('total_pkts', total_pkts)
+
+ 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
+
+
+# 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):
+
+
+ if not isinstance(pkts_per_burst, int):
+ raise STLArgumentError('pkts_per_burst', pkts_per_burst)
+
+ if not isinstance(count, int):
+ raise STLArgumentError('count', count)
+
+ if not isinstance(ibg, (int, float)):
+ raise STLArgumentError('ibg', ibg)
+
+ super(STLMultiBurstStream, self).__init__(packet, enabled, self_start, isg, rx_stats)
+
+ 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
+class HACKSTLStream(STLStream):
+ def __init__ (self, stream_pack):
+ if not isinstance(stream_pack, StreamPack):
+ raise Exception("internal error")
+
+ packet = CTRexPktBuilder()
+ packet.load_from_stream_obj(stream_pack.stream)
+ super(HACKSTLStream, self).__init__(packet)
+
+ self.fields = stream_pack.stream
diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py
index 88ff45dc..1defc6b2 100755
--- a/scripts/automation/trex_control_plane/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/console/trex_console.py
@@ -29,14 +29,14 @@ import sys
import tty, termios
import trex_root_path
from common.trex_streams import *
-from client.trex_stateless_client import STLClient, LoggerApi, STLError
+from client.trex_stateless_client import STLClient, LoggerApi
from common.text_opts import *
from client_utils.general_utils import user_input, get_current_user
from client_utils import parsing_opts
import trex_tui
+from common.trex_stl_exceptions import *
from functools import wraps
-
__version__ = "1.1"
# console custom logger
@@ -56,7 +56,7 @@ class ConsoleLogger(LoggerApi):
# override this for the prompt fix
def async_log (self, msg, level = LoggerApi.VERBOSE_REGULAR, newline = True):
self.log(msg, level, newline)
- if self.prompt_redraw:
+ if ( (self.level >= LoggerApi.VERBOSE_REGULAR) and self.prompt_redraw ):
self.prompt_redraw()
self.flush()
@@ -717,13 +717,14 @@ def main():
# TUI or no acquire will give us READ ONLY mode
try:
- stateless_client.connect("RO")
+ 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'))
@@ -751,7 +752,8 @@ def main():
print "\n\n*** Caught Ctrl + C... Exiting...\n\n"
finally:
- stateless_client.teardown(stop_traffic = False)
+ with stateless_client.logger.supress():
+ stateless_client.disconnect(stop_traffic = False)
if __name__ == '__main__':
diff --git a/src/common/base64.cpp b/src/common/base64.cpp
new file mode 100644
index 00000000..50006d4f
--- /dev/null
+++ b/src/common/base64.cpp
@@ -0,0 +1,123 @@
+/*
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for(i = 0; (i <4) ; i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for(j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+ int in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i ==4) {
+ for (i = 0; i <4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j <4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j <4; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+} \ No newline at end of file
diff --git a/src/common/base64.h b/src/common/base64.h
new file mode 100644
index 00000000..48a13fe5
--- /dev/null
+++ b/src/common/base64.h
@@ -0,0 +1,9 @@
+#ifndef __BASE64_H__
+#define __BASE64_H__
+
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
+
+#endif /* __BASE64_H__ */
diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
index 821479f5..69e5dd18 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp
@@ -24,7 +24,7 @@ limitations under the License.
#include <trex_stateless.h>
#include <trex_stateless_port.h>
#include <trex_streams_compiler.h>
-
+#include <common/base64.h>
#include <iostream>
using namespace std;
@@ -63,19 +63,31 @@ TrexRpcCmdAddStream::_run(const Json::Value &params, Json::Value &result) {
stream->m_next_stream_id = parse_int(section, "next_stream_id", result);
const Json::Value &pkt = parse_object(section, "packet", result);
- const Json::Value &pkt_binary = parse_array(pkt, "binary", result);
+ std::string pkt_binary = base64_decode(parse_string(pkt, "binary", result));
+
+ /* check packet size */
+ if ( (pkt_binary.size() < TrexStream::MIN_PKT_SIZE_BYTES) || (pkt_binary.size() > TrexStream::MAX_PKT_SIZE_BYTES) ) {
+ std::stringstream ss;
+ ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES;
+ delete stream;
+ generate_execute_err(result, ss.str());
+ }
/* fetch the packet from the message */
- stream->m_pkt.len = pkt_binary.size();
- stream->m_pkt.binary = new uint8_t[pkt_binary.size()];
+ stream->m_pkt.len = std::max(pkt_binary.size(), 60UL);
+
+ /* allocate and init to zero ( with () ) */
+ stream->m_pkt.binary = new uint8_t[pkt_binary.size()]();
if (!stream->m_pkt.binary) {
generate_internal_err(result, "unable to allocate memory");
}
- /* parse the packet */
+ const char *pkt_buffer = pkt_binary.c_str();
+
+ /* copy the packet - if less than 60 it will remain zeroes */
for (int i = 0; i < pkt_binary.size(); i++) {
- stream->m_pkt.binary[i] = parse_byte(pkt_binary, i, result);
+ stream->m_pkt.binary[i] = pkt_buffer[i];
}
/* meta data */
@@ -304,14 +316,6 @@ TrexRpcCmdAddStream::parse_vm(const Json::Value &vm, TrexStream *stream, Json::V
void
TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &result) {
- /* check packet size */
- if ( (stream->m_pkt.len < TrexStream::MIN_PKT_SIZE_BYTES) || (stream->m_pkt.len > TrexStream::MAX_PKT_SIZE_BYTES) ) {
- std::stringstream ss;
- ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES;
- delete stream;
- generate_execute_err(result, ss.str());
- }
-
/* add the stream to the port's stream table */
TrexStatelessPort * port = get_stateless_obj()->get_port_by_id(stream->m_port_id);
diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h
index 6bb30cf9..80d64ec5 100644
--- a/src/stateless/cp/trex_stream.h
+++ b/src/stateless/cp/trex_stream.h
@@ -125,7 +125,7 @@ public:
virtual ~TrexStream();
/* defines the min max per packet supported */
- static const uint32_t MIN_PKT_SIZE_BYTES = 60;
+ static const uint32_t MIN_PKT_SIZE_BYTES = 14;
static const uint32_t MAX_PKT_SIZE_BYTES = MAX_PKT_SIZE;
/* provides storage for the stream json*/
diff --git a/src/stub/trex_stateless_stub.cpp b/src/stub/trex_stateless_stub.cpp
deleted file mode 100644
index 199356d8..00000000
--- a/src/stub/trex_stateless_stub.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-
-#include <trex_stateless_dp_core.h>
-
-class CFlowGenListPerThread;
-class TrexStatelessCpToDpMsgBase;
-
-void
-TrexStatelessDpCore::create(unsigned char, CFlowGenListPerThread*) {
- m_thread_id = 0;
- m_core = NULL;
-
- m_state = STATE_IDLE;
-
- CMessagingManager * cp_dp = CMsgIns::Ins()->getCpDp();
-
- m_ring_from_cp = cp_dp->getRingCpToDp(0);
- m_ring_to_cp = cp_dp->getRingDpToCp(0);
-}
-
-void TrexStatelessDpCore::start(){}
-
-void TrexStatelessDpCore::handle_cp_msg(TrexStatelessCpToDpMsgBase*) {}
-
diff --git a/src/stub/zmq_stub.c b/src/stub/zmq_stub.c
new file mode 100644
index 00000000..b94f93e4
--- /dev/null
+++ b/src/stub/zmq_stub.c
@@ -0,0 +1,81 @@
+#include <zmq.h>
+#include <assert.h>
+
+void *zmq_ctx_new (void) {
+ return NULL;
+}
+
+void *zmq_socket (void *, int type) {
+ return NULL;
+}
+
+int zmq_close (void *s) {
+ return (-1);
+}
+
+int zmq_setsockopt (void *s, int option, const void *optval,size_t optvallen) {
+ return (-1);
+}
+
+int zmq_getsockopt (void *s, int option, void *optval,
+ size_t *optvallen) {
+ return (-1);
+}
+
+int zmq_bind (void *s, const char *addr) {
+ return (-1);
+}
+
+void *zmq_init (int io_threads) {
+ return NULL;
+}
+
+int zmq_term (void *context) {
+ return (-1);
+}
+
+int zmq_ctx_destroy (void *context) {
+ return (-1);
+}
+
+
+int zmq_connect (void *s, const char *addr) {
+ return (-1);
+}
+
+int zmq_send (void *s, const void *buf, size_t len, int flags) {
+ return (-1);
+}
+
+int zmq_recv (void *s, void *buf, size_t len, int flags) {
+ return (-1);
+}
+
+int zmq_errno (void) {
+ return (-1);
+}
+
+const char *zmq_strerror (int errnum) {
+ return "";
+}
+
+int zmq_msg_init (zmq_msg_t *msg) {
+ return (-1);
+}
+
+int zmq_msg_recv (zmq_msg_t *msg, void *s, int flags) {
+ return (-1);
+}
+
+int zmq_msg_close (zmq_msg_t *msg) {
+ return (-1);
+}
+
+void *zmq_msg_data (zmq_msg_t *msg) {
+ return NULL;
+}
+
+size_t zmq_msg_size (zmq_msg_t *msg) {
+ return (0);
+}
+