blob: be4dd6f131b3a86cc26881d63bc610c241bca2b8 [file] [log] [blame]
Pau Espin Pedrol8a725862018-10-26 15:54:28 +02001# osmo_gsm_tester: specifics for running an iperf3 client and server
2#
3# Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Pau Espin Pedrol <pespin@sysmocom.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20import os
21import json
22
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010023from . import log, util, process, pcap_recorder, run_node, remote
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020024
Pau Espin Pedrol64f0b1b2018-11-09 17:44:10 +010025def iperf3_result_to_json(file):
26 with open(file) as f:
27 # Sometimes iperf3 provides 2 dictionaries, the 2nd one being an error about being interrupted (by us).
28 # json parser doesn't support (raises exception) parsing several dictionaries at a time (not a valid json object).
29 # We are only interested in the first dictionary, the regular results one:
30 d = f.read().split("\n}\n")[0] + "\n}\n"
31 data = json.loads(d)
32 return data
33
34
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020035class IPerf3Server(log.Origin):
36
Pau Espin Pedrolcf6a3602018-11-14 16:38:19 +010037 DEFAULT_SRV_PORT = 5003
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010038 LOGFILE = 'iperf3_srv.json'
39 REMOTE_DIR = '/tmp'
Pau Espin Pedrolcf6a3602018-11-14 16:38:19 +010040
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020041 def __init__(self, suite_run, ip_address):
42 super().__init__(log.C_RUN, 'iperf3-srv_%s' % ip_address.get('addr'))
43 self.run_dir = None
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020044 self.process = None
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010045 self._run_node = None
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020046 self.suite_run = suite_run
47 self.ip_address = ip_address
Pau Espin Pedrolcf6a3602018-11-14 16:38:19 +010048 self._port = IPerf3Server.DEFAULT_SRV_PORT
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010049 self.log_file = None
50 self.rem_host = None
51 self.remote_log_file = None
52 self.log_copied = False
53
54 def cleanup(self):
55 if self.process is None:
56 return
57 if self.runs_locally():
58 return
59 # copy back files (may not exist, for instance if there was an early error of process):
60 try:
61 self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
62 except Exception as e:
63 self.log(repr(e))
64
65 def runs_locally(self):
66 locally = not self._run_node or self._run_node.is_local()
67 return locally
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020068
69 def start(self):
70 self.log('Starting iperf3-srv')
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010071 self.log_copied = False
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020072 self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010073 self.log_file = self.run_dir.new_file(IPerf3Server.LOGFILE)
74 if self.runs_locally():
75 self.start_locally()
76 else:
77 self.start_remotely()
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020078
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010079 def start_remotely(self):
80 self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
81 remote_prefix_dir = util.Dir(IPerf3Server.REMOTE_DIR)
82 remote_run_dir = util.Dir(remote_prefix_dir.child('srv-' + str(self)))
83 self.remote_log_file = remote_run_dir.child(IPerf3Server.LOGFILE)
84
85 self.rem_host.recreate_remote_dir(remote_run_dir)
86
87 args = ('iperf3', '-s', '-B', self.addr(),
88 '-p', str(self._port), '-J',
89 '--logfile', self.remote_log_file)
90 self.process = self.rem_host.RemoteProcess(self.name(), args)
91 self.suite_run.remember_to_stop(self.process)
92 self.process.launch()
93
94 def start_locally(self):
Pau Espin Pedrol8a725862018-10-26 15:54:28 +020095 pcap_recorder.PcapRecorder(self.suite_run, self.run_dir.new_dir('pcap'), None,
96 'host %s and port not 22' % self.addr())
97
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +010098 args = ('iperf3', '-s', '-B', self.addr(),
99 '-p', str(self._port), '-J',
100 '--logfile', os.path.abspath(self.log_file))
101
102 self.process = process.Process(self.name(), self.run_dir, args, env={})
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200103 self.suite_run.remember_to_stop(self.process)
104 self.process.launch()
105
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100106 def set_run_node(self, run_node):
107 self._run_node = run_node
108
Pau Espin Pedrolcf6a3602018-11-14 16:38:19 +0100109 def set_port(self, port):
110 self._port = port
111
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200112 def stop(self):
113 self.suite_run.stop_process(self.process)
114
115 def get_results(self):
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100116 if not self.runs_locally() and not self.log_copied:
117 self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
118 self.log_copied = True
Pau Espin Pedrol64f0b1b2018-11-09 17:44:10 +0100119 return iperf3_result_to_json(self.log_file)
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200120
121 def addr(self):
122 return self.ip_address.get('addr')
123
124 def port(self):
125 return self._port
126
Pau Espin Pedrol0df63172018-11-14 16:39:30 +0100127 def __str__(self):
128 return "%s:%u" %(self.addr(), self.port())
129
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200130 def running(self):
131 return not self.process.terminated()
132
133 def create_client(self):
134 return IPerf3Client(self.suite_run, self)
135
136class IPerf3Client(log.Origin):
137
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100138 REMOTE_DIR = '/tmp'
139 LOGFILE = 'iperf3_cli.json'
140
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200141 def __init__(self, suite_run, iperf3srv):
142 super().__init__(log.C_RUN, 'iperf3-cli_%s' % iperf3srv.addr())
143 self.run_dir = None
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200144 self.process = None
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100145 self._run_node = None
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200146 self.server = iperf3srv
147 self.suite_run = suite_run
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100148 self.log_file = None
149 self.rem_host = None
150 self.remote_log_file = None
151 self.log_copied = False
152
153 def runs_locally(self):
154 locally = not self._run_node or self._run_node.is_local()
155 return locally
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200156
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100157 def prepare_test_proc(self, downlink=False, netns=None, time_sec=10):
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200158 self.log('Starting iperf3-client connecting to %s:%d' % (self.server.addr(), self.server.port()))
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100159 self.log_copied = False
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200160 self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100161 self.log_file = self.run_dir.new_file(IPerf3Client.LOGFILE)
162 if self.runs_locally():
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100163 return self.prepare_test_proc_locally(downlink, netns, time_sec)
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100164 else:
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100165 return self.prepare_test_proc_remotely(downlink, netns, time_sec)
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200166
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100167 def prepare_test_proc_remotely(self, downlink, netns, time_sec):
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100168 self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
169
170 remote_prefix_dir = util.Dir(IPerf3Client.REMOTE_DIR)
171 remote_run_dir = util.Dir(remote_prefix_dir.child('cli-' + str(self)))
172 self.remote_log_file = remote_run_dir.child(IPerf3Client.LOGFILE)
173
174 self.rem_host.recreate_remote_dir(remote_run_dir)
175
176 popen_args = ('iperf3', '-c', self.server.addr(),
177 '-p', str(self.server.port()), '-J',
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100178 '--logfile', self.remote_log_file,
179 '-t', str(time_sec))
Pau Espin Pedrol905e5032020-03-02 15:35:33 +0100180 if downlink:
181 popen_args += ('-R',)
182
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100183 if netns:
184 self.process = self.rem_host.RemoteNetNSProcess(self.name(), netns, popen_args, env={})
185 else:
186 self.process = self.rem_host.RemoteProcess(self.name(), popen_args, env={})
187 return self.process
188
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100189 def prepare_test_proc_locally(self, downlink, netns, time_sec):
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200190 pcap_recorder.PcapRecorder(self.suite_run, self.run_dir.new_dir('pcap'), None,
191 'host %s and port not 22' % self.server.addr(), netns)
192
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200193 popen_args = ('iperf3', '-c', self.server.addr(),
194 '-p', str(self.server.port()), '-J',
Pau Espin Pedrol085a17e2020-03-02 18:14:02 +0100195 '--logfile', os.path.abspath(self.log_file),
196 '-t', str(time_sec))
Pau Espin Pedrol905e5032020-03-02 15:35:33 +0100197 if downlink:
198 popen_args += ('-R',)
199
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200200 if netns:
201 self.process = process.NetNSProcess(self.name(), self.run_dir, netns, popen_args, env={})
202 else:
203 self.process = process.Process(self.name(), self.run_dir, popen_args, env={})
Pau Espin Pedrol0fc74372018-11-14 16:44:57 +0100204 return self.process
205
206 def run_test_sync(self, netns=None):
207 self.prepare_test_proc(netns)
Pau Espin Pedrol79df7392018-11-12 18:15:30 +0100208 self.process.launch_sync()
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200209 return self.get_results()
210
211 def get_results(self):
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100212 if not self.runs_locally() and not self.log_copied:
213 self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
214 self.log_copied = True
Pau Espin Pedrol64f0b1b2018-11-09 17:44:10 +0100215 return iperf3_result_to_json(self.log_file)
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200216
Pau Espin Pedrolc852ad82020-02-11 17:43:58 +0100217 def set_run_node(self, run_node):
218 self._run_node = run_node
219
220 def __str__(self):
221 # FIXME: somehow differentiate between several clients connected to same server?
222 return "%s:%u" %(self.server.addr(), self.server.port())
223
Pau Espin Pedrol8a725862018-10-26 15:54:28 +0200224# vim: expandtab tabstop=4 shiftwidth=4