summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorimarom <imarom@cisco.com>2016-01-26 08:57:43 -0500
committerimarom <imarom@cisco.com>2016-01-26 08:58:49 -0500
commit2828fc9aab33b742c59a499dbf06ea2239ec6220 (patch)
tree8385177f873a06d8c4574fb6721bf8c9a74a60c3
parent4c6450049d82fb9f98dbafe98d3ea1e229bf2a6d (diff)
API simplification and example
-rw-r--r--api/stl/examples/stl_simple_burst.py69
-rw-r--r--api/stl/trex_stl_api.py18
-rw-r--r--scripts/automation/trex_control_plane/client/trex_port.py60
-rwxr-xr-xscripts/automation/trex_control_plane/client/trex_stateless_client.py77
-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.py5
8 files changed, 315 insertions, 132 deletions
diff --git a/api/stl/examples/stl_simple_burst.py b/api/stl/examples/stl_simple_burst.py
index b60df4bf..49355890 100644
--- a/api/stl/examples/stl_simple_burst.py
+++ b/api/stl/examples/stl_simple_burst.py
@@ -1,66 +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 ():
-
- passed = True
-
+
+ # 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:
- # activate this for some logging information
- #c.logger.set_verbose(c.logger.VERBOSE_REGULAR)
+ #c.logger.set_verbose(c.logger.VERBOSE_NORMAL)
+
+ # 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())
# connect to server
c.connect()
- # prepare port 0,1
+ # prepare our ports
c.reset(ports = [0, 1])
- # load profile to both ports
- c.load_profile('../profiles/burst.yaml', ports = [0, 1])
+ # add both streams to ports
+ c.add_streams([s1, s2], ports = [0, 1])
- # repeat for 5 times
+ # run 5 times
for i in xrange(1, 6):
-
c.clear_stats()
-
- # start traffic and block until done
- c.start(ports = [0, 1], mult = "1gbps", duration = 5)
+ c.start(ports = [0, 1], mult = "1gbps")
c.wait_on_traffic(ports = [0, 1])
- # read the stats
stats = c.get_stats()
ipackets = stats['total']['ipackets']
print "Test iteration {0} - Packets Received: {1} ".format(i, ipackets)
-
- # we have 600 packets in the burst and two ports
- if (ipackets != (600 * 2)):
+ # (5000 + 3000) * 2 ports = 16,000
+ if (ipackets != (16000)):
passed = False
-
- # error handling
except STLError as e:
passed = False
print e
- # cleanup
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/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py
index 438ff4be..c8147faf 100644
--- a/scripts/automation/trex_control_plane/client/trex_port.py
+++ b/scripts/automation/trex_control_plane/client/trex_port.py
@@ -49,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)
@@ -139,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")
-
-
- params = {"handler": self.handler,
- "port_id": self.port_id,
- "stream_id": stream_id,
- "stream": stream_obj}
+ # add streams
+ def add_streams (self, streams_list):
- 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):
@@ -461,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:
@@ -477,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 6b8e42a6..886edb61 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
@@ -1209,7 +1162,8 @@ class STLClient(object):
:parameters:
ports : list
ports to execute the command
-
+ streams: list
+ streams to attach
:raises:
+ :exc:`STLError`
@@ -1226,8 +1180,16 @@ class STLClient(object):
if not rc:
raise STLArgumentError('ports', ports, valid_values = self.get_all_ports())
- self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams.compiled), ports))
- rc = self.__add_stream_pack(streams, ports)
+ # transform single stream
+ if not isinstance(streams, list):
+ streams = [streams]
+
+ # 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:
@@ -1266,11 +1228,16 @@ class STLClient(object):
# load the YAML
try:
- streams = self.streams_db.load_yaml_file(filename)
+ streams_pack = self.streams_db.load_yaml_file(filename)
except Exception as e:
raise STLError(str(e))
- # attach
+ # 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)
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 62b68861..1defc6b2 100755
--- a/scripts/automation/trex_control_plane/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/console/trex_console.py
@@ -29,11 +29,12 @@ 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"
@@ -752,7 +753,7 @@ def main():
finally:
with stateless_client.logger.supress():
- stateless_client.disconnect()
+ stateless_client.disconnect(stop_traffic = False)
if __name__ == '__main__':