97ccc9730493e384fc57c139904b20e3f33c0e83
[csit.git] / resources / tools / t-rex / t-rex-stateless.py
1 #!/usr/bin/python
2
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 """This script uses T-REX stateless API to drive t-rex instance.
17
18 Requirements:
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
24
25 ##################### Example of /etc/trex_cfg.yaml ##########################
26 - port_limit      : 2 # numbers of ports to use
27   version         : 2
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 ##############################################################################
35
36 Functionality:
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
41
42 """
43
44 import json
45 import socket
46 import string
47 import struct
48 import sys, getopt
49
50 sys.path.insert(0, "/opt/trex-core-2.00/scripts/automation/"+\
51                    "trex_control_plane/stl/")
52 from trex_stl_lib.api import *
53
54
55 def generate_payload(length):
56     """Generate payload.
57
58     :param length: Length of payload.
59     :type length: int
60     :return: Payload filled with chars.
61     :rtype string
62     """
63
64     word = ''
65     alphabet_size = len(string.letters)
66     for i in range(length):
67         word += string.letters[(i % alphabet_size)]
68
69     return word
70
71
72 def get_start_end_ipv6(start_ip, end_ip):
73     """Get start and end host from IPv6 as integer.
74
75     :param start_ip: Start IPv6.
76     :param end_ip: End IPv6.
77     :type start_ip: string
78     :type end_ip: string
79     :return: Start host, end host.
80     :rtype int
81     """
82
83     try:
84         ip1 = socket.inet_pton(socket.AF_INET6, start_ip)
85         ip2 = socket.inet_pton(socket.AF_INET6, end_ip)
86
87         hi1, lo1 = struct.unpack('!QQ', ip1)
88         hi2, lo2 = struct.unpack('!QQ', ip2)
89
90         if ((hi1 << 64) | lo1) > ((hi2 << 64) | lo2):
91             print "IPv6: start_ip is greater then end_ip"
92             sys.exit(2)
93
94         max_p1 = abs(int(lo1) - int(lo2)) + 1
95         base_p1 = lo1
96     except AddressValueError as ex_error:
97         print ex_error
98         sys.exit(2)
99
100     return base_p1, max_p1
101
102
103 def create_packets(traffic_options, frame_size=64):
104     """Create two IP packets to be used in stream.
105
106     :param traffic_options: Parameters for packets.
107     :param frame_size: Size of L2 frame.
108     :type traffic_options: list
109     :type frame_size: int
110     :return: Packet instances.
111     :rtype STLPktBuilder
112     """
113
114     if frame_size < 64:
115         print_error("Packet min. size is 64B")
116         sys.exit(1)
117
118     fsize_no_fcs = frame_size - 4 # no FCS
119
120     p1_src_start_ip = traffic_options['p1_src_start_ip']
121     p1_src_end_ip = traffic_options['p1_src_end_ip']
122     p1_dst_start_ip = traffic_options['p1_dst_start_ip']
123     p2_src_start_ip = traffic_options['p2_src_start_ip']
124     p2_src_end_ip = traffic_options['p2_src_end_ip']
125     p2_dst_start_ip = traffic_options['p2_dst_start_ip']
126
127     base_pkt_a = Ether()/IP(src=p1_src_start_ip, dst=p1_dst_start_ip, proto=61)
128     base_pkt_b = Ether()/IP(src=p2_src_start_ip, dst=p2_dst_start_ip, proto=61)
129
130     # The following code applies raw instructions to packet (IP src increment).
131     # It splits the generated traffic by "ip_src" variable to cores and fix
132     # IPv4 header checksum.
133     vm1 = STLScVmRaw([STLVmFlowVar(name="src",
134                                    min_value=p1_src_start_ip,
135                                    max_value=p1_src_end_ip,
136                                    size=4, op="inc"),
137                       STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"),
138                       STLVmFixIpv4(offset="IP"),
139                      ], split_by_field="src")
140     # The following code applies raw instructions to packet (IP src increment).
141     # It splits the generated traffic by "ip_src" variable to cores and fix
142     # IPv4 header checksum.
143     vm2 = STLScVmRaw([STLVmFlowVar(name="src",
144                                    min_value=p2_src_start_ip,
145                                    max_value=p2_src_end_ip,
146                                    size=4, op="inc"),
147                       STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"),
148                       STLVmFixIpv4(offset="IP"),
149                      ], split_by_field="src")
150
151     pkt_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
152         fsize_no_fcs-len(base_pkt_a)), vm=vm1)
153     pkt_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
154         fsize_no_fcs-len(base_pkt_b)), vm=vm2)
155
156     return(pkt_a, pkt_b)
157
158
159 def create_packets_v6(traffic_options, frame_size=78):
160     """Create two IPv6 packets to be used in stream.
161
162     :param traffic_options: Parameters for packets.
163     :param frame_size: Size of L2 frame.
164     :type traffic_options: List
165     :type frame_size: int
166     :return: Packet instances.
167     :rtype STLPktBuilder
168     """
169
170     if frame_size < 78:
171         print "Packet min. size is 78B"
172         sys.exit(2)
173
174     fsize_no_fcs = frame_size - 4 # no FCS
175
176     p1_src_start_ip = traffic_options['p1_src_start_ip']
177     p1_src_end_ip = traffic_options['p1_src_end_ip']
178     p1_dst_start_ip = traffic_options['p1_dst_start_ip']
179     p2_src_start_ip = traffic_options['p2_src_start_ip']
180     p2_src_end_ip = traffic_options['p2_src_end_ip']
181     p2_dst_start_ip = traffic_options['p2_dst_start_ip']
182
183     base_p1, max_p1 = get_start_end_ipv6(p1_src_start_ip, p1_src_end_ip)
184     base_p2, max_p2 = get_start_end_ipv6(p2_src_start_ip, p2_src_end_ip)
185
186     base_pkt_a = Ether()/IPv6(src=p1_src_start_ip, dst=p1_dst_start_ip)
187     base_pkt_b = Ether()/IPv6(src=p2_src_start_ip, dst=p2_dst_start_ip)
188
189     # The following code applies raw instructions to packet (IP src increment).
190     # It splits the generated traffic by "ip_src" variable to cores
191     vm1 = STLScVmRaw([STLVmFlowVar(name="ipv6_src",
192                                    min_value=base_p1,
193                                    max_value=max_p1+base_p1,
194                                    size=8, op="inc"),
195                       STLVmWrFlowVar(fv_name="ipv6_src", pkt_offset="IPv6.src",
196                                      offset_fixup=8)
197                      ]
198                      , split_by_field="ipv6_src")
199
200     # The following code applies raw instructions to packet (IP src increment).
201     # It splits the generated traffic by "ip_src" variable to cores
202     vm2 = STLScVmRaw([STLVmFlowVar(name="ipv6_src",
203                                    min_value=base_p2,
204                                    max_value=max_p2+base_p2,
205                                    size=8, op="inc"),
206                       STLVmWrFlowVar(fv_name="ipv6_src", pkt_offset="IPv6.src",
207                                      offset_fixup=8)
208                      ]
209                      , split_by_field="ipv6_src")
210
211     pkt_a = STLPktBuilder(pkt=base_pkt_a/generate_payload(
212         max(0, fsize_no_fcs-len(base_pkt_a))), vm=vm1)
213     pkt_b = STLPktBuilder(pkt=base_pkt_b/generate_payload(
214         max(0, fsize_no_fcs-len(base_pkt_b))), vm=vm2)
215
216     return(pkt_a, pkt_b)
217
218
219 def simple_burst(pkt_a, pkt_b, duration=10, rate="1mpps", warmup_time=5):
220     """Run the traffic with specific parameters.
221
222     :param pkt_a: Base packet for stream 1.
223     :param pkt_b: Base packet for stream 2.
224     :param duration: Duration of traffic run in seconds.
225     :param rate: Rate of traffic run [percentage, pps, bps].
226     :param warmup_time: Warm up duration.
227     :type pkt_a: STLPktBuilder
228     :type pkt_b: STLPktBuilder
229     :type duration: int
230     :type rate: string
231     :type warmup_time: int
232     :return: nothing
233     """
234
235     # create client
236     client = STLClient()
237
238     total_rcvd = 0
239     total_sent = 0
240     lost_a = 0
241     lost_b = 0
242
243     try:
244         # turn this off if too many logs
245         #client.set_verbose("high")
246
247         # create two streams
248         stream1 = STLStream(packet=pkt_a,
249                             mode=STLTXCont(pps=100))
250
251         # second stream with a phase of 10ns (inter stream gap)
252         stream2 = STLStream(packet=pkt_b,
253                             isg=10.0,
254                             mode=STLTXCont(pps=100))
255
256         # connect to server
257         client.connect()
258
259         # prepare our ports (my machine has 0 <--> 1 with static route)
260         client.reset(ports=[0, 1])
261
262         # add both streams to ports
263         client.add_streams(stream1, ports=[0])
264         client.add_streams(stream2, ports=[1])
265
266         #warmup phase
267         if warmup_time is not None:
268             client.clear_stats()
269             client.start(ports=[0, 1], mult=rate, duration=warmup_time)
270             client.wait_on_traffic(ports=[0, 1], timeout=(warmup_time+30))
271             stats = client.get_stats()
272             print stats
273             print "#####warmup statistics#####"
274             print json.dumps(stats, indent=4,
275                              separators=(',', ': '), sort_keys=True)
276             lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
277             lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
278
279             print "\npackets lost from 0 --> 1:   {0} pkts".format(lost_a)
280             print "packets lost from 1 --> 0:   {0} pkts".format(lost_b)
281
282
283         # clear the stats before injecting
284         client.clear_stats()
285         total_rcvd = 0
286         total_sent = 0
287         lost_a = 0
288         lost_b = 0
289
290         # choose rate and start traffic
291         client.start(ports=[0, 1], mult=rate, duration=duration)
292
293         # block until done
294         client.wait_on_traffic(ports=[0, 1], timeout=(duration+30))
295
296         # read the stats after the test
297         stats = client.get_stats()
298
299         print "#####statistics#####"
300         print json.dumps(stats, indent=4,
301                          separators=(',', ': '), sort_keys=True)
302
303         lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
304         lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
305
306         total_sent = stats[0]["opackets"] + stats[1]["opackets"]
307         total_rcvd = stats[0]["ipackets"] + stats[1]["ipackets"]
308
309         print "\npackets lost from 0 --> 1:   {0} pkts".format(lost_a)
310         print "packets lost from 1 --> 0:   {0} pkts".format(lost_b)
311
312     except STLError as ex_error:
313         print_error(str(ex_error))
314         sys.exit(1)
315
316     finally:
317         client.disconnect()
318         print "rate={0}, totalReceived={1}, totalSent={2}, frameLoss={3}"\
319               .format(rate, total_rcvd, total_sent, lost_a+lost_b)
320
321
322 def print_help():
323     """Print help on stdout."""
324
325     print "args: [-h] -d <duration> -s <size of frame in bytes>"+\
326     " [-r] <traffic rate with unit: %, mpps> "+\
327     " [-6] Use of ipv6 "+\
328     "--p1_src_mac <port1_src_mac> "+\
329     "--p1_dst_mac <port1_dst_mac> "+\
330     "--p1_src_start_ip <port1_src_start_ip> "+\
331     "--p1_src_end_ip <port1_src_end_ip> "+\
332     "--p1_dst_start_ip <port1_dst_start_ip> "+\
333     "--p1_dst_end_ip <port1_dst_end_ip> "+\
334     "--p2_src_mac <port2_src_mac> "+\
335     "--p2_dst_mac <port2_dst_mac> "+\
336     "--p2_src_start_ip <port2_src_start_ip> "+\
337     "--p2_src_end_ip <port2_src_end_ip> "+\
338     "--p2_dst_start_ip <port2_dst_start_ip> "+\
339     "--p2_dst_end_ip <port2_dst_end_ip>"
340
341
342 def print_error(msg):
343     """Print error message on stderr.
344
345     :param msg: Error message to print.
346     :type msg: string
347     :return: nothing
348     """
349
350     sys.stderr.write(msg+'\n')
351
352
353 def main(argv):
354     """Main function."""
355
356     _duration = 10
357     _frame_size = 64
358     _rate = '1mpps'
359     _traffic_options = {}
360     _use_ipv6 = False
361
362     try:
363         opts, _ = getopt.getopt(argv, "hd:s:r:6o:",
364                                 ["help",
365                                  "p1_src_mac=",
366                                  "p1_dst_mac=",
367                                  "p1_src_start_ip=",
368                                  "p1_src_end_ip=",
369                                  "p1_dst_start_ip=",
370                                  "p1_dst_end_ip=",
371                                  "p2_src_mac=",
372                                  "p2_dst_mac=",
373                                  "p2_src_start_ip=",
374                                  "p2_src_end_ip=",
375                                  "p2_dst_start_ip=",
376                                  "p2_dst_end_ip="])
377     except getopt.GetoptError:
378         print_help()
379         sys.exit(1)
380     for opt, arg in opts:
381         if opt in ('-h', "--help"):
382             print_help()
383             sys.exit()
384         elif opt == '-d':
385             _duration = int(arg)
386         elif opt == '-s':
387             _frame_size = int(arg)
388         elif opt == '-r':
389             _rate = arg
390         elif opt == '-6':
391             _use_ipv6 = True
392         elif opt.startswith("--p"):
393             _traffic_options[opt[2:]] = arg
394
395     print _traffic_options
396     if len(_traffic_options) != 6:
397         print_error("Supported only: src_start_ip, src_end_ip, dst_start_ip")
398         print_help()
399         sys.exit(1)
400
401     if _use_ipv6:
402         pkt_a, pkt_b = create_packets_v6(_traffic_options,
403                                          frame_size=_frame_size)
404     else:
405         pkt_a, pkt_b = create_packets(_traffic_options,
406                                       frame_size=_frame_size)
407
408     simple_burst(pkt_a, pkt_b, duration=_duration, rate=_rate)
409
410 if __name__ == "__main__":
411     main(sys.argv[1:])