aboutsummaryrefslogtreecommitdiffstats
path: root/tools/pkt_gen
diff options
context:
space:
mode:
Diffstat (limited to 'tools/pkt_gen')
-rwxr-xr-xtools/pkt_gen/dummy/dummy.py20
-rwxr-xr-xtools/pkt_gen/ixia/ixia.py33
-rwxr-xr-xtools/pkt_gen/ixnet/ixnet.py90
-rw-r--r--tools/pkt_gen/moongen/moongen.py45
-rw-r--r--tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py270
-rw-r--r--tools/pkt_gen/testcenter/testcenter.py55
-rwxr-xr-xtools/pkt_gen/trafficgen/trafficgen.py5
-rw-r--r--tools/pkt_gen/trex/trex_client.py (renamed from tools/pkt_gen/trex/trex.py)310
-rw-r--r--tools/pkt_gen/xena/XenaDriver.py155
-rw-r--r--tools/pkt_gen/xena/json/xena_json.py124
-rwxr-xr-xtools/pkt_gen/xena/xena.py32
11 files changed, 896 insertions, 243 deletions
diff --git a/tools/pkt_gen/dummy/dummy.py b/tools/pkt_gen/dummy/dummy.py
index 3dc5448e..ef4b37d9 100755
--- a/tools/pkt_gen/dummy/dummy.py
+++ b/tools/pkt_gen/dummy/dummy.py
@@ -25,6 +25,7 @@ own.
import json
+from collections import OrderedDict
from conf import settings
from conf import merge_spec
from tools.pkt_gen import trafficgen
@@ -108,41 +109,41 @@ class Dummy(trafficgen.ITrafficGenerator):
"""
pass
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""
Send a burst of traffic.
"""
traffic_ = self.traffic_defaults.copy()
- result = {}
+ result = OrderedDict()
if traffic:
traffic_ = merge_spec(traffic_, traffic)
results = get_user_traffic(
'burst',
- '%dpkts, %dmS' % (numpkts, duration),
+ '%dpkts, %dmS' % (traffic['burst_size'], duration),
traffic_,
('frames rx', 'payload errors', 'sequence errors'))
# builds results by using user-supplied values where possible
# and guessing remainder using available info
- result[ResultsConstants.TX_FRAMES] = numpkts
+ result[ResultsConstants.TX_FRAMES] = traffic['burst_size']
result[ResultsConstants.RX_FRAMES] = results[0]
result[ResultsConstants.TX_BYTES] = traffic_['l2']['framesize'] \
- * numpkts
+ * traffic['burst_size']
result[ResultsConstants.RX_BYTES] = traffic_['l2']['framesize'] \
* results[0]
result[ResultsConstants.PAYLOAD_ERR] = results[1]
result[ResultsConstants.SEQ_ERR] = results[2]
- return results
+ return result
def send_cont_traffic(self, traffic=None, duration=30):
"""
Send a continuous flow of traffic.
"""
traffic_ = self.traffic_defaults.copy()
- result = {}
+ result = OrderedDict()
if traffic:
traffic_ = merge_spec(traffic_, traffic)
@@ -179,7 +180,7 @@ class Dummy(trafficgen.ITrafficGenerator):
Send traffic per RFC2544 throughput test specifications.
"""
traffic_ = self.traffic_defaults.copy()
- result = {}
+ result = OrderedDict()
if traffic:
traffic_ = merge_spec(traffic_, traffic)
@@ -216,7 +217,7 @@ class Dummy(trafficgen.ITrafficGenerator):
Send traffic per RFC2544 back2back test specifications.
"""
traffic_ = self.traffic_defaults.copy()
- result = {}
+ result = OrderedDict()
if traffic:
traffic_ = merge_spec(traffic_, traffic)
@@ -273,4 +274,5 @@ if __name__ == '__main__':
print(dev.send_cont_traffic(traffic=TRAFFIC))
print(dev.send_rfc2544_throughput(traffic=TRAFFIC))
print(dev.send_rfc2544_back2back(traffic=TRAFFIC))
+ # pylint: disable=no-member
print(dev.send_rfc(traffic=TRAFFIC))
diff --git a/tools/pkt_gen/ixia/ixia.py b/tools/pkt_gen/ixia/ixia.py
index d4ca56f2..31f51246 100755
--- a/tools/pkt_gen/ixia/ixia.py
+++ b/tools/pkt_gen/ixia/ixia.py
@@ -157,8 +157,8 @@ class Ixia(trafficgen.ITrafficGenerator):
return NotImplementedError(
'Ixia start back2back traffic not implemented')
- def send_rfc2544_back2back(self, traffic=None, duration=60,
- lossrate=0.0, tests=1):
+ def send_rfc2544_back2back(self, traffic=None, tests=1, duration=60,
+ lossrate=0.0):
return NotImplementedError(
'Ixia send back2back traffic not implemented')
@@ -242,11 +242,11 @@ class Ixia(trafficgen.ITrafficGenerator):
return result
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""See ITrafficGenerator for description
"""
flow = {
- 'numpkts': numpkts,
+ 'numpkts': traffic['burst_size'],
'duration': duration,
'type': 'stopStream',
'framerate': traffic['frame_rate'],
@@ -254,9 +254,9 @@ class Ixia(trafficgen.ITrafficGenerator):
result = self._send_traffic(flow, traffic)
- assert len(result) == 6 # fail-fast if underlying Tcl code changes
+ assert len(result) == 10 # fail-fast if underlying Tcl code changes
- #NOTE - implement Burst results setting via TrafficgenResults.
+ return Ixia._create_result(result)
def send_cont_traffic(self, traffic=None, duration=30):
"""See ITrafficGenerator for description
@@ -317,20 +317,25 @@ class Ixia(trafficgen.ITrafficGenerator):
:returns: dictionary strings representing results from
traffic generator.
"""
- assert len(result) == 8 # fail-fast if underlying Tcl code changes
+ assert len(result) == 8 or len(result) == 10 # fail-fast if underlying Tcl code changes
+
+ # content of result common for all tests
+ # [framesSent, framesRecv, bytesSent, bytesRecv, sendRate, recvRate, sendRateBytes, recvRateBytes]
+ # burst test has additional two values at the end: payError, seqError
if float(result[0]) == 0:
loss_rate = 100
else:
- loss_rate = (float(result[0]) - float(result[1])) / float(result[0]) * 100
+ loss_rate = round((float(result[0]) - float(result[1])) / float(result[0]) * 100, 5)
result_dict = OrderedDict()
- # drop the first 4 elements as we don't use/need them. In
- # addition, IxExplorer does not support latency or % line rate
+ # IxExplorer does not support latency or % line rate
# metrics so we have to return dummy values for these metrics
- result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = result[4]
- result_dict[ResultsConstants.TX_RATE_FPS] = result[5]
- result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = str(round(int(result[6]) / 1000000, 3))
- result_dict[ResultsConstants.TX_RATE_MBPS] = str(round(int(result[7]) / 1000000, 3))
+ result_dict[ResultsConstants.TX_FRAMES] = result[0]
+ result_dict[ResultsConstants.RX_FRAMES] = result[1]
+ result_dict[ResultsConstants.TX_RATE_FPS] = result[4]
+ result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = result[5]
+ result_dict[ResultsConstants.TX_RATE_MBPS] = str(round(int(result[6]) * 8 / 1e6, 3))
+ result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = str(round(int(result[7]) * 8 / 1e6, 3))
result_dict[ResultsConstants.FRAME_LOSS_PERCENT] = loss_rate
result_dict[ResultsConstants.TX_RATE_PERCENT] = \
ResultsConstants.UNKNOWN_VALUE
diff --git a/tools/pkt_gen/ixnet/ixnet.py b/tools/pkt_gen/ixnet/ixnet.py
index d1ba9096..c7036606 100755
--- a/tools/pkt_gen/ixnet/ixnet.py
+++ b/tools/pkt_gen/ixnet/ixnet.py
@@ -83,6 +83,7 @@ import logging
import os
import re
import csv
+import random
from collections import OrderedDict
from tools.pkt_gen import trafficgen
@@ -129,7 +130,7 @@ def _build_set_cmds(values, prefix='dict set'):
if isinstance(value, list):
value = '{{{}}}'.format(' '.join(str(x) for x in value))
- yield ' '.join([prefix, 'set', key, value]).strip()
+ yield ' '.join([prefix, key, value]).strip()
continue
# tcl doesn't recognise the strings "True" or "False", only "1"
@@ -176,10 +177,9 @@ class IxNet(trafficgen.ITrafficGenerator):
:returns: Output of command, where applicable.
"""
self._logger.debug('%s%s', trafficgen.CMD_PREFIX, cmd)
-
output = self._tclsh.eval(cmd)
- return output.split()
+ return output
def configure(self):
"""Configure system for IxNetwork.
@@ -193,12 +193,16 @@ class IxNet(trafficgen.ITrafficGenerator):
'port': settings.getValue('TRAFFICGEN_IXNET_PORT'),
'user': settings.getValue('TRAFFICGEN_IXNET_USER'),
# IXIA chassis configuration
- 'chassis': settings.getValue('TRAFFICGEN_IXIA_HOST'),
- 'card': settings.getValue('TRAFFICGEN_IXIA_CARD'),
- 'port1': settings.getValue('TRAFFICGEN_IXIA_PORT1'),
- 'port2': settings.getValue('TRAFFICGEN_IXIA_PORT2'),
+ 'chassis_east': settings.getValue('TRAFFICGEN_EAST_IXIA_HOST'),
+ 'card_east': settings.getValue('TRAFFICGEN_EAST_IXIA_CARD'),
+ 'port_east': settings.getValue('TRAFFICGEN_EAST_IXIA_PORT'),
+ 'chassis_west': settings.getValue('TRAFFICGEN_WEST_IXIA_HOST'),
+ 'card_west': settings.getValue('TRAFFICGEN_WEST_IXIA_CARD'),
+ 'port_west': settings.getValue('TRAFFICGEN_WEST_IXIA_PORT'),
'output_dir':
settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'),
+ 'frame_size_list':
+ settings.getValue('TRAFFICGEN_PKT_SIZES'),
}
self._logger.debug('IXIA configuration configuration : %s', self._cfg)
@@ -256,11 +260,12 @@ class IxNet(trafficgen.ITrafficGenerator):
'An error occured when connecting to IxNetwork machine...')
raise RuntimeError('Ixia failed to initialise.')
- self.run_tcl('startRfc2544Test $config $traffic')
+ results_path = self.run_tcl('startRfc2544Test $config $traffic')
if output:
self._logger.critical(
'Failed to start continuous traffic test')
raise RuntimeError('Continuous traffic test failed to start.')
+ return results_path
def stop_cont_traffic(self):
"""See ITrafficGenerator for description
@@ -271,9 +276,12 @@ class IxNet(trafficgen.ITrafficGenerator):
lossrate=0.0):
"""See ITrafficGenerator for description
"""
- self.start_rfc2544_throughput(traffic, tests, duration, lossrate)
-
- return self.wait_rfc2544_throughput()
+ results_file = self.start_rfc2544_throughput(traffic, tests, duration, lossrate)
+ run_result = self.wait_rfc2544_throughput()
+ dest_file_name = 'Traffic_Item_Statistics_' + str(random.randrange(1, 100)) + '.csv'
+ self.copy_results_file(results_file,
+ os.path.join(settings.getValue('RESULTS_PATH'), dest_file_name))
+ return run_result
def start_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
lossrate=0.0):
@@ -313,12 +321,14 @@ class IxNet(trafficgen.ITrafficGenerator):
'An error occured when connecting to IxNetwork machine...')
raise RuntimeError('Ixia failed to initialise.')
- self.run_tcl('startRfc2544Test $config $traffic')
+ results_file = self.run_tcl('startRfc2544Test $config $traffic')
if output:
self._logger.critical(
'Failed to start RFC2544 test')
raise RuntimeError('RFC2544 test failed to start.')
+ return results_file
+
def wait_rfc2544_throughput(self):
"""See ITrafficGenerator for description
"""
@@ -370,7 +380,7 @@ class IxNet(trafficgen.ITrafficGenerator):
next(reader)
for row in reader:
#Replace null entries added by Ixia with 0s.
- row = [entry if len(entry) > 0 else '0' for entry in row]
+ row = [entry if entry else '0' for entry in row]
# tx_fps and tx_mps cannot be reliably calculated
# as the DUT may be modifying the frame size
@@ -397,12 +407,34 @@ class IxNet(trafficgen.ITrafficGenerator):
return results
output = self.run_tcl('waitForRfc2544Test')
-
# the run_tcl function will return a list with one element. We extract
# that one element (a string representation of an IXIA-specific Tcl
# datatype), parse it to find the path of the results file then parse
# the results file
- return parse_ixnet_rfc_results(parse_result_string(output[0]))
+ test_result = parse_ixnet_rfc_results(parse_result_string(output))
+ return test_result
+
+ def copy_results_file(self, source_file=None, dest_file=None):
+ """Copy a file from a source address to destination
+ """
+ dest_dict = {}
+ source_dict = {}
+ srcfile = ''
+ if isinstance(source_file, list):
+ for i in source_file:
+ srcfile = srcfile + ' ' + i
+ else:
+ srcfile = source_file
+
+ source = (srcfile.replace("\\", "/")).strip()
+ source_dict['source_file'] = {'source_file': '\"{}\"'.format(source)}
+ dest_dict['dest_file'] = {'dest_file': '{}'.format(dest_file)}
+ for cmd in _build_set_cmds(source_dict):
+ self.run_tcl(cmd)
+ for cmd in _build_set_cmds(dest_dict):
+ self.run_tcl(cmd)
+ self.run_tcl('copyFileResults $source_file $dest_file')
+ return dest_dict['dest_file']
def send_rfc2544_back2back(self, traffic=None, tests=1, duration=2,
lossrate=0.0):
@@ -411,9 +443,12 @@ class IxNet(trafficgen.ITrafficGenerator):
# NOTE 2 seconds is the recommended duration for a back 2 back
# test in RFC2544. 50 trials is the recommended number from the
# RFC also.
- self.start_rfc2544_back2back(traffic, tests, duration, lossrate)
-
- return self.wait_rfc2544_back2back()
+ b2b_results_file = self.start_rfc2544_back2back(traffic, tests, duration, lossrate)
+ b2b_run_result = self.wait_rfc2544_back2back()
+ dest_file_name = 'Traffic_Item_Statistics_' + str(random.randrange(1, 100)) + '.csv'
+ self.copy_results_file(b2b_results_file,
+ os.path.join(settings.getValue('RESULTS_PATH'), dest_file_name))
+ return b2b_run_result
def start_rfc2544_back2back(self, traffic=None, tests=1, duration=2,
lossrate=0.0):
@@ -453,15 +488,18 @@ class IxNet(trafficgen.ITrafficGenerator):
'An error occured when connecting to IxNetwork machine...')
raise RuntimeError('Ixia failed to initialise.')
- self.run_tcl('startRfc2544Test $config $traffic')
+ results_file = self.run_tcl('startRfc2544Test $config $traffic')
if output:
self._logger.critical(
'Failed to start RFC2544 test')
raise RuntimeError('RFC2544 test failed to start.')
+ return results_file
+
def wait_rfc2544_back2back(self):
"""Wait for results.
"""
+
def parse_result_string(results):
"""Get path to results file from output
@@ -487,7 +525,7 @@ class IxNet(trafficgen.ITrafficGenerator):
# transform path into something useful
path = result_path.group(1).replace('\\', '/')
- path = os.path.join(path, 'iteration.csv')
+ path = os.path.join(path, 'AggregateResults.csv')
path = path.replace(
settings.getValue('TRAFFICGEN_IXNET_TESTER_RESULT_DIR'),
settings.getValue('TRAFFICGEN_IXNET_DUT_RESULT_DIR'))
@@ -511,11 +549,11 @@ class IxNet(trafficgen.ITrafficGenerator):
for row in reader:
# if back2back count higher than previously found, store it
# Note: row[N] here refers to the Nth column of a row
- if float(row[14]) <= self._params['config']['lossrate']:
- if int(row[12]) > \
+ if float(row[10]) <= self._params['config']['lossrate']:
+ if int(float(row[8])) > \
int(results[ResultsConstants.B2B_FRAMES]):
- results[ResultsConstants.B2B_FRAMES] = int(row[12])
- results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = float(row[14])
+ results[ResultsConstants.B2B_FRAMES] = int(float(row[8]))
+ results[ResultsConstants.B2B_FRAME_LOSS_PERCENT] = float(row[10])
return results
@@ -526,9 +564,9 @@ class IxNet(trafficgen.ITrafficGenerator):
# datatype), parse it to find the path of the results file then parse
# the results file
- return parse_ixnet_rfc_results(parse_result_string(output[0]))
+ return parse_ixnet_rfc_results(parse_result_string(output))
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
return NotImplementedError('IxNet does not implement send_burst_traffic')
if __name__ == '__main__':
diff --git a/tools/pkt_gen/moongen/moongen.py b/tools/pkt_gen/moongen/moongen.py
index 570720e8..b7d55c4d 100644
--- a/tools/pkt_gen/moongen/moongen.py
+++ b/tools/pkt_gen/moongen/moongen.py
@@ -64,46 +64,46 @@ class Moongen(ITrafficGenerator):
:param one_shot: No RFC 2544 binary search,
just packet flow at traffic specifics
"""
- logging.debug("traffic['frame_rate'] = " + \
+ logging.debug("traffic['frame_rate'] = %s", \
str(traffic['frame_rate']))
- logging.debug("traffic['multistream'] = " + \
+ logging.debug("traffic['multistream'] = %s", \
str(traffic['multistream']))
- logging.debug("traffic['stream_type'] = " + \
+ logging.debug("traffic['stream_type'] = %s", \
str(traffic['stream_type']))
- logging.debug("traffic['l2']['srcmac'] = " + \
+ logging.debug("traffic['l2']['srcmac'] = %s", \
str(traffic['l2']['srcmac']))
- logging.debug("traffic['l2']['dstmac'] = " + \
+ logging.debug("traffic['l2']['dstmac'] = %s", \
str(traffic['l2']['dstmac']))
- logging.debug("traffic['l3']['proto'] = " + \
+ logging.debug("traffic['l3']['proto'] = %s", \
str(traffic['l3']['proto']))
- logging.debug("traffic['l3']['srcip'] = " + \
+ logging.debug("traffic['l3']['srcip'] = %s", \
str(traffic['l3']['srcip']))
- logging.debug("traffic['l3']['dstip'] = " + \
+ logging.debug("traffic['l3']['dstip'] = %s", \
str(traffic['l3']['dstip']))
- logging.debug("traffic['l4']['srcport'] = " + \
+ logging.debug("traffic['l4']['srcport'] = %s", \
str(traffic['l4']['srcport']))
- logging.debug("traffic['l4']['dstport'] = " + \
+ logging.debug("traffic['l4']['dstport'] = %s", \
str(traffic['l4']['dstport']))
- logging.debug("traffic['vlan']['enabled'] = " + \
+ logging.debug("traffic['vlan']['enabled'] = %s", \
str(traffic['vlan']['enabled']))
- logging.debug("traffic['vlan']['id'] = " + \
+ logging.debug("traffic['vlan']['id'] = %s", \
str(traffic['vlan']['id']))
- logging.debug("traffic['vlan']['priority'] = " + \
+ logging.debug("traffic['vlan']['priority'] = %s", \
str(traffic['vlan']['priority']))
- logging.debug("traffic['vlan']['cfi'] = " + \
+ logging.debug("traffic['vlan']['cfi'] = %s", \
str(traffic['vlan']['cfi']))
logging.debug(traffic['l2']['framesize'])
@@ -160,9 +160,9 @@ class Moongen(ITrafficGenerator):
(traffic['frame_rate'] / 100) * (self._moongen_line_speed / \
(8 * (traffic['l2']['framesize'] + 20)) / math.pow(10, 6)))
- logging.debug("startRate = " + start_rate)
+ logging.debug("startRate = %s", start_rate)
- out_file.write("startRate = " + \
+ out_file.write("startRate = %s" % \
start_rate + "\n")
out_file.write("}" + "\n")
@@ -240,14 +240,13 @@ class Moongen(ITrafficGenerator):
"""
self._logger.info("MOONGEN: In moongen disconnect method")
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""Send a burst of traffic.
- Send a ``numpkts`` packets of traffic, using ``traffic``
+ Send a ``traffic['burst_traffic']`` packets of traffic, using ``traffic``
configuration, with a timeout of ``time``.
:param traffic: Detailed "traffic" spec, i.e. IP address, VLAN tags
- :param numpkts: Number of packets to send
:param duration: Time to wait to receive packets
:returns: dictionary of strings with following data:
@@ -508,8 +507,8 @@ class Moongen(ITrafficGenerator):
return moongen_results
- def send_rfc2544_throughput(self, traffic=None, duration=20,
- lossrate=0.0, tests=1):
+ def send_rfc2544_throughput(self, traffic=None, tests=1, duration=20,
+ lossrate=0.0):
#
# Send traffic per RFC2544 throughput test specifications.
#
@@ -631,8 +630,8 @@ class Moongen(ITrafficGenerator):
"""
self._logger.info('In moongen wait_rfc2544_throughput')
- def send_rfc2544_back2back(self, traffic=None, duration=60,
- lossrate=0.0, tests=1):
+ def send_rfc2544_back2back(self, traffic=None, tests=1, duration=60,
+ lossrate=0.0):
"""Send traffic per RFC2544 back2back test specifications.
Send packets at a fixed rate, using ``traffic``
diff --git a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py
index 6c30b130..8089ef42 100644
--- a/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py
+++ b/tools/pkt_gen/testcenter/testcenter-rfc2544-rest.py
@@ -22,12 +22,27 @@ TestCenter REST APIs. This test supports Python 3.4
'''
import argparse
+import collections
import logging
import os
-
+import sqlite3
+import time
_LOGGER = logging.getLogger(__name__)
+GENOME_PKTSIZE_ENCODING = {"a": 64, "b": 128, "c": 256, "d": 512,
+ "e": 1024, "f": 1280, "g": 1518, "h": 2112}
+
+
+def genome2weights(sequence):
+ """ Convert genome sequence to packetsize weights"""
+ weights = collections.defaultdict(int)
+ for char in GENOME_PKTSIZE_ENCODING:
+ charcount = sequence.count(char)
+ if charcount:
+ weights[GENOME_PKTSIZE_ENCODING[char]] = charcount
+ return weights
+
def create_dir(path):
"""Create the directory as specified in path """
@@ -39,6 +54,17 @@ def create_dir(path):
raise
+def write_histogram_to_csv(results_path, csv_results_file_prefix,
+ counts, ranges):
+ """ Write the results of the query to the CSV """
+ filec = os.path.join(results_path, csv_results_file_prefix + ".csv")
+ with open(filec, "wb") as result_file:
+ for key in counts:
+ result_file.write(str(key) + "\n")
+ result_file.write(str(ranges) + "\n")
+ result_file.write(str(counts[key]) + "\n")
+
+
def write_query_results_to_csv(results_path, csv_results_file_prefix,
query_results):
""" Write the results of the query to the CSV """
@@ -51,6 +77,46 @@ def write_query_results_to_csv(results_path, csv_results_file_prefix,
result_file.write(row.replace(" ", ",") + "\n")
+def write_headers(results_path, file_name, rx_tx):
+ """ Write headers for the live-results files """
+ filec = os.path.join(results_path, file_name + rx_tx)
+ with open(filec, "a") as result_file:
+ if 'rx' in rx_tx:
+ result_file.write('Time,RxPrt,DrpFrCnt,SeqRnLen,AvgLat,' +
+ 'DrpFrRate,FrCnt,FrRate,MaxLat,MinLat,' +
+ 'OctCnt,OctRate\n')
+ else:
+ result_file.write('Time,StrId,BlkId,FrCnt,FrRate,ERxFrCnt,' +
+ 'OctCnt,OctRate,bitCnt,bitRate\n')
+
+
+def write_rx_live_results_to_file(results_path, file_name, results):
+ """ Write live results from the rx-ports"""
+ filec = os.path.join(results_path, file_name + ".rx")
+ with open(filec, "a") as result_file:
+ result_file.write('{0},{3},{1},{2},{4},{5},{6},{7},{8},{9},{10},{11}\n'
+ .format(time.time(), results['DroppedFrameCount'],
+ results['SeqRunLength'], results['RxPort'],
+ results['AvgLatency'],
+ results['DroppedFrameRate'],
+ results['FrameCount'], results['FrameRate'],
+ results['MaxLatency'], results['MinLatency'],
+ results['OctetCount'], results['OctetRate']))
+
+
+def write_tx_live_results_to_file(results_path, file_name, results):
+ """ Write live results from the tx-ports"""
+ filec = os.path.join(results_path, file_name + ".tx")
+ with open(filec, "a") as result_file:
+ result_file.write('{0},{1},{9},{2},{3},{4},{5},{6},{7},{8}\n'
+ .format(time.time(), results['StreamId'],
+ results['FrameCount'], results['FrameRate'],
+ results['ExpectedRxFrameCount'],
+ results['OctetCount'], results['OctetRate'],
+ results['BitCount'], results['BitRate'],
+ results['BlockId']))
+
+
def positive_int(value):
""" Positive Integer type for Arguments """
ivalue = int(value)
@@ -68,7 +134,8 @@ def percent_float(value):
"%s not in range [0.0, 100.0]" % pvalue)
return pvalue
-# pylint: disable=too-many-branches, too-many-statements
+
+# pylint: disable=too-many-branches, too-many-statements, too-many-locals
def main():
""" Read the arguments, Invoke Test and Return the results"""
parser = argparse.ArgumentParser()
@@ -146,6 +213,11 @@ def main():
default="./Results",
help="The directory to copy results to",
dest="results_dir")
+ optional_named.add_argument("--vsperf_results_dir",
+ required=False,
+ default="./Results",
+ help="The directory to copy results to",
+ dest="vsperf_results_dir")
optional_named.add_argument("--csv_results_file_prefix",
required=False,
default="Rfc2544Tput",
@@ -269,6 +341,27 @@ def main():
"the first emulated device interface"
"on the first west port"),
dest="west_intf_gateway_addr")
+ optional_named.add_argument("--latency_histogram",
+ required=False,
+ action="store_true",
+ help="latency histogram is required in output?",
+ dest="latency_histogram")
+ optional_named.add_argument("--imix",
+ required=False,
+ default="",
+ help=("IMIX specification as genome"
+ "Encoding - RFC 6985"),
+ dest="imix")
+ optional_named.add_argument("--live_results",
+ required=False,
+ action="store_true",
+ help="Live Results required?",
+ dest="live_results")
+ optional_named.add_argument("--logfile",
+ required=False,
+ default="./traffic_gen.log",
+ help="Log file to log live results",
+ dest="logfile")
parser.add_argument("-v",
"--verbose",
required=False,
@@ -309,6 +402,7 @@ def main():
_LOGGER.debug("SpirentTestCenter system version: %s",
stc.get("system1", "version"))
+ # pylint: disable=too-many-nested-blocks
try:
device_list = []
port_list = []
@@ -325,6 +419,10 @@ def main():
_LOGGER.debug("Creating project ...")
project = stc.get("System1", "children-Project")
+ # Configure the Result view
+ resultopts = stc.get('project1', 'children-resultoptions')
+ stc.config(resultopts, {'ResultViewMode': 'BASIC'})
+
# Configure any custom traffic parameters
if args.traffic_custom == "cont":
if args.verbose:
@@ -353,7 +451,9 @@ def main():
east_chassis_port})
# Create the DeviceGenEthIIIfParams object
stc.create("DeviceGenEthIIIfParams",
- under=east_device_gen_params)
+ under=east_device_gen_params,
+ attributes={'UseDefaultPhyMac': True})
+
# Configuring Ipv4 interfaces
stc.create("DeviceGenIpv4IfParams",
under=east_device_gen_params,
@@ -374,7 +474,9 @@ def main():
west_chassis_port})
# Create the DeviceGenEthIIIfParams object
stc.create("DeviceGenEthIIIfParams",
- under=west_device_gen_params)
+ under=west_device_gen_params,
+ attributes={'UseDefaultPhyMac': True})
+
# Configuring Ipv4 interfaces
stc.create("DeviceGenIpv4IfParams",
under=west_device_gen_params,
@@ -390,6 +492,45 @@ def main():
if args.verbose:
_LOGGER.debug(device_list)
+ # Configure Histogram
+ if args.latency_histogram:
+ # Generic Configuration
+ histResOptions = stc.get("project1", 'children-ResultOptions')
+ stc.config(histResOptions, {'ResultViewMode': 'HISTOGRAM'})
+ # East Port Configuration
+ histAnaEast = stc.get(east_chassis_port, 'children-Analyzer')
+ histAnaEastConfig = stc.get(histAnaEast, 'children-AnalyzerConfig')
+ stc.config(histAnaEastConfig, {'HistogramMode': 'LATENCY'})
+ eLatHist = stc.get(histAnaEastConfig, 'children-LatencyHistogram')
+ stc.config(eLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE',
+ 'BucketSizeUnit': 'ten_nanoseconds',
+ 'Active': 'TRUE',
+ 'DistributionMode': 'CENTERED_MODE'})
+ # West Port Configuration
+ histAnaWest = stc.get(west_chassis_port, 'children-Analyzer')
+ histAnaWestConfig = stc.get(histAnaWest, 'children-AnalyzerConfig')
+ stc.config(histAnaWestConfig, {'HistogramMode': 'LATENCY'})
+ wLatHist = stc.get(histAnaWestConfig, 'children-LatencyHistogram')
+ stc.config(wLatHist, {'ConfigMode': 'CONFIG_LIMIT_MODE',
+ 'BucketSizeUnit': 'ten_nanoseconds',
+ 'Active': 'TRUE',
+ 'DistributionMode': 'CENTERED_MODE'})
+ gBucketSizeList = stc.get(wLatHist, 'BucketSizeList')
+ # gLimitSizeList = stc.get(wLatHist, 'LimitList')
+
+ # IMIX configuration
+ fld = None
+ if args.imix:
+ args.frame_size_list = []
+ weights = genome2weights(args.imix)
+ fld = stc.create('FrameLengthDistribution', under=project)
+ def_slots = stc.get(fld, "children-framelengthdistributionslot")
+ stc.perform("Delete", params={"ConfigList": def_slots})
+ for fsize in weights:
+ stc.create('framelengthdistributionslot', under=fld,
+ attributes={'FixedFrameLength': fsize,
+ 'Weight': weights[fsize]})
+
# Create the RFC 2544 'metric test
if args.metric == "throughput":
if args.verbose:
@@ -407,7 +548,8 @@ def main():
"RateUpperLimit": args.rate_upper_limit_pct,
"Resolution": args.resolution_pct,
"SearchMode": args.search_mode,
- "TrafficPattern": args.traffic_pattern})
+ "TrafficPattern": args.traffic_pattern,
+ "FrameSizeDistributionList": fld})
elif args.metric == "backtoback":
stc.perform("Rfc2544SetupBackToBackTestCommand",
params={"AcceptableFrameLoss":
@@ -467,24 +609,133 @@ def main():
_LOGGER.debug("Apply configuration...")
stc.apply()
+ # Register for the results
+ hResDataRx = stc.create('ResultDataSet', under='project1')
+ strmBlockList = stc.get('project1', 'children-streamblock')
+ stc.create('ResultQuery', under=hResDataRx, attributes={
+ 'ResultRootList': strmBlockList,
+ 'ConfigClassId': 'StreamBlock',
+ 'ResultClassId': 'RxStreamSummaryResults',
+ 'PropertyIdArray': "RxStreamSummaryResults.RxPort \
+ RxStreamSummaryResults.AvgLatency \
+ RxStreamSummaryResults.BitCount \
+ RxStreamSummaryResults.BitRate \
+ RxStreamSummaryResults.DroppedFrameCount\
+ RxStreamSummaryResults.DroppedFrameRate \
+ RxStreamSummaryResults.FrameCount \
+ RxStreamSummaryResults.FrameRate \
+ RxStreamSummaryResults.MaxLatency \
+ RxStreamSummaryResults.MinLatency \
+ RxStreamSummaryResults.OctetCount \
+ RxStreamSummaryResults.OctetRate \
+ RxStreamSummaryResults.SeqRunLength"})
+ hResDataTx = stc.create('ResultDataSet', under='project1')
+ strmBlockList = stc.get('project1', 'children-streamblock')
+ stc.create('ResultQuery', under=hResDataTx, attributes={
+ 'ResultRootList': strmBlockList,
+ 'ConfigClassId': 'StreamBlock',
+ 'ResultClassId': 'TxStreamResults',
+ 'PropertyIdArray': "TxStreamResults.BlockId \
+ TxStreamResults.BitCount \
+ TxStreamResults.BitRate \
+ TxStreamResults.FrameCount \
+ TxStreamResults.FrameRate \
+ TxStreamResults.OctetCount \
+ TxStreamResults.OctetRate"})
+ stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataRx})
+ stc.perform('ResultDataSetSubscribe', params={'ResultDataSet': hResDataTx})
+ time.sleep(3)
+ stc.perform('RefreshResultView', params={'ResultDataSet': hResDataTx})
+ hndListRx = stc.get(hResDataRx, 'ResultHandleList')
+ hndListTx = stc.get(hResDataTx, 'ResultHandleList')
+
if args.verbose:
_LOGGER.debug("Starting the sequencer...")
stc.perform("SequencerStart")
- # Wait for sequencer to finish
- _LOGGER.info(
- "Starting test... Please wait for the test to complete...")
- stc.wait_until_complete()
+ sequencer = stc.get("system1", "children-sequencer")
+ state = stc.get(sequencer, 'State')
+
+ # If Live-results are required, we don't wait for the test to complete
+ if args.live_results:
+ write_headers(args.vsperf_results_dir, args.logfile, '.rx')
+ write_headers(args.vsperf_results_dir, args.logfile, '.tx')
+ while state != 'IDLE':
+ state = stc.get(sequencer, 'State')
+ hndListTx = stc.get(hResDataTx, 'ResultHandleList')
+ if hndListTx:
+ handles = hndListTx.split(' ')
+ for handle in handles:
+ tx_values = stc.get(handle)
+ write_tx_live_results_to_file(args.vsperf_results_dir,
+ args.logfile,
+ tx_values)
+ if hndListRx:
+ handles = hndListRx.split(' ')
+ for handle in handles:
+ rx_values = stc.get(handle)
+ write_rx_live_results_to_file(args.vsperf_results_dir,
+ args.logfile,
+ rx_values)
+ time.sleep(1)
+ # Live results not needed, so just wait!
+ else:
+ # Wait for sequencer to finish
+ _LOGGER.info(
+ "Starting test... Please wait for the test to complete...")
+ stc.wait_until_complete()
+
_LOGGER.info("The test has completed... Saving results...")
# Determine what the results database filename is...
lab_server_resultsdb = stc.get(
"system1.project.TestResultSetting", "CurrentResultFileName")
+ if not lab_server_resultsdb or 'Results' not in lab_server_resultsdb:
+ _LOGGER.info("Failed to find results.")
+ stc.end_session()
+ return
+
if args.verbose:
_LOGGER.debug("The lab server results database is %s",
lab_server_resultsdb)
+ # Create Latency Histogram CSV file()
+ if args.latency_histogram:
+ hist_dict_counts = {}
+ for file_url in stc.files():
+ if '-FrameSize-' in file_url:
+ stc.download(file_url)
+ filename = file_url.split('/')[-1]
+ if os.path.exists(os.getcwd() + '/' + filename):
+ conn = sqlite3.connect(os.getcwd() + '/' + filename)
+ # cursor = conn.execute(
+ # 'select * from RxEotStreamResults')
+ # names = [desc[0] for desc in cursor.description]
+ counts = conn.execute("SELECT \
+ HistBin1Count, HistBin2Count,\
+ HistBin3Count, HistBin4Count,\
+ HistBin5Count, HistBin6Count,\
+ HistBin7Count, HistBin8Count,\
+ HistBin9Count, HistBin10Count,\
+ HistBin11Count, HistBin12Count,\
+ HistBin13Count, HistBin14Count, \
+ HistBin15Count, HistBin16Count \
+ from RxEotStreamResults")
+ strs = filename.split('-')
+ key = strs[strs.index('FrameSize')+1]
+ if key in hist_dict_counts:
+ hist_dict_counts[key] = [a+b for a, b in
+ zip(counts.fetchone(),
+ hist_dict_counts[key])]
+ else:
+ hist_dict_counts[key] = counts.fetchone()
+ conn.close()
+
+ write_histogram_to_csv(args.vsperf_results_dir, 'Histogram',
+ hist_dict_counts,
+ gBucketSizeList)
+
stc.perform("CSSynchronizeFiles",
params={"DefaultDownloadDir": args.results_dir})
@@ -565,6 +816,7 @@ def main():
args.results_dir, args.csv_results_file_prefix, resultsdict)
except RuntimeError as e:
+ stc.end_session()
_LOGGER.error(e)
if args.verbose:
diff --git a/tools/pkt_gen/testcenter/testcenter.py b/tools/pkt_gen/testcenter/testcenter.py
index 9980ae7c..a15c502c 100644
--- a/tools/pkt_gen/testcenter/testcenter.py
+++ b/tools/pkt_gen/testcenter/testcenter.py
@@ -98,7 +98,9 @@ def get_rfc2544_common_settings():
"--trial_duration_sec",
settings.getValue("TRAFFICGEN_STC_TRIAL_DURATION_SEC"),
"--traffic_pattern",
- settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN")]
+ settings.getValue("TRAFFICGEN_STC_TRAFFIC_PATTERN"),
+ "--vsperf_results_dir",
+ settings.getValue("RESULTS_PATH")]
return args
@@ -169,6 +171,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
Spirent TestCenter
"""
_logger = logging.getLogger(__name__)
+ _liveresults_file = settings.getValue("TRAFFICGEN_STC_LIVERESULTS_FILE")
def connect(self):
"""
@@ -182,7 +185,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
"""
pass
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""
Do nothing.
"""
@@ -246,8 +249,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
row["ForwardingRate(fps)"])
return result
- # pylint: disable=unused-argument
- def send_rfc2889_forwarding(self, traffic=None, tests=1, duration=20):
+ def send_rfc2889_forwarding(self, traffic=None, tests=1, _duration=20):
"""
Send traffic per RFC2889 Forwarding test specifications.
"""
@@ -257,7 +259,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
framesize = traffic['l2']['framesize']
args = get_rfc2889_common_settings(framesize, tests,
traffic['traffic_type'])
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.debug("Arguments used to call test: %s", args)
@@ -273,7 +275,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
return self.get_rfc2889_forwarding_results(filec)
- def send_rfc2889_caching(self, traffic=None, tests=1, duration=20):
+ def send_rfc2889_caching(self, traffic=None, tests=1, _duration=20):
"""
Send as per RFC2889 Addr-Caching test specifications.
"""
@@ -286,7 +288,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
custom_args = get_rfc2889_custom_settings()
args = common_args + custom_args
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.debug("Arguments used to call test: %s", args)
@@ -302,7 +304,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
return self.get_rfc2889_addr_caching_results(filec)
- def send_rfc2889_learning(self, traffic=None, tests=1, duration=20):
+ def send_rfc2889_learning(self, traffic=None, tests=1, _duration=20):
"""
Send traffic per RFC2889 Addr-Learning test specifications.
"""
@@ -315,7 +317,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
custom_args = get_rfc2889_custom_settings()
args = common_args + custom_args
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.debug("Arguments used to call test: %s", args)
@@ -331,11 +333,13 @@ class TestCenter(trafficgen.ITrafficGenerator):
return self.get_rfc2889_addr_learning_results(filec)
- def get_rfc2544_results(self, filename):
+ def get_rfc2544_results(self, filename, genome=None):
"""
Reads the CSV file and return the results
"""
result = {}
+ if not os.path.exists(filename):
+ return result
with open(filename, "r") as csvfile:
csvreader = csv.DictReader(csvfile)
for row in csvreader:
@@ -366,6 +370,10 @@ class TestCenter(trafficgen.ITrafficGenerator):
row["AverageLatency(us)"]) * 1000
result[ResultsConstants.FRAME_LOSS_PERCENT] = float(
row["PercentLoss"])
+ if genome:
+ result[ResultsConstants.IMIX_GENOME] = genome
+ result[ResultsConstants.IMIX_AVG_FRAMESIZE] = float(
+ row["AvgFrameSize"])
return result
def send_cont_traffic(self, traffic=None, duration=30):
@@ -387,7 +395,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
custom, 1)
args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.debug("Arguments used to call test: %s", args)
@@ -420,7 +428,25 @@ class TestCenter(trafficgen.ITrafficGenerator):
tests)
args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if traffic and 'latency_histogram' in traffic:
+ if traffic['latency_histogram']['enabled']:
+ if traffic['latency_histogram']['type'] == 'Default':
+ args.append("--latency_histogram")
+
+ genome = ''
+ if traffic and 'imix' in traffic:
+ if traffic['imix']['enabled']:
+ if traffic['imix']['type'] == 'genome':
+ genome = traffic['imix']['genome']
+ args.append('--imix')
+ args.append(genome)
+
+ if settings.getValue("TRAFFICGEN_STC_LIVE_RESULTS") == "True":
+ args.append('--live_results')
+ args.append('--logfile')
+ args.append(self._liveresults_file)
+
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.debug("Arguments used to call test: %s", args)
@@ -434,7 +460,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
if verbose:
self._logger.info("file: %s", filec)
- return self.get_rfc2544_results(filec)
+ return self.get_rfc2544_results(filec, genome)
def send_rfc2544_back2back(self, traffic=None, tests=1, duration=20,
lossrate=0.0):
@@ -453,7 +479,7 @@ class TestCenter(trafficgen.ITrafficGenerator):
tests)
args = rfc2544_common_args + stc_common_args + rfc2544_custom_args
- if settings.getValue("TRAFFICGEN_STC_VERBOSE") is "True":
+ if settings.getValue("TRAFFICGEN_STC_VERBOSE") == "True":
args.append("--verbose")
verbose = True
self._logger.info("Arguments used to call test: %s", args)
@@ -498,4 +524,5 @@ if __name__ == '__main__':
}
with TestCenter() as dev:
print(dev.send_rfc2544_throughput(traffic=TRAFFIC))
+ # pylint: disable=no-member
print(dev.send_rfc2544_backtoback(traffic=TRAFFIC))
diff --git a/tools/pkt_gen/trafficgen/trafficgen.py b/tools/pkt_gen/trafficgen/trafficgen.py
index 262df71d..a6f7edcc 100755
--- a/tools/pkt_gen/trafficgen/trafficgen.py
+++ b/tools/pkt_gen/trafficgen/trafficgen.py
@@ -81,15 +81,14 @@ class ITrafficGenerator(object):
"""
raise NotImplementedError('Please call an implementation.')
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""Send a burst of traffic.
- Send a ``numpkts`` packets of traffic, using ``traffic``
+ Send a ``traffic['burst_size']`` packets of traffic, using ``traffic``
configuration, for ``duration`` seconds.
Attributes:
:param traffic: Detailed "traffic" spec, see design docs for details
- :param numpkts: Number of packets to send
:param duration: Time to wait to receive packets
:returns: dictionary of strings with following data:
diff --git a/tools/pkt_gen/trex/trex.py b/tools/pkt_gen/trex/trex_client.py
index e0ce4c48..3d6836d8 100644
--- a/tools/pkt_gen/trex/trex.py
+++ b/tools/pkt_gen/trex/trex_client.py
@@ -15,16 +15,18 @@
"""
Trex Traffic Generator Model
"""
+
# pylint: disable=undefined-variable
import logging
import subprocess
import sys
import time
import os
+import re
from collections import OrderedDict
# pylint: disable=unused-import
import netaddr
-import zmq
+#import zmq
from conf import settings
from conf import merge_spec
from core.results.results_constants import ResultsConstants
@@ -33,7 +35,7 @@ try:
# pylint: disable=wrong-import-position, import-error
sys.path.append(settings.getValue('PATHS')['trafficgen']['Trex']['src']['path'])
from trex_stl_lib.api import *
- from trex_stl_lib import trex_stl_exceptions
+ # from trex_stl_lib import trex_stl_exceptions
except ImportError:
# VSPERF performs detection of T-Rex api during testcase initialization. So if
# T-Rex is requsted and API is not available it will fail before this code
@@ -69,6 +71,20 @@ _EMPTY_STATS = {
'tx_pps': 0.0,
'tx_util': 0.0,}}
+# Default frame definition, which can be overridden by TRAFFIC['scapy'].
+# The content of the frame and its network layers are driven by TRAFFIC
+# dictionary, i.e. 'l2', 'l3, 'l4' and 'vlan' parts.
+_SCAPY_FRAME = {
+ '0' : 'Ether(src={Ether_src}, dst={Ether_dst})/'
+ 'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+ 'IP(proto={IP_proto}, src={IP_src}, dst={IP_dst})/'
+ '{IP_PROTO}(sport={IP_PROTO_sport}, dport={IP_PROTO_dport})',
+ '1' : 'Ether(src={Ether_dst}, dst={Ether_src})/'
+ 'Dot1Q(prio={Dot1Q_prio}, id={Dot1Q_id}, vlan={Dot1Q_vlan})/'
+ 'IP(proto={IP_proto}, src={IP_dst}, dst={IP_src})/'
+ '{IP_PROTO}(sport={IP_PROTO_dport}, dport={IP_PROTO_sport})',
+}
+
class Trex(ITrafficGenerator):
"""Trex Traffic generator wrapper."""
@@ -144,7 +160,7 @@ class Trex(ITrafficGenerator):
try:
self._stlclient = STLClient(username=self._trex_user, server=self._trex_host_ip_addr,
- verbose_level=0)
+ verbose_level='info')
self._stlclient.connect()
except STLError:
raise RuntimeError('T-Rex: Cannot connect to T-Rex server. Please check if it is '
@@ -165,35 +181,77 @@ class Trex(ITrafficGenerator):
self._logger.info("T-Rex: In trex disconnect method")
self._stlclient.disconnect(stop_traffic=True, release_ports=True)
- @staticmethod
- def create_packets(traffic, ports_info):
+ def create_packets(self, traffic, ports_info):
"""Create base packet according to traffic specification.
If traffic haven't specified srcmac and dstmac fields
- packet will be create with mac address of trex server.
+ packet will be created with mac address of trex server.
"""
- mac_add = [li['hw_mac'] for li in ports_info]
-
- if traffic and traffic['l2']['framesize'] > 0:
- if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
- traffic['l2']['srcmac'] == '00:00:00:00:00:00':
- base_pkt_a = Ether(src=mac_add[0], dst=mac_add[1])/ \
- IP(proto=traffic['l3']['proto'], src=traffic['l3']['srcip'],
- dst=traffic['l3']['dstip'])/ \
- UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
- base_pkt_b = Ether(src=mac_add[1], dst=mac_add[0])/ \
- IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
- dst=traffic['l3']['srcip'])/ \
- UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
- else:
- base_pkt_a = Ether(src=traffic['l2']['srcmac'], dst=traffic['l2']['dstmac'])/ \
- IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
- dst=traffic['l3']['srcip'])/ \
- UDP(dport=traffic['l4']['dstport'], sport=traffic['l4']['srcport'])
+ if not traffic or traffic['l2']['framesize'] <= 0:
+ return (None, None)
- base_pkt_b = Ether(src=traffic['l2']['dstmac'], dst=traffic['l2']['srcmac'])/ \
- IP(proto=traffic['l3']['proto'], src=traffic['l3']['dstip'],
- dst=traffic['l3']['srcip'])/ \
- UDP(dport=traffic['l4']['srcport'], sport=traffic['l4']['dstport'])
+ if traffic['l2']['dstmac'] == '00:00:00:00:00:00' and \
+ traffic['l2']['srcmac'] == '00:00:00:00:00:00':
+
+ mac_add = [li['hw_mac'] for li in ports_info]
+ src_mac = mac_add[0]
+ dst_mac = mac_add[1]
+ else:
+ src_mac = traffic['l2']['srcmac']
+ dst_mac = traffic['l2']['dstmac']
+
+ if traffic['scapy']['enabled']:
+ base_pkt_a = traffic['scapy']['0']
+ base_pkt_b = traffic['scapy']['1']
+ else:
+ base_pkt_a = _SCAPY_FRAME['0']
+ base_pkt_b = _SCAPY_FRAME['1']
+
+ # check and remove network layers disabled by TRAFFIC dictionary
+ # Note: In general, it is possible to remove layers from scapy object by
+ # e.g. del base_pkt_a['IP']. However it doesn't work for all layers
+ # (e.g. Dot1Q). Thus it is safer to modify string with scapy frame definition
+ # directly, before it is converted to the real scapy object.
+ if not traffic['vlan']['enabled']:
+ self._logger.info('VLAN headers are disabled by TRAFFIC')
+ base_pkt_a = re.sub(r'(^|\/)Dot1Q?\([^\)]*\)', '', base_pkt_a)
+ base_pkt_b = re.sub(r'(^|\/)Dot1Q?\([^\)]*\)', '', base_pkt_b)
+ if not traffic['l3']['enabled']:
+ self._logger.info('IP headers are disabled by TRAFFIC')
+ base_pkt_a = re.sub(r'(^|\/)IP(v6)?\([^\)]*\)', '', base_pkt_a)
+ base_pkt_b = re.sub(r'(^|\/)IP(v6)?\([^\)]*\)', '', base_pkt_b)
+ if not traffic['l4']['enabled']:
+ self._logger.info('%s headers are disabled by TRAFFIC',
+ traffic['l3']['proto'].upper())
+ base_pkt_a = re.sub(r'(^|\/)(UDP|TCP|SCTP|{{IP_PROTO}}|{})\([^\)]*\)'.format(
+ traffic['l3']['proto'].upper()), '', base_pkt_a)
+ base_pkt_b = re.sub(r'(^|\/)(UDP|TCP|SCTP|{{IP_PROTO}}|{})\([^\)]*\)'.format(
+ traffic['l3']['proto'].upper()), '', base_pkt_b)
+
+ # pylint: disable=eval-used
+ base_pkt_a = eval(base_pkt_a.format(
+ Ether_src=repr(src_mac),
+ Ether_dst=repr(dst_mac),
+ Dot1Q_prio=traffic['vlan']['priority'],
+ Dot1Q_id=traffic['vlan']['cfi'],
+ Dot1Q_vlan=traffic['vlan']['id'],
+ IP_proto=repr(traffic['l3']['proto']),
+ IP_PROTO=traffic['l3']['proto'].upper(),
+ IP_src=repr(traffic['l3']['srcip']),
+ IP_dst=repr(traffic['l3']['dstip']),
+ IP_PROTO_sport=traffic['l4']['srcport'],
+ IP_PROTO_dport=traffic['l4']['dstport']))
+ base_pkt_b = eval(base_pkt_b.format(
+ Ether_src=repr(src_mac),
+ Ether_dst=repr(dst_mac),
+ Dot1Q_prio=traffic['vlan']['priority'],
+ Dot1Q_id=traffic['vlan']['cfi'],
+ Dot1Q_vlan=traffic['vlan']['id'],
+ IP_proto=repr(traffic['l3']['proto']),
+ IP_PROTO=traffic['l3']['proto'].upper(),
+ IP_src=repr(traffic['l3']['srcip']),
+ IP_dst=repr(traffic['l3']['dstip']),
+ IP_PROTO_sport=traffic['l4']['srcport'],
+ IP_PROTO_dport=traffic['l4']['dstport']))
return (base_pkt_a, base_pkt_b)
@@ -248,25 +306,53 @@ class Trex(ITrafficGenerator):
pkt_a = STLPktBuilder(pkt=base_pkt_a / payload_a)
pkt_b = STLPktBuilder(pkt=base_pkt_b / payload_b)
- stream_1 = STLStream(packet=pkt_a,
- name='stream_1',
- mode=STLTXCont(percentage=traffic['frame_rate']))
- stream_2 = STLStream(packet=pkt_b,
- name='stream_2',
- mode=STLTXCont(percentage=traffic['frame_rate']))
lat_pps = settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS')
- if lat_pps > 0:
- stream_1_lat = STLStream(packet=pkt_a,
+ if traffic['traffic_type'] == 'burst':
+ if lat_pps > 0:
+ # latency statistics are requested; in case of frame burst we can enable
+ # statistics for all frames
+ stream_1 = STLStream(packet=pkt_a,
flow_stats=STLFlowLatencyStats(pg_id=0),
- name='stream_1_lat',
- mode=STLTXCont(pps=lat_pps))
- stream_2_lat = STLStream(packet=pkt_b,
+ name='stream_1',
+ mode=STLTXSingleBurst(percentage=traffic['frame_rate'],
+ total_pkts=traffic['burst_size']))
+ stream_2 = STLStream(packet=pkt_b,
flow_stats=STLFlowLatencyStats(pg_id=1),
- name='stream_2_lat',
- mode=STLTXCont(pps=lat_pps))
+ name='stream_2',
+ mode=STLTXSingleBurst(percentage=traffic['frame_rate'],
+ total_pkts=traffic['burst_size']))
+ else:
+ stream_1 = STLStream(packet=pkt_a,
+ name='stream_1',
+ mode=STLTXSingleBurst(percentage=traffic['frame_rate'],
+ total_pkts=traffic['burst_size']))
+ stream_2 = STLStream(packet=pkt_b,
+ name='stream_2',
+ mode=STLTXSingleBurst(percentage=traffic['frame_rate'],
+ total_pkts=traffic['burst_size']))
+ else:
+ stream_1 = STLStream(packet=pkt_a,
+ name='stream_1',
+ mode=STLTXCont(percentage=traffic['frame_rate']))
+ stream_2 = STLStream(packet=pkt_b,
+ name='stream_2',
+ mode=STLTXCont(percentage=traffic['frame_rate']))
+ # workaround for latency statistics, which can't be enabled for streams
+ # with high framerate due to the huge performance impact
+ if lat_pps > 0:
+ stream_1_lat = STLStream(packet=pkt_a,
+ flow_stats=STLFlowLatencyStats(pg_id=0),
+ name='stream_1_lat',
+ mode=STLTXCont(pps=lat_pps))
+ stream_2_lat = STLStream(packet=pkt_b,
+ flow_stats=STLFlowLatencyStats(pg_id=1),
+ name='stream_2_lat',
+ mode=STLTXCont(pps=lat_pps))
return (stream_1, stream_2, stream_1_lat, stream_2_lat)
+
+ # pylint: disable=too-many-locals, too-many-statements
def generate_traffic(self, traffic, duration, disable_capture=False):
"""The method that generate a stream
"""
@@ -293,13 +379,13 @@ class Trex(ITrafficGenerator):
# since we can only control both ports at once take the lower of the two
max_speed = min(max_speed_1, max_speed_2)
gbps_speed = (max_speed / 1000) * (float(traffic['frame_rate']) / 100.0)
- self._logger.debug('Starting traffic at %s Gpbs speed', gbps_speed)
+ self._logger.debug('Starting traffic at %s Gbps speed', gbps_speed)
# for SR-IOV
if settings.getValue('TRAFFICGEN_TREX_PROMISCUOUS'):
self._stlclient.set_port_attr(my_ports, promiscuous=True)
- packet_1, packet_2 = Trex.create_packets(traffic, ports_info)
+ packet_1, packet_2 = self.create_packets(traffic, ports_info)
self.show_packet_info(packet_1, packet_2)
stream_1, stream_2, stream_1_lat, stream_2_lat = Trex.create_streams(packet_1, packet_2, traffic)
self._stlclient.add_streams(stream_1, ports=[0])
@@ -330,7 +416,70 @@ class Trex(ITrafficGenerator):
core_mask=self._stlclient.CORE_MASK_PIN)
except STLError:
self._stlclient.start(ports=my_ports, force=True, duration=duration, mult="{}gbps".format(gbps_speed))
- self._stlclient.wait_on_traffic(ports=my_ports)
+
+ if settings.getValue('TRAFFICGEN_TREX_LIVE_RESULTS'):
+ filec = os.path.join(settings.getValue('RESULTS_PATH'),
+ settings.getValue('TRAFFICGEN_TREX_LC_FILE'))
+ filee = os.path.join(settings.getValue('RESULTS_PATH'),
+ settings.getValue('TRAFFICGEN_TREX_LE_FILE'))
+ pgids = self._stlclient.get_active_pgids()
+ rx_port_0 = 1
+ tx_port_0 = 0
+ rx_port_1 = 0
+ tx_port_1 = 1
+ with open(filec, 'a') as fcp, open(filee, 'a') as fep:
+ fcp.write("ts,rx_port,tx_port,rx_pkts,tx_pkts,rx_pps,tx_pps,"+
+ "rx_bps_num,rx_bps_den,tx_bps_num,tx_bps_den\n")
+ fep.write('ts,dropped,ooo,dup,seq_too_high,seq_too_low\n')
+ while True:
+ tr_status = self._stlclient.is_traffic_active(ports=my_ports)
+ if not tr_status:
+ break
+ time.sleep(1)
+ stats = self._stlclient.get_pgid_stats(pgids['flow_stats'])
+ lat_stats = stats['latency'].get(0)
+ flow_stats_0 = stats['flow_stats'].get(0)
+ flow_stats_1 = stats['flow_stats'].get(1)
+ if flow_stats_0:
+ rx_pkts = flow_stats_0['rx_pkts'][rx_port_0]
+ tx_pkts = flow_stats_0['tx_pkts'][tx_port_0]
+ rx_pps = flow_stats_0['rx_pps'][rx_port_0]
+ tx_pps = flow_stats_0['tx_pps'][tx_port_0]
+ rx_bps = flow_stats_0['rx_bps'][rx_port_0]
+ tx_bps = flow_stats_0['tx_bps'][tx_port_0]
+ rx_bps_l1 = flow_stats_0['rx_bps_l1'][rx_port_0]
+ tx_bps_l1 = flow_stats_0['tx_bps_l1'][tx_port_0]
+ # https://github.com/cisco-system-traffic-generator/\
+ # trex-core/blob/master/scripts/automation/\
+ # trex_control_plane/interactive/trex/examples/\
+ # stl/stl_flow_latency_stats.py
+ fcp.write("{10},{8},{9},{0},{1},{2},{3},{4},{5},{6},{7}\n"
+ .format(rx_pkts, tx_pkts, rx_pps, tx_pps,
+ rx_bps, rx_bps_l1, tx_bps, tx_bps_l1,
+ rx_port_0, tx_port_0, time.time()))
+ if flow_stats_1:
+ rx_pkts = flow_stats_1['rx_pkts'][rx_port_1]
+ tx_pkts = flow_stats_1['tx_pkts'][tx_port_1]
+ rx_pps = flow_stats_1['rx_pps'][rx_port_1]
+ tx_pps = flow_stats_1['tx_pps'][tx_port_1]
+ rx_bps = flow_stats_1['rx_bps'][rx_port_1]
+ tx_bps = flow_stats_1['tx_bps'][tx_port_1]
+ rx_bps_l1 = flow_stats_1['rx_bps_l1'][rx_port_1]
+ tx_bps_l1 = flow_stats_1['tx_bps_l1'][tx_port_1]
+ fcp.write("{10},{8},{9},{0},{1},{2},{3},{4},{5},{6},{7}\n"
+ .format(rx_pkts, tx_pkts, rx_pps, tx_pps,
+ rx_bps, rx_bps_l1, tx_bps, tx_bps_l1,
+ rx_port_1, tx_port_1, time.time()))
+ if lat_stats:
+ drops = lat_stats['err_cntrs']['dropped']
+ ooo = lat_stats['err_cntrs']['out_of_order']
+ dup = lat_stats['err_cntrs']['dup']
+ sth = lat_stats['err_cntrs']['seq_too_high']
+ stl = lat_stats['err_cntrs']['seq_too_low']
+ fep.write('{5},{0},{1},{2},{3},{4}\n'
+ .format(drops, ooo, dup, sth, stl, time.time()))
+ else:
+ self._stlclient.wait_on_traffic(ports=my_ports)
stats = self._stlclient.get_stats(sync_now=True)
# export captured data into pcap file if possible
@@ -382,20 +531,29 @@ class Trex(ITrafficGenerator):
result[ResultsConstants.FRAME_LOSS_PERCENT] = 100
if settings.getValue('TRAFFICGEN_TREX_LATENCY_PPS') > 0 and stats['latency']:
- result[ResultsConstants.MIN_LATENCY_NS] = (
- '{:.3f}'.format(
- (float(min(stats["latency"][0]["latency"]["total_min"],
- stats["latency"][1]["latency"]["total_min"])))))
-
- result[ResultsConstants.MAX_LATENCY_NS] = (
- '{:.3f}'.format(
- (float(max(stats["latency"][0]["latency"]["total_max"],
- stats["latency"][1]["latency"]["total_max"])))))
-
- result[ResultsConstants.AVG_LATENCY_NS] = (
- '{:.3f}'.format(
- float((stats["latency"][0]["latency"]["average"]+
- stats["latency"][1]["latency"]["average"])/2)))
+ try:
+ result[ResultsConstants.MIN_LATENCY_NS] = (
+ '{:.3f}'.format(
+ (float(min(stats["latency"][0]["latency"]["total_min"],
+ stats["latency"][1]["latency"]["total_min"])))))
+ except TypeError:
+ result[ResultsConstants.MIN_LATENCY_NS] = 'Unknown'
+
+ try:
+ result[ResultsConstants.MAX_LATENCY_NS] = (
+ '{:.3f}'.format(
+ (float(max(stats["latency"][0]["latency"]["total_max"],
+ stats["latency"][1]["latency"]["total_max"])))))
+ except TypeError:
+ result[ResultsConstants.MAX_LATENCY_NS] = 'Unknown'
+
+ try:
+ result[ResultsConstants.AVG_LATENCY_NS] = (
+ '{:.3f}'.format(
+ float((stats["latency"][0]["latency"]["average"]+
+ stats["latency"][1]["latency"]["average"])/2)))
+ except TypeError:
+ result[ResultsConstants.AVG_LATENCY_NS] = 'Unknown'
else:
result[ResultsConstants.MIN_LATENCY_NS] = 'Unknown'
@@ -436,9 +594,14 @@ class Trex(ITrafficGenerator):
:return: passing stats as dictionary
"""
threshold = settings.getValue('TRAFFICGEN_TREX_RFC2544_TPUT_THRESHOLD')
+ max_repeat = settings.getValue('TRAFFICGEN_TREX_RFC2544_MAX_REPEAT')
+ loss_verification = settings.getValue('TRAFFICGEN_TREX_RFC2544_BINARY_SEARCH_LOSS_VERIFICATION')
+ if loss_verification:
+ self._logger.info("Running Binary Search with Loss Verification")
stats_ok = _EMPTY_STATS
new_params = copy.deepcopy(traffic)
iteration = 1
+ repeat = 0
left = boundaries['left']
right = boundaries['right']
center = boundaries['center']
@@ -462,11 +625,20 @@ class Trex(ITrafficGenerator):
if test_lossrate == 0.0 and new_params['frame_rate'] == traffic['frame_rate']:
return copy.deepcopy(stats)
elif test_lossrate > lossrate:
+ if loss_verification:
+ if repeat < max_repeat:
+ repeat += 1
+ iteration += 1
+ continue
+ else:
+ repeat = 0
right = center
center = (left + right) / 2
new_params = copy.deepcopy(traffic)
new_params['frame_rate'] = center
else:
+ if loss_verification:
+ repeat = 0
stats_ok = copy.deepcopy(stats)
left = center
center = (left + right) / 2
@@ -568,9 +740,25 @@ class Trex(ITrafficGenerator):
raise NotImplementedError(
'Trex wait rfc2544 throughput not implemented')
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=5):
- raise NotImplementedError(
- 'Trex send burst traffic not implemented')
+ def send_burst_traffic(self, traffic=None, duration=20):
+ """See ITrafficGenerator for description
+ """
+ self._logger.info("In Trex send_burst_traffic method")
+ self._params.clear()
+
+ self._params['traffic'] = self.traffic_defaults.copy()
+ if traffic:
+ self._params['traffic'] = merge_spec(
+ self._params['traffic'], traffic)
+
+ if settings.getValue('TRAFFICGEN_TREX_LEARNING_MODE'):
+ self.learning_packets(traffic)
+ self._logger.info("T-Rex sending traffic")
+ stats = self.generate_traffic(traffic, duration)
+
+ time.sleep(3) # allow packets to complete before reading stats
+
+ return self.calculate_results(stats)
def send_rfc2544_back2back(self, traffic=None, tests=1, duration=30,
lossrate=0.0):
diff --git a/tools/pkt_gen/xena/XenaDriver.py b/tools/pkt_gen/xena/XenaDriver.py
index 6e39e47a..ac9cef1c 100644
--- a/tools/pkt_gen/xena/XenaDriver.py
+++ b/tools/pkt_gen/xena/XenaDriver.py
@@ -30,6 +30,7 @@ through socket commands and returning different statistics.
"""
import locale
import logging
+import math
import socket
import struct
import sys
@@ -86,6 +87,26 @@ CMD_VERSION = 'c_versionno ?'
_LOCALE = locale.getlocale()[1]
_LOGGER = logging.getLogger(__name__)
+class ModSet(object):
+ """
+ Mod set attribute tracker
+ """
+ def __init__(self, **kwargs):
+ """ Constructor
+ All mods default to False
+ :param kwargs: Any class attribute can be set here.
+ """
+ self.mod_src_mac = False
+ self.mod_dst_mac = False
+ self.mod_src_ip = False
+ self.mod_dst_ip = False
+ self.mod_src_port = False
+ self.mod_dst_port = False
+
+ for (key, value) in kwargs.items():
+ if hasattr(self, key):
+ setattr(self, key, value)
+
class SimpleSocket(object):
"""
@@ -170,8 +191,7 @@ class KeepAliveThread(threading.Thread):
self.finished = threading.Event()
self.setDaemon(True)
_LOGGER.debug(
- 'Xena Socket keep alive thread initiated, interval ' +
- '{} seconds'.format(self.interval))
+ 'Xena Socket keep alive thread initiated, interval %s seconds', self.interval)
def stop(self):
""" Thread stop. See python thread docs for more info
@@ -640,57 +660,98 @@ class XenaStream(object):
"""
return self._stream_id
- def enable_multistream(self, flows, layer):
+ def enable_multistream(self, flows, mod_class):
"""
- Basic implementation of multi stream. Enable multi stream by setting
- modifiers on the stream
- :param flows: Numbers of flows or end range
- :param layer: layer to enable multi stream as str. Acceptable values
- are L2, L3, or L4
+ Implementation of multi stream. Enable multi stream by setting
+ modifiers on the stream. If no mods are selected, src_ip mod will be used.
+ :param flows: Numbers of flows, Values greater than 65535 will square rooted
+ to the closest value. Xena mods are limited to 4 bytes.
+ :param mod_class: ModSet object
:return: True if success False otherwise
"""
if not self._header_protocol:
raise RuntimeError(
"Please set a protocol header before calling this method.")
-
- # byte offsets for setting the modifier
- offsets = {
- 'L2': [0, 6],
- 'L3': [32, 36] if 'VLAN' in self._header_protocol else [28, 32],
- 'L4': [38, 40] if 'VLAN' in self._header_protocol else [34, 36]
- }
-
- responses = list()
- if layer in offsets.keys() and flows > 0:
- command = make_port_command(
- CMD_STREAM_MODIFIER_COUNT + ' [{}]'.format(self._stream_id) +
- ' 2', self._xena_port)
- responses.append(self._manager.driver.ask_verify(command))
- command = make_port_command(
- CMD_STREAM_MODIFIER + ' [{},0] {} 0xFFFF0000 INC 1'.format(
- self._stream_id, offsets[layer][0]), self._xena_port)
- responses.append(self._manager.driver.ask_verify(command))
- command = make_port_command(
- CMD_STREAM_MODIFIER_RANGE + ' [{},0] 0 1 {}'.format(
- self._stream_id, flows), self._xena_port)
- responses.append(self._manager.driver.ask_verify(command))
- command = make_port_command(
- CMD_STREAM_MODIFIER + ' [{},1] {} 0xFFFF0000 INC 1'.format(
- self._stream_id, offsets[layer][1]), self._xena_port)
- responses.append(self._manager.driver.ask_verify(command))
- command = make_port_command(
- CMD_STREAM_MODIFIER_RANGE + ' [{},1] 0 1 {}'.format(
- self._stream_id, flows), self._xena_port)
- responses.append(self._manager.driver.ask_verify(command))
- return all(responses) # return True if they all worked
- elif flows < 1:
- _LOGGER.warning(
- 'No flows specified in enable multistream. Bypassing...')
- return False
+ # maximum value for a Xena modifier is 65535 (unsigned int). If flows
+ # is greater than 65535 we have to do two mods getting as close as we
+ # can with square rooting the flow count.
+ if flows > 4294836225:
+ _LOGGER.debug('Flow mods exceeds highest value, changing to 4294836225')
+ flows = 4294836225
+ if flows <= 65535:
+ mod1 = flows
+ mod2 = 0
else:
- raise NotImplementedError(
- "Non-implemented stream layer in method enable multistream ",
- "layer=", layer)
+ mod1, mod2 = int(math.sqrt(flows)), int(math.sqrt(flows))
+ _LOGGER.debug('Flow count modified to %s', mod1*mod2)
+ offset_list = list()
+ if not any([mod_class.mod_src_mac, mod_class.mod_dst_mac, mod_class.mod_src_ip,
+ mod_class.mod_dst_ip, mod_class.mod_src_port, mod_class.mod_dst_port]):
+ # no mods were selected, default to src ip only
+ mod_class.mod_src_ip = True
+ if mod_class.mod_src_mac:
+ offset_list.append(3)
+ if mod_class.mod_dst_mac:
+ offset_list.append(9)
+ if mod_class.mod_src_ip:
+ offset_list.append(32 if 'VLAN' in self._header_protocol else 28)
+ if mod_class.mod_dst_ip:
+ offset_list.append(36 if 'VLAN' in self._header_protocol else 32)
+ if mod_class.mod_src_port:
+ offset_list.append(38 if 'VLAN' in self._header_protocol else 34)
+ if mod_class.mod_dst_port:
+ offset_list.append(40 if 'VLAN' in self._header_protocol else 36)
+ # calculate how many mods we have to do
+ countertotal = len(offset_list)
+ if mod2:
+ # to handle flows greater than 65535 we will need more mods for
+ # layer 2 and 3
+ for mod in [mod_class.mod_src_mac, mod_class.mod_dst_mac,
+ mod_class.mod_src_ip, mod_class.mod_dst_ip]:
+ if mod:
+ countertotal += 1
+ command = make_port_command(
+ CMD_STREAM_MODIFIER_COUNT + ' [{}]'.format(self._stream_id) +
+ ' {}'.format(countertotal), self._xena_port)
+ responses = list()
+ responses.append(self._manager.driver.ask_verify(command))
+ modcounter = 0
+ for offset in offset_list:
+ if (mod_class.mod_dst_port or mod_class.mod_src_port) and \
+ (offset >= 38 if 'VLAN' in self._header_protocol else 34):
+ # only do a 1 mod for udp ports at max 65535
+ newmod1 = 65535 if flows >= 65535 else flows
+ command = make_port_command(
+ CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC 1'.format(
+ self._stream_id, modcounter, offset), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ command = make_port_command(
+ CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+ self._stream_id, modcounter, newmod1 - 1), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ else:
+ command = make_port_command(
+ CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC 1'.format(
+ self._stream_id, modcounter, offset), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ command = make_port_command(
+ CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+ self._stream_id, modcounter, mod1 - 1), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ # if we have a second modifier set the modifier to mod2 and to
+ # incremement once every full rotation of mod 1
+ if mod2:
+ modcounter += 1
+ command = make_port_command(
+ CMD_STREAM_MODIFIER + ' [{},{}] {} 0xFFFF0000 INC {}'.format(
+ self._stream_id, modcounter, offset-2, mod1), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ command = make_port_command(
+ CMD_STREAM_MODIFIER_RANGE + ' [{},{}] 0 1 {}'.format(
+ self._stream_id, modcounter, mod2), self._xena_port)
+ responses.append(self._manager.driver.ask_verify(command))
+ modcounter += 1
+ return all(responses) # return True if they all worked
def get_stream_data(self):
"""
@@ -904,7 +965,7 @@ class XenaRXStats(object):
statdict[entry_id] = self._pack_stats(param, 3)
elif param[1] == 'PR_TPLDS':
tid_list = self._pack_tplds_stats(param, 2)
- if len(tid_list):
+ if tid_list:
statdict['pr_tplds'] = tid_list
elif param[1] == 'PR_TPLDTRAFFIC':
if 'pr_tpldstraffic' in statdict:
diff --git a/tools/pkt_gen/xena/json/xena_json.py b/tools/pkt_gen/xena/json/xena_json.py
index b1eed720..e56b4125 100644
--- a/tools/pkt_gen/xena/json/xena_json.py
+++ b/tools/pkt_gen/xena/json/xena_json.py
@@ -26,10 +26,9 @@ Xena JSON module
from collections import OrderedDict
import locale
import logging
+import math
import os
-import scapy.layers.inet as inet
-
from tools.pkt_gen.xena.json import json_utilities
_LOGGER = logging.getLogger(__name__)
@@ -73,30 +72,87 @@ class XenaJSON(object):
3: ('Dest IP Addr', 'Src IP Addr'),
4: ('Dest Port', 'Src Port')
}
- segments = [
- {
- "Offset": 0,
- "Mask": "//8=", # mask of 255/255
- "Action": "INC",
- "StartValue": 0,
- "StopValue": stop_value,
- "StepValue": 1,
- "RepeatCount": 1,
- "SegmentId": seg_uuid,
- "FieldName": field_name[int(layer)][0]
- },
- {
- "Offset": 0,
- "Mask": "//8=", # mask of 255/255
- "Action": "INC",
- "StartValue": 0,
- "StopValue": stop_value,
- "StepValue": 1,
- "RepeatCount": 1,
- "SegmentId": seg_uuid,
- "FieldName": field_name[int(layer)][1]
- }
- ]
+
+ if stop_value > 4294836225:
+ _LOGGER.debug('Flow mods exceeds highest value, changing to 4294836225')
+ stop_value = 4294836225
+
+ if stop_value <= 65535 or layer == 4:
+ segments = [
+ {
+ "Offset": 0 if layer == 4 else 2,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": 1,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][0]
+ },
+ {
+ "Offset": 0 if layer == 4 else 2,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": 1,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][1]
+ }
+ ]
+ else:
+ stop_value = int(math.sqrt(stop_value))
+ _LOGGER.debug('Flow count modified to %s', stop_value * stop_value)
+ segments = [
+ {
+ "Offset": 0 if layer == 3 else 1,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": stop_value,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][0]
+ },
+ {
+ "Offset": 2 if layer == 3 else 3,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": 1,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][0]
+ },
+ {
+ "Offset": 0 if layer == 3 else 1,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": stop_value,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][1]
+ },
+ {
+ "Offset": 2 if layer == 3 else 3,
+ "Mask": "//8=", # mask of 255/255
+ "Action": "INC",
+ "StartValue": 0,
+ "StopValue": stop_value - 1,
+ "StepValue": 1,
+ "RepeatCount": 1,
+ "SegmentId": seg_uuid,
+ "FieldName": field_name[int(layer)][1]
+ }
+ ]
+
+
self.json_data['StreamProfileHandler']['EntityList'][entity][
'StreamConfig']['HwModifiers'] = (segments)
@@ -279,6 +335,10 @@ class XenaJSON(object):
:param kwargs: Extra params per scapy usage.
:return: None
"""
+ # import can't be performed at module level, because it conflicts with import
+ # of customized scapy version by T-Rex
+ import scapy.layers.inet as inet
+
self.packet_data['layer2'] = [
inet.Ether(dst=dst_mac, src=src_mac, **kwargs),
inet.Ether(dst=src_mac, src=dst_mac, **kwargs)]
@@ -293,6 +353,10 @@ class XenaJSON(object):
:param kwargs: Extra params per scapy usage
:return: None
"""
+ # import can't be performed at module level, because it conflicts with import
+ # of customized scapy version by T-Rex
+ import scapy.layers.inet as inet
+
self.packet_data['layer3'] = [
inet.IP(src=src_ip, dst=dst_ip, proto=protocol.lower(), **kwargs),
inet.IP(src=dst_ip, dst=src_ip, proto=protocol.lower(), **kwargs)]
@@ -305,6 +369,10 @@ class XenaJSON(object):
:param kwargs: Extra params per scapy usage
:return: None
"""
+ # import can't be performed at module level, because it conflicts with import
+ # of customized scapy version by T-Rex
+ import scapy.layers.inet as inet
+
self.packet_data['layer4'] = [
inet.UDP(sport=source_port, dport=destination_port, **kwargs),
inet.UDP(sport=source_port, dport=destination_port, **kwargs)]
@@ -316,6 +384,10 @@ class XenaJSON(object):
:param kwargs: Extra params per scapy usage
:return: None
"""
+ # import can't be performed at module level, because it conflicts with import
+ # of customized scapy version by T-Rex
+ import scapy.layers.inet as inet
+
self.packet_data['vlan'] = [
inet.Dot1Q(vlan=vlan_id, **kwargs),
inet.Dot1Q(vlan=vlan_id, **kwargs)]
diff --git a/tools/pkt_gen/xena/xena.py b/tools/pkt_gen/xena/xena.py
index 19b44f0b..3adc8294 100755
--- a/tools/pkt_gen/xena/xena.py
+++ b/tools/pkt_gen/xena/xena.py
@@ -32,8 +32,6 @@ import xml.etree.ElementTree as ET
from collections import OrderedDict
from time import sleep
-import scapy.layers.inet as inet
-
from conf import merge_spec
from conf import settings
from core.results.results_constants import ResultsConstants
@@ -41,6 +39,7 @@ from tools.pkt_gen.trafficgen.trafficgen import ITrafficGenerator
from tools.pkt_gen.xena.XenaDriver import (
aggregate_stats,
line_percentage,
+ ModSet,
XenaSocketDriver,
XenaManager,
)
@@ -149,6 +148,10 @@ class Xena(ITrafficGenerator):
:param reverse: Swap source and destination info when building header
:return: packet header in hex
"""
+ # import can't be performed at module level, because it conflicts with import
+ # of customized scapy version by T-Rex
+ import scapy.layers.inet as inet
+
srcmac = self._params['traffic']['l2'][
'srcmac'] if not reverse else self._params['traffic']['l2'][
'dstmac']
@@ -274,10 +277,6 @@ class Xena(ITrafficGenerator):
enable the pairs topology
:return: None
"""
- # set duplex mode, this code is valid, pylint complaining with a
- # warning that many have complained about online.
- # pylint: disable=redefined-variable-type
-
try:
if self._params['traffic']['bidir'] == "True":
j_file = XenaJSONMesh()
@@ -285,6 +284,9 @@ class Xena(ITrafficGenerator):
j_file = XenaJSONBlocks()
elif bonding_test:
j_file = XenaJSONPairs()
+ else: # just default to mesh config
+ self._logger.error('Invalid traffic type defaulting to Mesh config')
+ j_file = XenaJSONMesh()
j_file.set_chassis_info(
settings.getValue('TRAFFICGEN_XENA_IP'),
@@ -348,7 +350,7 @@ class Xena(ITrafficGenerator):
id=self._params['traffic']['vlan']['cfi'],
prio=self._params['traffic']['vlan']['priority'])
j_file.add_header_segments(
- flows=self._params['traffic']['multistream'],
+ flows=self._params['traffic']['multistream'] - 1,
multistream_layer=self._params['traffic']['stream_type'])
j_file.write_config(os.path.join(
@@ -456,9 +458,17 @@ class Xena(ITrafficGenerator):
port.micro_tpld_enable()
if self._params['traffic']['multistream']:
+ if self._params['traffic']['stream_type'] == 'L2':
+ modobj = ModSet(mod_src_mac=True, mod_dst_mac=True)
+ elif self._params['traffic']['stream_type'] == 'L3':
+ modobj = ModSet(mod_src_ip=True, mod_dst_ip=True)
+ elif self._params['traffic']['stream_type'] == 'L4':
+ modobj = ModSet(mod_src_port=True, mod_dst_port=True)
+ else:
+ self._logger.error('Invalid segment for multistream. Using L2..')
+ modobj = ModSet(mod_src_mac=True, mod_dst_mac=True)
stream.enable_multistream(
- flows=self._params['traffic']['multistream'],
- layer=self._params['traffic']['stream_type'])
+ flows=self._params['traffic']['multistream'], mod_class=modobj)
s1_p0 = self.xmanager.ports[0].add_stream()
setup_stream(s1_p0, self.xmanager.ports[0], 0)
@@ -568,7 +578,7 @@ class Xena(ITrafficGenerator):
self._xsocket.disconnect()
self._xsocket = None
- def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
+ def send_burst_traffic(self, traffic=None, duration=20):
"""Send a burst of traffic.
See ITrafficGenerator for description
@@ -579,7 +589,7 @@ class Xena(ITrafficGenerator):
if traffic:
self._params['traffic'] = merge_spec(self._params['traffic'],
traffic)
- self._start_traffic_api(numpkts)
+ self._start_traffic_api(traffic['burst_size'])
return self._stop_api_traffic()
def send_cont_traffic(self, traffic=None, duration=20):