CSIT-1440: Re-organize graphs in report
[csit.git] / resources / tools / trex / trex_stateless_profile.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2017 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 module gets a traffic profile together with other parameters, reads
17 the profile and sends the traffic. At the end, it measures the packet loss and
18 latency.
19 """
20
21 import sys
22 import argparse
23 import json
24
25 sys.path.insert(0, "/opt/trex-core-2.35/scripts/automation/"
26                    "trex_control_plane/stl/")
27
28 from trex_stl_lib.api import *
29
30
31 def fmt_latency(lat_min, lat_avg, lat_max):
32     """Return formatted, rounded latency.
33
34     :param lat_min: Min latency
35     :param lat_avg: Average latency
36     :param lat_max: Max latency
37     :type lat_min: string
38     :type lat_avg: string
39     :type lat_max: string
40     :return: Formatted and rounded output "min/avg/max"
41     :rtype: string
42     """
43
44     try:
45         t_min = int(round(float(lat_min)))
46     except ValueError:
47         t_min = int(-1)
48     try:
49         t_avg = int(round(float(lat_avg)))
50     except ValueError:
51         t_avg = int(-1)
52     try:
53         t_max = int(round(float(lat_max)))
54     except ValueError:
55         t_max = int(-1)
56
57     return "/".join(str(tmp) for tmp in (t_min, t_avg, t_max))
58
59
60 def simple_burst(profile_file, duration, framesize, rate, warmup_time, port_0,
61                  port_1, latency, async_start=False, unidirection=False):
62     """Send traffic and measure packet loss and latency.
63
64     Procedure:
65      - reads the given traffic profile with streams,
66      - connects to the T-rex client,
67      - resets the ports,
68      - removes all existing streams,
69      - adds streams from the traffic profile to the ports,
70      - if the warm-up time is more than 0, sends the warm-up traffic, reads the
71        statistics,
72      - clears the statistics from the client,
73      - starts the traffic,
74      - waits for the defined time (or runs forever if async mode is defined),
75      - stops the traffic,
76      - reads and displays the statistics and
77      - disconnects from the client.
78
79     :param profile_file: A python module with T-rex traffic profile.
80     :param framesize: Frame size.
81     :param duration: Duration of traffic run in seconds (-1=infinite).
82     :param rate: Traffic rate [percentage, pps, bps].
83     :param warmup_time: Traffic warm-up time in seconds, 0 = disable.
84     :param port_0: Port 0 on the traffic generator.
85     :param port_1: Port 1 on the traffic generator.
86     :param latency: With latency stats.
87     :param async_start: Start the traffic and exit.
88     :param unidirection: Traffic is unidirectional.
89     :type profile_file: str
90     :type framesize: int or str
91     :type duration: float
92     :type rate: str
93     :type warmup_time: float
94     :type port_0: int
95     :type port_1: int
96     :type latency: bool
97     :type async_start: bool
98     :type unidirection: bool
99     """
100
101     #unidirection traffic
102     if unidirection:
103         send_traffic_unidirection(profile_file, duration, framesize, rate,
104                                   warmup_time, port_0, port_1, latency,
105                                   async_start)
106     #bidirection traffic
107     else:
108         send_traffic_bidirection(profile_file, duration, framesize, rate,
109                                  warmup_time, port_0, port_1, latency,
110                                  async_start)
111
112
113 def send_traffic_bidirection(profile_file, duration, framesize, rate,
114                              warmup_time, port_0, port_1, latency,
115                              async_start=False):
116     """Send traffic bidirection and measure packet loss and latency.
117
118     :param profile_file: A python module with T-rex traffic profile.
119     :param framesize: Frame size.
120     :param duration: Duration of traffic run in seconds (-1=infinite).
121     :param rate: Traffic rate [percentage, pps, bps].
122     :param warmup_time: Traffic warm-up time in seconds, 0 = disable.
123     :param port_0: Port 0 on the traffic generator.
124     :param port_1: Port 1 on the traffic generator.
125     :param latency: With latency stats.
126     :param async_start: Start the traffic and exit.
127     :type profile_file: str
128     :type framesize: int or str
129     :type duration: float
130     :type rate: str
131     :type warmup_time: float
132     :type port_0: int
133     :type port_1: int
134     :type latency: bool
135     :type async_start: bool
136     """
137
138     client = None
139     total_rcvd = 0
140     total_sent = 0
141     lost_a = 0
142     lost_b = 0
143     lat_a = "-1/-1/-1"
144     lat_b = "-1/-1/-1"
145
146     # Read the profile:
147     try:
148         print("### Profile file:\n{}".format(profile_file))
149         profile = STLProfile.load(profile_file, direction=0, port_id=0,
150                                   framesize=framesize)
151         streams = profile.get_streams()
152     except STLError as err:
153         print("Error while loading profile '{0}' {1}".format(profile_file, err))
154         sys.exit(1)
155
156     try:
157         # Create the client:
158         client = STLClient(verbose_level=LoggerApi.VERBOSE_QUIET)
159         # Connect to server:
160         client.connect()
161         # Prepare our ports (the machine has 0 <--> 1 with static route):
162         client.reset(ports=[port_0, port_1])
163         client.remove_all_streams(ports=[port_0, port_1])
164
165         if "macsrc" in profile_file:
166             client.set_port_attr(ports=[port_0, port_1], promiscuous=True,
167                                  resolve=False)
168         if isinstance(framesize, int):
169             client.add_streams(streams[0], ports=[port_0])
170             client.add_streams(streams[1], ports=[port_1])
171         elif isinstance(framesize, str):
172             client.add_streams(streams[0:3], ports=[port_0])
173             client.add_streams(streams[3:6], ports=[port_1])
174         if latency:
175             try:
176                 if isinstance(framesize, int):
177                     client.add_streams(streams[2], ports=[port_0])
178                     client.add_streams(streams[3], ports=[port_1])
179                 elif isinstance(framesize, str):
180                     latency = False
181             except STLError:
182                 # Disable latency if NIC does not support requested stream type
183                 print("##### FAILED to add latency streams #####")
184                 latency = False
185         # Warm-up phase:
186         if warmup_time > 0:
187             # Clear the stats before injecting:
188             client.clear_stats()
189
190             # Choose rate and start traffic:
191             client.start(ports=[port_0, port_1], mult=rate,
192                          duration=warmup_time)
193
194             # Block until done:
195             client.wait_on_traffic(ports=[port_0, port_1],
196                                    timeout=warmup_time+30)
197
198             if client.get_warnings():
199                 for warning in client.get_warnings():
200                     print(warning)
201
202             # Read the stats after the test:
203             stats = client.get_stats()
204
205             print("##### Warmup statistics #####")
206             print(json.dumps(stats, indent=4, separators=(',', ': '),
207                              sort_keys=True))
208
209             lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
210             lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
211
212             print("\npackets lost from 0 --> 1: {0} pkts".format(lost_a))
213             print("packets lost from 1 --> 0: {0} pkts".format(lost_b))
214
215         # Clear the stats before injecting:
216         client.clear_stats()
217         lost_a = 0
218         lost_b = 0
219
220         # Choose rate and start traffic:
221         client.start(ports=[port_0, port_1], mult=rate, duration=duration)
222
223         if not async_start:
224             # Block until done:
225             client.wait_on_traffic(ports=[port_0, port_1], timeout=duration+30)
226
227             if client.get_warnings():
228                 for warning in client.get_warnings():
229                     print(warning)
230
231             # Read the stats after the test
232             stats = client.get_stats()
233
234             print("##### Statistics #####")
235             print(json.dumps(stats, indent=4, separators=(',', ': '),
236                              sort_keys=True))
237
238             lost_a = stats[0]["opackets"] - stats[1]["ipackets"]
239             lost_b = stats[1]["opackets"] - stats[0]["ipackets"]
240
241             if latency:
242                 lat_a = fmt_latency(
243                     str(stats["latency"][0]["latency"]["total_min"]),
244                     str(stats["latency"][0]["latency"]["average"]),
245                     str(stats["latency"][0]["latency"]["total_max"]))
246                 lat_b = fmt_latency(
247                     str(stats["latency"][1]["latency"]["total_min"]),
248                     str(stats["latency"][1]["latency"]["average"]),
249                     str(stats["latency"][1]["latency"]["total_max"]))
250
251             total_sent = stats[0]["opackets"] + stats[1]["opackets"]
252             total_rcvd = stats[0]["ipackets"] + stats[1]["ipackets"]
253
254             print("\npackets lost from 0 --> 1:   {0} pkts".format(lost_a))
255             print("packets lost from 1 --> 0:   {0} pkts".format(lost_b))
256
257     except STLError as err:
258         sys.stderr.write("{0}\n".format(err))
259         sys.exit(1)
260
261     finally:
262         if async_start:
263             if client:
264                 client.disconnect(stop_traffic=False, release_ports=True)
265         else:
266             if client:
267                 client.disconnect()
268             print("rate={0}, totalReceived={1}, totalSent={2}, "
269                   "frameLoss={3}, latencyStream0(usec)={4}, "
270                   "latencyStream1(usec)={5}".
271                   format(rate, total_rcvd, total_sent, lost_a + lost_b,
272                          lat_a, lat_b))
273
274
275 def send_traffic_unidirection(profile_file, duration, framesize, rate,
276                               warmup_time, port_0, port_1, latency,
277                               async_start=False):
278     """Send traffic unidirection and measure packet loss and latency.
279
280     :param profile_file: A python module with T-rex traffic profile.
281     :param framesize: Frame size.
282     :param duration: Duration of traffic run in seconds (-1=infinite).
283     :param rate: Traffic rate [percentage, pps, bps].
284     :param warmup_time: Traffic warm-up time in seconds, 0 = disable.
285     :param port_0: Port 0 on the traffic generator.
286     :param port_1: Port 1 on the traffic generator.
287     :param latency: With latency stats.
288     :param async_start: Start the traffic and exit.
289     :type profile_file: str
290     :type framesize: int or str
291     :type duration: float
292     :type rate: str
293     :type warmup_time: float
294     :type port_0: int
295     :type port_1: int
296     :type latency: bool
297     :type async_start: bool
298     """
299
300     client = None
301     total_rcvd = 0
302     total_sent = 0
303     lost_a = 0
304     lat_a = "-1/-1/-1"
305
306     # Read the profile:
307     try:
308         print("### Profile file:\n{}".format(profile_file))
309         profile = STLProfile.load(profile_file, direction=0, port_id=0,
310                                   framesize=framesize)
311         streams = profile.get_streams()
312     except STLError as err:
313         print("Error while loading profile '{0}' {1}".format(profile_file, err))
314         sys.exit(1)
315
316     try:
317         # Create the client:
318         client = STLClient(verbose_level=LoggerApi.VERBOSE_QUIET)
319         # Connect to server:
320         client.connect()
321         # Prepare our ports:
322         if port_0 == port_1:
323             client.reset(ports=[port_0])
324             client.remove_all_streams(ports=[port_0])
325
326             if "macsrc" in profile_file:
327                 client.set_port_attr(ports=[port_0], promiscuous=True,
328                                      resolve=False)
329         else:
330             client.reset(ports=[port_0, port_1])
331             client.remove_all_streams(ports=[port_0, port_1])
332
333             if "macsrc" in profile_file:
334                 client.set_port_attr(ports=[port_0, port_1], promiscuous=True,
335                                      resolve=False)
336
337         if isinstance(framesize, int):
338             client.add_streams(streams[0], ports=[port_0])
339         elif isinstance(framesize, str):
340             client.add_streams(streams[0:3], ports=[port_0])
341         if latency:
342             try:
343                 if isinstance(framesize, int):
344                     client.add_streams(streams[2], ports=[port_0])
345                 elif isinstance(framesize, str):
346                     latency = False
347             except STLError:
348                 # Disable latency if NIC does not support requested stream type
349                 print("##### FAILED to add latency streams #####")
350                 latency = False
351
352         # Warm-up phase:
353         if warmup_time > 0:
354             # Clear the stats before injecting:
355             client.clear_stats()
356
357             # Choose rate and start traffic:
358             client.start(ports=[port_0], mult=rate,
359                          duration=warmup_time)
360
361             # Block until done:
362             client.wait_on_traffic(ports=[port_0],
363                                    timeout=warmup_time+30)
364
365             if client.get_warnings():
366                 for warning in client.get_warnings():
367                     print(warning)
368
369             # Read the stats after the test:
370             stats = client.get_stats()
371
372             print("##### Warmup statistics #####")
373             print(json.dumps(stats, indent=4, separators=(',', ': '),
374                              sort_keys=True))
375
376             lost_a = stats[port_0]["opackets"] - stats[port_1]["ipackets"]
377             print("\npackets lost : {0} pkts".format(lost_a))
378
379         # Clear the stats before injecting:
380         client.clear_stats()
381         lost_a = 0
382
383         # Choose rate and start traffic:
384         client.start(ports=[port_0], mult=rate, duration=duration)
385
386         if not async_start:
387             # Block until done:
388             client.wait_on_traffic(ports=[port_0], timeout=duration+30)
389
390             if client.get_warnings():
391                 for warning in client.get_warnings():
392                     print(warning)
393
394             # Read the stats after the test
395             stats = client.get_stats()
396
397             print("##### Statistics #####")
398             print(json.dumps(stats, indent=4, separators=(',', ': '),
399                              sort_keys=True))
400
401             lost_a = stats[port_0]["opackets"] - stats[port_1]["ipackets"]
402
403             if latency:
404                 lat_a = fmt_latency(
405                     str(stats["latency"][0]["latency"]["total_min"]),
406                     str(stats["latency"][0]["latency"]["average"]),
407                     str(stats["latency"][0]["latency"]["total_max"]))
408
409             total_sent = stats[port_0]["opackets"]
410             total_rcvd = stats[port_1]["ipackets"]
411
412             print("\npackets lost : {0} pkts".format(lost_a))
413
414     except STLError as err:
415         sys.stderr.write("{0}\n".format(err))
416         sys.exit(1)
417
418     finally:
419         if async_start:
420             if client:
421                 client.disconnect(stop_traffic=False, release_ports=True)
422         else:
423             if client:
424                 client.disconnect()
425             print("rate={0}, totalReceived={1}, totalSent={2}, "
426                   "frameLoss={3}, latencyStream0(usec)={4}".
427                   format(rate, total_rcvd, total_sent, lost_a, lat_a))
428
429
430 def main():
431     """Main function for the traffic generator using T-rex.
432
433     It verifies the given command line arguments and runs "simple_burst"
434     function.
435     """
436
437     parser = argparse.ArgumentParser()
438     parser.add_argument("-p", "--profile",
439                         required=True,
440                         type=str,
441                         help="Python traffic profile.")
442     parser.add_argument("-d", "--duration",
443                         required=True,
444                         type=float,
445                         help="Duration of traffic run.")
446     parser.add_argument("-s", "--frame_size",
447                         required=True,
448                         help="Size of a Frame without padding and IPG.")
449     parser.add_argument("-r", "--rate",
450                         required=True,
451                         help="Traffic rate with included units (%, pps).")
452     parser.add_argument("-w", "--warmup_time",
453                         type=float,
454                         default=5.0,
455                         help="Traffic warm-up time in seconds, 0 = disable.")
456     parser.add_argument("--port_0",
457                         required=True,
458                         type=int,
459                         help="Port 0 on the traffic generator.")
460     parser.add_argument("--port_1",
461                         required=True,
462                         type=int,
463                         help="Port 1 on the traffic generator.")
464     parser.add_argument("--async",
465                         action="store_true",
466                         default=False,
467                         help="Non-blocking call of the script.")
468     parser.add_argument("--latency",
469                         action="store_true",
470                         default=False,
471                         help="Add latency stream.")
472     parser.add_argument("--unidirection",
473                         action="store_true",
474                         default=False,
475                         help="Send unidirection traffic.")
476
477     args = parser.parse_args()
478
479     try:
480         framesize = int(args.frame_size)
481     except ValueError:
482         framesize = args.frame_size
483
484     simple_burst(profile_file=args.profile,
485                  duration=args.duration,
486                  framesize=framesize,
487                  rate=args.rate,
488                  warmup_time=args.warmup_time,
489                  port_0=args.port_0,
490                  port_1=args.port_1,
491                  latency=args.latency,
492                  async_start=args.async,
493                  unidirection=args.unidirection)
494
495
496 if __name__ == '__main__':
497     main()