1 # Copyright (c) 2021 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """iPerf3 utilities library."""
18 from resources.libraries.python.Constants import Constants
19 from resources.libraries.python.CpuUtils import CpuUtils
20 from resources.libraries.python.IPUtil import IPUtil
21 from resources.libraries.python.Namespaces import Namespaces
22 from resources.libraries.python.OptionString import OptionString
23 from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
27 """iPerf3 traffic generator utilities."""
30 """Initialize iPerf3 class."""
31 # Computed affinity for iPerf server.
32 self._s_affinity = None
33 # Computed affinity for iPerf client.
34 self._c_affinity = None
36 def initialize_iperf_server(
37 self, node, pf_key, interface, bind, bind_gw, bind_mask,
38 namespace=None, cpu_skip_cnt=0, cpu_cnt=1, instances=1):
39 """iPerf3 initialization.
41 :param node: Topology node running iPerf3 server.
42 :param pf_key: First TG's interface (To compute numa location).
43 :param interface: Name of TG bind interface.
44 :param bind: Bind to host, one of node's addresses.
45 :param bind_gw: Bind gateway (required for default route).
46 :param bind_mask: Bind address mask.
47 :param namespace: Name of TG namespace to execute.
48 :param cpu_skip_cnt: Amount of CPU cores to skip.
49 :param cpu_cnt: iPerf3 main thread count.
50 :param instances: Number of simultaneous iPerf3 instances.
58 :type cpu_skip_cnt: int
62 if Iperf3.is_iperf_running(node):
63 Iperf3.teardown_iperf(node)
66 IPUtil.set_linux_interface_ip(
67 node, interface=interface, ip_addr=bind, prefix=bind_mask,
69 IPUtil.set_linux_interface_up(
70 node, interface=interface, namespace=namespace)
71 Namespaces.add_default_route_to_namespace(
72 node, namespace=namespace, default_route=bind_gw)
74 # Compute affinity for iPerf server.
75 self._s_affinity = CpuUtils.get_affinity_iperf(
76 node, pf_key, cpu_skip_cnt=cpu_skip_cnt,
77 cpu_cnt=cpu_cnt * instances)
78 # Compute affinity for iPerf client.
79 self._c_affinity = CpuUtils.get_affinity_iperf(
80 node, pf_key, cpu_skip_cnt=cpu_skip_cnt + cpu_cnt * instances,
81 cpu_cnt=cpu_cnt * instances)
83 for i in range(0, instances):
84 Iperf3.start_iperf_server(
85 node, namespace=namespace, port=5201 + i,
86 affinity=self._s_affinity)
89 def start_iperf_server(
90 node, namespace=None, port=5201, affinity=None):
91 """Start iPerf3 server instance as a deamon.
93 :param node: Topology node running iPerf3 server.
94 :param namespace: Name of TG namespace to execute.
95 :param port: The server port for the server to listen on.
96 :param affinity: iPerf3 server affinity.
102 cmd = IPerf3Server.iperf3_cmdline(
103 namespace=namespace, port=port, affinity=affinity)
105 node, cmd, sudo=True, message=u"Failed to start iPerf3 server!")
108 def is_iperf_running(node):
109 """Check if iPerf3 is running using pgrep.
111 :param node: Topology node running iPerf3.
113 :returns: True if iPerf3 is running otherwise False.
116 ret, _, _ = exec_cmd(node, u"pgrep iperf3", sudo=True)
117 return bool(int(ret) == 0)
120 def teardown_iperf(node):
123 :param node: Topology node running iPerf3.
126 pidfile = u"/tmp/iperf3_server.pid"
127 logfile = u"/tmp/iperf3.log"
131 f"sh -c 'if [ -f {pidfile} ]; then "
136 sudo=True, message=u"iPerf3 kill failed!")
138 def iperf_client_start_remote_exec(
139 self, node, duration, rate, frame_size, async_call=False,
140 warmup_time=0, traffic_directions=1, namespace=None, udp=False,
141 host=None, bind=None, affinity=None):
142 """Execute iPerf3 client script on remote node over ssh to start running
145 :param node: Topology node running iPerf3.
146 :param duration: Time expressed in seconds for how long to send traffic.
147 :param rate: Traffic rate.
148 :param frame_size: L2 frame size to send (without padding and IPG).
149 :param async_call: If enabled then don't wait for all incoming traffic.
150 :param warmup_time: Warmup time period.
151 :param traffic_directions: Traffic is bi- (2) or uni- (1) directional.
153 :param namespace: Namespace to execute iPerf3 client on.
154 :param udp: UDP traffic.
155 :param host: Client connecting to an iPerf server running on host.
156 :param bind: Client bind IP address.
157 :param affinity: iPerf3 client affinity.
159 :type duration: float
161 :type frame_size: str
162 :type async_call: bool
163 :type warmup_time: float
164 :type traffic_directions: int
170 :returns: List of iPerf3 PIDs.
173 if not isinstance(duration, (float, int)):
174 duration = float(duration)
175 if not isinstance(warmup_time, (float, int)):
176 warmup_time = float(warmup_time)
178 affinity = self._c_affinity
182 kwargs[u"namespace"] = namespace
183 kwargs[u"host"] = host
184 kwargs[u"bind"] = bind
187 kwargs[u"affinity"] = affinity
188 kwargs[u"duration"] = duration
189 kwargs[u"rate"] = rate
190 kwargs[u"frame_size"] = frame_size
191 kwargs[u"warmup_time"] = warmup_time
192 kwargs[u"traffic_directions"] = traffic_directions
193 kwargs[u"async_call"] = async_call
195 cmd = IPerf3Client.iperf3_cmdline(**kwargs)
197 stdout, _ = exec_cmd_no_error(
198 node, cmd, timeout=int(duration) + 30,
199 message=u"iPerf3 runtime error!")
202 return stdout.split()
203 return json.loads(stdout)
206 def iperf_client_stop_remote_exec(node, pids):
207 """Stop iPerf3 client execution.
209 :param pids: PID or List of PIDs of iPerf3 client.
210 :type pids: str or list
212 if not isinstance(pids, list):
217 node, f"kill {pid}", sudo=True, message=u"Kill iPerf3 failed!")
221 """iPerf3 server utilities."""
224 def iperf3_cmdline(**kwargs):
225 """Get iPerf3 server command line.
227 :param kwargs: List of iPerf3 server parameters.
229 :returns: iPerf3 server command line.
233 if kwargs['namespace']:
234 cmd.add(f"ip netns exec {kwargs['namespace']}")
237 cmd_options = OptionString(prefix=u"--")
238 # Run iPerf in server mode. (This will only allow one iperf connection
243 # Run the server in background as a daemon.
244 cmd_options.add_if_from_dict(
245 u"daemon", u"daemon", kwargs, True)
247 # Write a file with the process ID, most useful when running as a
249 cmd_options.add_with_value_from_dict(
250 u"pidfile", u"pidfile", kwargs, f"/tmp/iperf3_server.pid")
252 # Send output to a log file.
253 cmd_options.add_with_value_from_dict(
254 u"logfile", u"logfile", kwargs, f"/tmp/iperf3.log")
256 # The server port for the server to listen on and the client to
257 # connect to. This should be the same in both client and server.
259 cmd_options.add_with_value_from_dict(
260 u"port", u"port", kwargs, 5201)
262 # Set the CPU affinity, if possible (Linux and FreeBSD only).
263 cmd_options.add_with_value_from_dict(
264 u"affinity", u"affinity", kwargs)
266 # Output in JSON format.
267 cmd_options.add_if_from_dict(
268 u"json", u"json", kwargs, True)
270 # Give more detailed output.
271 cmd_options.add_if_from_dict(
272 u"verbose", u"verbose", kwargs, True)
274 return cmd.extend(cmd_options)
278 """iPerf3 client utilities."""
281 def iperf3_cmdline(**kwargs):
282 """Get iperf_client driver command line.
284 :param kwargs: List of iperf_client driver parameters.
286 :returns: iperf_client driver command line.
291 dirname = f"{Constants.REMOTE_FW_DIR}/resources/tools/iperf"
292 cmd.add(f"'{dirname}/iperf_client.py'")
294 cmd_options = OptionString(prefix=u"--")
295 # Namespace to execute iPerf3 client on.
296 cmd_options.add_with_value_from_dict(
297 u"namespace", u"namespace", kwargs)
299 # Client connecting to an iPerf3 server running on host.
300 cmd_options.add_with_value_from_dict(
301 u"host", u"host", kwargs)
303 # Client bind IP address.
304 cmd_options.add_with_value_from_dict(
305 u"bind", u"bind", kwargs)
307 # Use UDP rather than TCP.
308 cmd_options.add_if_from_dict(
309 u"udp", u"udp", kwargs, False)
311 # Set the CPU affinity, if possible.
312 cmd_options.add_with_value_from_dict(
313 u"affinity", u"affinity", kwargs)
315 # Time expressed in seconds for how long to send traffic.
316 cmd_options.add_with_value_from_dict(
317 u"duration", u"duration", kwargs)
319 # Send bi- (2) or uni- (1) directional traffic.
320 cmd_options.add_with_value_from_dict(
321 u"traffic_directions", u"traffic_directions", kwargs, 1)
323 # Traffic warm-up time in seconds, (0=disable).
324 cmd_options.add_with_value_from_dict(
325 u"warmup_time", u"warmup_time", kwargs, 5.0)
327 # L2 frame size to send (without padding and IPG).
328 cmd_options.add_with_value_from_dict(
329 u"frame_size", u"frame_size", kwargs)
331 # Traffic rate expressed with units.
332 cmd_options.add_with_value_from_dict(
333 u"rate", u"rate", kwargs)
335 # If enabled then don't wait for all incoming traffic.
336 cmd_options.add_if_from_dict(
337 u"async_start", u"async_call", kwargs, False)
339 # Number of iPerf3 client parallel instances.
340 cmd_options.add_with_value_from_dict(
341 u"instances", u"instances", kwargs, 1)
343 # Number of iPerf3 client parallel flows.
344 cmd_options.add_with_value_from_dict(
345 u"parallel", u"parallel", kwargs, 8)
347 return cmd.extend(cmd_options)