3 # Copyright (c) 2020 Cisco and/or its affiliates.
5 # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
7 # Licensed under the Apache License 2.0 or
8 # GNU General Public License v2.0 or later; you may not use this file
9 # except in compliance with one of these Licenses. You
10 # may obtain a copy of the Licenses at:
12 # http://www.apache.org/licenses/LICENSE-2.0
13 # https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
15 # Note: If this file is linked with Scapy, which is GPLv2+, your use of it
16 # must be under GPLv2+. If at any point in the future it is no longer linked
17 # with Scapy (or other GPLv2+ licensed software), you are free to choose Apache 2.
19 # Unless required by applicable law or agreed to in writing, software
20 # distributed under the License is distributed on an "AS IS" BASIS,
21 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 # See the License for the specific language governing permissions and
23 # limitations under the License.
25 """This module gets a traffic profile together with other parameters, reads
26 the profile and sends the traffic. At the end, it measures the packet loss and
36 0, u"/opt/trex-core-2.86/scripts/automation/trex_control_plane/interactive/"
38 from trex.stl.api import *
41 def fmt_latency(lat_min, lat_avg, lat_max, hdrh):
42 """Return formatted, rounded latency.
44 :param lat_min: Min latency
45 :param lat_avg: Average latency
46 :param lat_max: Max latency
47 :param hdrh: Base64 encoded compressed HDRHistogram object.
52 :return: Formatted and rounded output (hdrh unchanged) "min/avg/max/hdrh".
56 t_min = int(round(float(lat_min)))
60 t_avg = int(round(float(lat_avg)))
64 t_max = int(round(float(lat_max)))
68 return u"/".join(str(tmp) for tmp in (t_min, t_avg, t_max, hdrh))
83 """Send traffic and measure packet loss and latency.
86 - reads the given traffic profile with streams,
87 - connects to the T-rex client,
89 - removes all existing streams,
90 - adds streams from the traffic profile to the ports,
91 - if the warm-up time is more than 0, sends the warm-up traffic, reads the
93 - clears the statistics from the client,
95 - waits for the defined time (or runs forever if async mode is defined),
97 - reads and displays the statistics and
98 - disconnects from the client.
100 :param profile_file: A python module with T-rex traffic profile.
101 :param framesize: Frame size.
102 :param duration: Duration of traffic run in seconds (-1=infinite).
103 :param rate: Traffic rate [percentage, pps, bps].
104 :param port_0: Port 0 on the traffic generator.
105 :param port_1: Port 1 on the traffic generator.
106 :param latency: With latency stats.
107 :param async_start: Start the traffic and exit.
108 :param traffic_directions: Bidirectional (2) or unidirectional (1) traffic.
109 :param force: Force start regardless of ports state.
110 :type profile_file: str
111 :type framesize: int or str
112 :type duration: float
117 :type async_start: bool
118 :type traffic_directions: int
124 approximated_duration = 0.0
132 print(f"### Profile file:\n{profile_file}")
133 profile = STLProfile.load(
134 profile_file, direction=0, port_id=0, framesize=framesize,
137 streams = profile.get_streams()
139 print(f"Error while loading profile '{profile_file}'!")
147 # Prepare our ports (the machine has 0 <--> 1 with static route):
148 client.reset(ports=[port_0, port_1])
149 client.remove_all_streams(ports=[port_0, port_1])
151 if u"macsrc" in profile_file:
152 client.set_port_attr(ports=[port_0, port_1], promiscuous=True)
153 if isinstance(framesize, int):
154 last_stream_a = int((len(streams) - 2 ) / 2)
155 last_stream_b = (last_stream_a * 2)
156 client.add_streams(streams[0:last_stream_a], ports=[port_0])
157 if traffic_directions > 1:
159 streams[last_stream_a:last_stream_b], ports=[port_1])
160 elif isinstance(framesize, str):
161 client.add_streams(streams[0:3], ports=[port_0])
162 if traffic_directions > 1:
163 client.add_streams(streams[3:6], ports=[port_1])
166 if isinstance(framesize, int):
167 client.add_streams(streams[last_stream_b], ports=[port_0])
168 if traffic_directions > 1:
170 streams[last_stream_b + 1], ports=[port_1])
171 elif isinstance(framesize, str):
174 # Disable latency if NIC does not support requested stream type
175 print(u"##### FAILED to add latency streams #####")
178 if traffic_directions > 1:
181 # Clear the stats before injecting:
186 # Choose rate and start traffic:
192 core_mask=STLClient.CORE_MASK_PIN,
196 # For async stop, we need to export the current snapshot.
197 xsnap0 = client.ports[0].get_xstats().reference_stats
198 print(f"Xstats snapshot 0: {xsnap0!r}")
199 if traffic_directions > 1:
200 xsnap1 = client.ports[1].get_xstats().reference_stats
201 print(f"Xstats snapshot 1: {xsnap1!r}")
204 time_start = time.monotonic()
205 client.wait_on_traffic(ports=ports, timeout=duration+30)
206 time_stop = time.monotonic()
207 approximated_duration = time_stop - time_start
209 if client.get_warnings():
210 for warning in client.get_warnings():
213 # Read the stats after the test
214 stats = client.get_stats()
216 print(u"##### Statistics #####")
217 print(json.dumps(stats, indent=4, separators=(u",", u": ")))
219 lost_a = stats[port_0][u"opackets"] - stats[port_1][u"ipackets"]
220 if traffic_directions > 1:
221 lost_b = stats[port_1][u"opackets"] - stats[port_0][u"ipackets"]
223 # Stats index is not a port number, but "pgid".
225 lat_obj = stats[u"latency"][0][u"latency"]
227 str(lat_obj[u"total_min"]), str(lat_obj[u"average"]),
228 str(lat_obj[u"total_max"]), str(lat_obj[u"hdrh"]))
229 if traffic_directions > 1:
230 lat_obj = stats[u"latency"][1][u"latency"]
232 str(lat_obj[u"total_min"]), str(lat_obj[u"average"]),
233 str(lat_obj[u"total_max"]), str(lat_obj[u"hdrh"]))
235 if traffic_directions > 1:
236 total_sent = stats[0][u"opackets"] + stats[1][u"opackets"]
237 total_rcvd = stats[0][u"ipackets"] + stats[1][u"ipackets"]
239 total_sent = stats[port_0][u"opackets"]
240 total_rcvd = stats[port_1][u"ipackets"]
242 print(f"\npackets lost from {port_0} --> {port_1}: {lost_a} pkts")
243 if traffic_directions > 1:
244 print(f"packets lost from {port_1} --> {port_0}: {lost_b} pkts")
247 print(u"T-Rex STL runtime error!", file=sys.stderr)
253 client.disconnect(stop_traffic=False, release_ports=True)
259 f"total_received={total_rcvd}; "
260 f"total_sent={total_sent}; "
261 f"frame_loss={lost_a + lost_b}; "
262 f"target_duration={duration!r}; "
263 f"approximated_duration={approximated_duration!r}; "
264 f"latency_stream_0(usec)={lat_a}; "
265 f"latency_stream_1(usec)={lat_b}; "
270 """Main function for the traffic generator using T-rex.
272 It verifies the given command line arguments and runs "simple_burst"
275 parser = argparse.ArgumentParser()
277 u"-p", u"--profile", required=True, type=str,
278 help=u"Python traffic profile."
281 u"-d", u"--duration", required=True, type=float,
282 help=u"Duration of traffic run."
285 u"-s", u"--frame_size", required=True,
286 help=u"Size of a Frame without padding and IPG."
289 u"-r", u"--rate", required=True,
290 help=u"Traffic rate with included units (pps)."
293 u"--port_0", required=True, type=int,
294 help=u"Port 0 on the traffic generator."
297 u"--port_1", required=True, type=int,
298 help=u"Port 1 on the traffic generator."
301 u"--async_start", action=u"store_true", default=False,
302 help=u"Non-blocking call of the script."
305 u"--latency", action=u"store_true", default=False,
306 help=u"Add latency stream."
309 u"--traffic_directions", type=int, default=2,
310 help=u"Send bi- (2) or uni- (1) directional traffic."
313 u"--force", action=u"store_true", default=False,
314 help=u"Force start regardless of ports state."
317 args = parser.parse_args()
320 framesize = int(args.frame_size)
322 framesize = args.frame_size
325 profile_file=args.profile,
326 duration=args.duration,
331 latency=args.latency,
332 async_start=args.async_start,
333 traffic_directions=args.traffic_directions,
338 if __name__ == u"__main__":