3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """This script uses T-REX stateless API to drive t-rex instance.
19 - T-REX: https://github.com/cisco-system-traffic-generator/trex-core
20 - compiled and running T-REX process (eg. ./t-rex-64 -i -c 4)
21 - trex_stl_lib.api library
22 - Script must be executed on a node with T-REX instance
23 - 2 interfaces must be configured in configuretion file /etc/trex_cfg.yaml
25 ##################### Example of /etc/trex_cfg.yaml ##########################
26 - port_limit : 2 # numbers of ports to use
28 interfaces : ["84:00.0","84:00.1"] # PCI address of interfaces
29 port_info : # set eth mac addr
30 - dest_mac : [0x90,0xe2,0xba,0x1f,0x97,0xd5] # port 0
31 src_mac : [0x90,0xe2,0xba,0x1f,0x97,0xd4]
32 - dest_mac : [0x90,0xe2,0xba,0x1f,0x97,0xd4] # port 1
33 src_mac : [0x90,0xe2,0xba,0x1f,0x97,0xd5]
34 ##############################################################################
37 1. Configure traffic on running T-REX instance
38 2. Clear statistics on all ports
39 3. Ctart traffic with specified duration
40 4. Print statistics to stdout
51 sys.path.insert(0, "/opt/trex-core-2.03/scripts/automation/"+\
52 "trex_control_plane/stl/")
53 from trex_stl_lib.api import *
56 def generate_payload(length):
59 :param length: Length of payload.
61 :return: Payload filled with chars.
66 alphabet_size = len(string.letters)
67 for i in range(length):
68 word += string.letters[(i % alphabet_size)]
73 def get_start_end_ipv6(start_ip, end_ip):
74 """Get start and end host from IPv6 as integer.
76 :param start_ip: Start IPv6.
77 :param end_ip: End IPv6.
78 :type start_ip: string
80 :return: Start host, end host.
85 ip1 = socket.inet_pton(socket.AF_INET6, start_ip)
86 ip2 = socket.inet_pton(socket.AF_INET6, end_ip)
88 hi1, lo1 = struct.unpack('!QQ', ip1)
89 hi2, lo2 = struct.unpack('!QQ', ip2)
91 if ((hi1 << 64) | lo1) > ((hi2 << 64) | lo2):
92 print "IPv6: start_ip is greater then end_ip"
95 max_p1 = abs(int(lo1) - int(lo2)) + 1
97 except AddressValueError as ex_error:
101 return base_p1, max_p1
104 def create_packets(traffic_options, frame_size=64):
105 """Create two IP packets to be used in stream.
107 :param traffic_options: Parameters for packets.
108 :param frame_size: Size of L2 frame.
109 :type traffic_options: list
110 :type frame_size: int
111 :return: Packet instances.
112 :rtype: Tuple of STLPktBuilder
116 print_error("Packet min. size is 64B")
119 fsize_no_fcs = frame_size - 4 # no FCS
121 p1_src_start_ip = traffic_options['p1_src_start_ip']
122 p1_src_end_ip = traffic_options['p1_src_end_ip']
123 p1_dst_start_ip = traffic_options['p1_dst_start_ip']
124 p2_src_start_ip = traffic_options['p2_src_start_ip']
125 p2_src_end_ip = traffic_options['p2_src_end_ip']
126 p2_dst_start_ip = traffic_options['p2_dst_start_ip']
128 base_pkt_a = Ether()/IP(src=p1_src_start_ip, dst=p1_dst_start_ip, proto=61)
129 base_pkt_b = Ether()/IP(src=p2_src_start_ip, dst=p2_dst_start_ip, proto=61)
131 # The following code applies raw instructions to packet (IP src increment).
132 # It splits the generated traffic by "ip_src" variable to cores and fix
133 # IPv4 header checksum.
134 vm1 = STLScVmRaw([STLVmFlowVar(name="src",
135 min_value=p1_src_start_ip,
136 max_value=p1_src_end_ip,
138 STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"),
139 STLVmFixIpv4(offset="IP"),
140 ], split_by_field="src")
141 # The following code applies raw instructions to packet (IP src increment).
142 # It splits the generated traffic by "ip_src" variable to cores and fix
143 # IPv4 header checksum.
144 vm2 = STLScVmRaw([STLVmFlowVar(name="src",
145 min_value=p2_src_start_ip,
146 max_value=p2_src_end_ip,
148 STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"),
149 STLVmFixIpv4(offset="IP"),
150 ], split_by_field="src")
152 pkt_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
153 max(0, fsize_no_fcs-len(base_pkt_a))), vm=vm1)
154 pkt_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
155 max(0, fsize_no_fcs-len(base_pkt_b))), vm=vm2)
156 lat_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
157 max(0, fsize_no_fcs-len(base_pkt_a))))
158 lat_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
159 max(0, fsize_no_fcs-len(base_pkt_b))))
161 return(pkt_a, pkt_b, lat_a, lat_b)
164 def create_packets_v6(traffic_options, frame_size=78):
165 """Create two IPv6 packets to be used in stream.
167 :param traffic_options: Parameters for packets.
168 :param frame_size: Size of L2 frame.
169 :type traffic_options: List
170 :type frame_size: int
171 :return: Packet instances.
172 :rtype: Tuple of STLPktBuilder
176 print "Packet min. size is 78B"
179 fsize_no_fcs = frame_size - 4 # no FCS
181 p1_src_start_ip = traffic_options['p1_src_start_ip']
182 p1_src_end_ip = traffic_options['p1_src_end_ip']
183 p1_dst_start_ip = traffic_options['p1_dst_start_ip']
184 p2_src_start_ip = traffic_options['p2_src_start_ip']
185 p2_src_end_ip = traffic_options['p2_src_end_ip']
186 p2_dst_start_ip = traffic_options['p2_dst_start_ip']
188 base_p1, max_p1 = get_start_end_ipv6(p1_src_start_ip, p1_src_end_ip)
189 base_p2, max_p2 = get_start_end_ipv6(p2_src_start_ip, p2_src_end_ip)
191 base_pkt_a = Ether()/IPv6(src=p1_src_start_ip, dst=p1_dst_start_ip)
192 base_pkt_b = Ether()/IPv6(src=p2_src_start_ip, dst=p2_dst_start_ip)
194 # The following code applies raw instructions to packet (IP src increment).
195 # It splits the generated traffic by "ip_src" variable to cores
196 vm1 = STLScVmRaw([STLVmFlowVar(name="ipv6_src",
198 max_value=max_p1+base_p1,
200 STLVmWrFlowVar(fv_name="ipv6_src", pkt_offset="IPv6.src",
203 , split_by_field="ipv6_src")
205 # The following code applies raw instructions to packet (IP src increment).
206 # It splits the generated traffic by "ip_src" variable to cores
207 vm2 = STLScVmRaw([STLVmFlowVar(name="ipv6_src",
209 max_value=max_p2+base_p2,
211 STLVmWrFlowVar(fv_name="ipv6_src", pkt_offset="IPv6.src",
214 , split_by_field="ipv6_src")
216 pkt_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
217 max(0, fsize_no_fcs-len(base_pkt_a))), vm=vm1)
218 pkt_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
219 max(0, fsize_no_fcs-len(base_pkt_b))), vm=vm2)
220 lat_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
221 max(0, fsize_no_fcs-len(base_pkt_a))))
222 lat_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
223 max(0, fsize_no_fcs-len(base_pkt_b))))
225 return(pkt_a, pkt_b, lat_a, lat_b)
228 def simple_burst(pkt_a, pkt_b, pkt_lat_a, pkt_lat_b, duration, rate,
229 warmup_time, async_start, latency):
230 """Run the traffic with specific parameters.
232 :param pkt_a: Base packet for stream 1.
233 :param pkt_b: Base packet for stream 2.
234 :param pkt_lat_a: Base packet for latency stream 1.
235 :param pkt_lat_b: Base packet for latency stream 2.
236 :param duration: Duration of traffic run in seconds (-1=infinite).
237 :param rate: Rate of traffic run [percentage, pps, bps].
238 :param warmup_time: Warm up duration.
239 :param async_start: Start the traffic and exit.
240 :param latency: With latency stats.
241 :type pkt_a: STLPktBuilder
242 :type pkt_b: STLPktBuilder
243 :type pkt_lat_a: STLPktBuilder
244 :type pkt_lat_b: STLPktBuilder
247 :type warmup_time: int
248 :type async_start: bool
264 # turn this off if too many logs
265 #client.set_verbose("high")
270 # prepare our ports (my machine has 0 <--> 1 with static route)
271 client.reset(ports=[0, 1])
273 # create two traffic streams without latency stats
274 stream1 = STLStream(packet=pkt_a,
275 mode=STLTXCont(pps=1000))
276 # second traffic stream with a phase of 10ns (inter stream gap)
277 stream2 = STLStream(packet=pkt_b,
279 mode=STLTXCont(pps=1000))
280 client.add_streams(stream1, ports=[0])
281 client.add_streams(stream2, ports=[1])
284 # create two traffic streams with latency stats
285 lat_stream1 = STLStream(packet=pkt_lat_a,
286 flow_stats=STLFlowLatencyStats(pg_id=0),
287 mode=STLTXCont(pps=1000))
288 # second traffic stream with a phase of 10ns (inter stream gap)
289 lat_stream2 = STLStream(packet=pkt_lat_b,
291 flow_stats=STLFlowLatencyStats(pg_id=1),
292 mode=STLTXCont(pps=1000))
293 client.add_streams(lat_stream1, ports=[0])
294 client.add_streams(lat_stream2, ports=[1])
298 # clear the stats before injecting
301 # choose rate and start traffic
302 client.start(ports=[0, 1], mult=rate, duration=warmup_time)
305 client.wait_on_traffic(ports=[0, 1], timeout=(warmup_time+30))
307 if client.get_warnings():
308 for warning in client.get_warnings():
311 # read the stats after the test
312 stats = client.get_stats()
314 print "#####warmup statistics#####"
315 print json.dumps(stats, indent=4,
316 separators=(',', ': '), sort_keys=True)
317 lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
318 lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
320 print "\npackets lost from 0 --> 1: {0} pkts".format(lost_a)
321 print "packets lost from 1 --> 0: {0} pkts".format(lost_b)
323 # clear the stats before injecting
328 # choose rate and start traffic
329 client.start(ports=[0, 1], mult=rate, duration=duration)
333 client.wait_on_traffic(ports=[0, 1], timeout=(duration+30))
335 if client.get_warnings():
336 for warning in client.get_warnings():
339 # read the stats after the test
340 stats = client.get_stats()
342 print "#####statistics#####"
343 print json.dumps(stats, indent=4,
344 separators=(',', ': '), sort_keys=True)
345 lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
346 lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
350 str(stats["latency"][0]["latency"]["total_min"]),\
351 str(stats["latency"][0]["latency"]["average"]),\
352 str(stats["latency"][0]["latency"]["total_max"])))
354 str(stats["latency"][1]["latency"]["total_min"]),\
355 str(stats["latency"][1]["latency"]["average"]),\
356 str(stats["latency"][1]["latency"]["total_max"])))
358 total_sent = stats[0]["opackets"] + stats[1]["opackets"]
359 total_rcvd = stats[0]["ipackets"] + stats[1]["ipackets"]
361 print "\npackets lost from 0 --> 1: {0} pkts".format(lost_a)
362 print "packets lost from 1 --> 0: {0} pkts".format(lost_b)
364 except STLError as ex_error:
365 print_error(str(ex_error))
370 client.disconnect(stop_traffic=False, release_ports=True)
373 print "rate={0}, totalReceived={1}, totalSent={2}, "\
374 "frameLoss={3}, latencyStream0(usec)={4}, "\
375 "latencyStream1(usec)={5}".format(rate, total_rcvd,\
376 total_sent, lost_a+lost_b, lat_a, lat_b)
379 def print_error(msg):
380 """Print error message on stderr.
382 :param msg: Error message to print.
387 sys.stderr.write(msg+'\n')
391 """Parse arguments from cmd line.
393 :return: Parsed arguments.
394 :rtype ArgumentParser
397 parser = argparse.ArgumentParser()
398 parser.add_argument("-d", "--duration", required=True, type=int,
399 help="Duration of traffic run")
400 parser.add_argument("-s", "--frame_size", required=True, type=int,
401 help="Size of a Frame without padding and IPG")
402 parser.add_argument("-r", "--rate", required=True,
403 help="Traffic rate with included units (%, pps)")
404 parser.add_argument("-6", "--use_IPv6", action="store_true",
406 help="Use IPv6 traffic profile instead of IPv4")
407 parser.add_argument("--async", action="store_true",
409 help="Non-blocking call of the script")
410 parser.add_argument("--latency", action="store_true",
412 help="Add latency stream")
413 parser.add_argument("-w", "--warmup_time", type=int,
415 help="Traffic warmup time in seconds, 0 = disable")
416 # parser.add_argument("--p1_src_mac",
417 # help="Port 1 source MAC address")
418 # parser.add_argument("--p1_dst_mac",
419 # help="Port 1 destination MAC address")
420 parser.add_argument("--p1_src_start_ip", required=True,
421 help="Port 1 source start IP address")
422 parser.add_argument("--p1_src_end_ip", required=True,
423 help="Port 1 source end IP address")
424 parser.add_argument("--p1_dst_start_ip", required=True,
425 help="Port 1 destination start IP address")
426 # parser.add_argument("--p1_dst_end_ip",
427 # help="Port 1 destination end IP address")
428 # parser.add_argument("--p2_src_mac",
429 # help="Port 2 source MAC address")
430 # parser.add_argument("--p2_dst_mac",
431 # help="Port 2 destination MAC address")
432 parser.add_argument("--p2_src_start_ip", required=True,
433 help="Port 2 source start IP address")
434 parser.add_argument("--p2_src_end_ip", required=True,
435 help="Port 2 source end IP address")
436 parser.add_argument("--p2_dst_start_ip", required=True,
437 help="Port 2 destination start IP address")
438 # parser.add_argument("--p2_dst_end_ip",
439 # help="Port 2 destination end IP address")
441 return parser.parse_args()
449 _duration = args.duration
450 _frame_size = args.frame_size
452 _use_ipv6 = args.use_IPv6
453 _async_call = args.async
454 _latency = args.latency
455 _warmup_time = args.warmup_time
457 _traffic_options = {}
458 for attr in [a for a in dir(args) if a.startswith('p')]:
459 if getattr(args, attr) is not None:
460 _traffic_options[attr] = getattr(args, attr)
463 # WARNING: Trex limitation to IPv4 only. IPv6 is not yet supported.
464 print_error('IPv6 latency is not supported yet. Running without lat.')
467 pkt_a, pkt_b, lat_a, lat_b = create_packets_v6(_traffic_options,
468 frame_size=_frame_size)
470 pkt_a, pkt_b, lat_a, lat_b = create_packets(_traffic_options,
471 frame_size=_frame_size)
473 simple_burst(pkt_a, pkt_b, lat_a, lat_b, _duration, _rate, _warmup_time,
474 _async_call, _latency)
476 if __name__ == "__main__":