aboutsummaryrefslogtreecommitdiffstats
path: root/tools/pkt_gen/trex/trex.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/pkt_gen/trex/trex.py')
-rw-r--r--tools/pkt_gen/trex/trex.py223
1 files changed, 166 insertions, 57 deletions
diff --git a/tools/pkt_gen/trex/trex.py b/tools/pkt_gen/trex/trex.py
index e0ce4c48..94b793d6 100644
--- a/tools/pkt_gen/trex/trex.py
+++ b/tools/pkt_gen/trex/trex.py
@@ -15,12 +15,14 @@
"""
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
@@ -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."""
@@ -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,22 +306,48 @@ 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)
@@ -293,13 +377,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])
@@ -382,20 +466,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'
@@ -568,9 +661,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):