<p>Pau Espin Pedrol <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/11476">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  Neels Hofmeyr: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Introduce iperf3 testing infrastructure<br><br>Change-Id: I6ff6bef14feb535d98ca41b9788700d699e1ef1e<br>---<br>A src/osmo_gsm_tester/iperf3.py<br>M src/osmo_gsm_tester/suite.py<br>A suites/gprs/iperf3.py<br>M suites/gprs/suite.conf<br>4 files changed, 189 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/src/osmo_gsm_tester/iperf3.py b/src/osmo_gsm_tester/iperf3.py</span><br><span>new file mode 100644</span><br><span>index 0000000..8141fea</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo_gsm_tester/iperf3.py</span><br><span>@@ -0,0 +1,107 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# osmo_gsm_tester: specifics for running an iperf3 client and server</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# Copyright (C) 2018 by sysmocom - s.f.m.c. GmbH</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# Author: Pau Espin Pedrol <pespin@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is free software: you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+# it under the terms of the GNU General Public License as</span><br><span style="color: hsl(120, 100%, 40%);">+# published by the Free Software Foundation, either version 3 of the</span><br><span style="color: hsl(120, 100%, 40%);">+# License, or (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+# GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+# along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+import os</span><br><span style="color: hsl(120, 100%, 40%);">+import json</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from . import log, util, process, pcap_recorder</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+DEFAULT_SRV_PORT = 5003</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class IPerf3Server(log.Origin):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, suite_run, ip_address):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(log.C_RUN, 'iperf3-srv_%s' % ip_address.get('addr'))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.run_dir = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config_file = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.process = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run = suite_run</span><br><span style="color: hsl(120, 100%, 40%);">+        self.ip_address = ip_address</span><br><span style="color: hsl(120, 100%, 40%);">+        self._port = DEFAULT_SRV_PORT</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def start(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('Starting iperf3-srv')</span><br><span style="color: hsl(120, 100%, 40%);">+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        pcap_recorder.PcapRecorder(self.suite_run, self.run_dir.new_dir('pcap'), None,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   'host %s and port not 22' % self.addr())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log_file = self.run_dir.new_file('iperf3_srv.json')</span><br><span style="color: hsl(120, 100%, 40%);">+        self.process = process.Process(self.name(), self.run_dir,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       ('iperf3', '-s', '-B', self.addr(),</span><br><span style="color: hsl(120, 100%, 40%);">+                                        '-p', str(self._port), '-J',</span><br><span style="color: hsl(120, 100%, 40%);">+                                        '--logfile', os.path.abspath(self.log_file)),</span><br><span style="color: hsl(120, 100%, 40%);">+                                       env={})</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run.remember_to_stop(self.process)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.process.launch()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def stop(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run.stop_process(self.process)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_results(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        with open(self.log_file) as f:</span><br><span style="color: hsl(120, 100%, 40%);">+                data = json.load(f)</span><br><span style="color: hsl(120, 100%, 40%);">+        return data</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def addr(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.ip_address.get('addr')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def port(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self._port</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def running(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return not self.process.terminated()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def create_client(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return IPerf3Client(self.suite_run, self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class IPerf3Client(log.Origin):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, suite_run, iperf3srv):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(log.C_RUN, 'iperf3-cli_%s' % iperf3srv.addr())</span><br><span style="color: hsl(120, 100%, 40%);">+        self.run_dir = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.config_file = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.process = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.server = iperf3srv</span><br><span style="color: hsl(120, 100%, 40%);">+        self.suite_run = suite_run</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def run_test(self, netns=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log('Starting iperf3-client connecting to %s:%d' % (self.server.addr(), self.server.port()))</span><br><span style="color: hsl(120, 100%, 40%);">+        self.run_dir = util.Dir(self.suite_run.get_test_run_dir().new_dir(self.name()))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        pcap_recorder.PcapRecorder(self.suite_run, self.run_dir.new_dir('pcap'), None,</span><br><span style="color: hsl(120, 100%, 40%);">+                                   'host %s and port not 22' % self.server.addr(), netns)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        self.log_file = self.run_dir.new_file('iperf3_cli.json')</span><br><span style="color: hsl(120, 100%, 40%);">+        popen_args = ('iperf3', '-c',  self.server.addr(),</span><br><span style="color: hsl(120, 100%, 40%);">+                      '-p', str(self.server.port()), '-J',</span><br><span style="color: hsl(120, 100%, 40%);">+                      '--logfile', os.path.abspath(self.log_file))</span><br><span style="color: hsl(120, 100%, 40%);">+        if netns:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.process = process.NetNSProcess(self.name(), self.run_dir, netns, popen_args, env={})</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.process = process.Process(self.name(), self.run_dir, popen_args, env={})</span><br><span style="color: hsl(120, 100%, 40%);">+        process.run_proc_sync(self.process)</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.get_results()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def get_results(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        with open(self.log_file) as f:</span><br><span style="color: hsl(120, 100%, 40%);">+                data = json.load(f)</span><br><span style="color: hsl(120, 100%, 40%);">+        return data</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# vim: expandtab tabstop=4 shiftwidth=4</span><br><span>diff --git a/src/osmo_gsm_tester/suite.py b/src/osmo_gsm_tester/suite.py</span><br><span>index 67ddefd..e5ac9a8 100644</span><br><span>--- a/src/osmo_gsm_tester/suite.py</span><br><span>+++ b/src/osmo_gsm_tester/suite.py</span><br><span>@@ -23,7 +23,7 @@</span><br><span> import pprint</span><br><span> from . import config, log, util, resource, test</span><br><span> from .event_loop import MainLoop</span><br><span style="color: hsl(0, 100%, 40%);">-from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, modem, esme, osmocon, ms_driver</span><br><span style="color: hsl(120, 100%, 40%);">+from . import osmo_nitb, osmo_hlr, osmo_mgcpgw, osmo_mgw, osmo_msc, osmo_bsc, osmo_stp, osmo_ggsn, osmo_sgsn, modem, esme, osmocon, ms_driver, iperf3</span><br><span> </span><br><span> class Timeout(Exception):</span><br><span>     pass</span><br><span>@@ -349,6 +349,12 @@</span><br><span>         self.register_for_cleanup(osmocon_obj)</span><br><span>         return osmocon_obj</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    def iperf3srv(self, ip_address=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        if ip_address is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            ip_address = self.ip_address()</span><br><span style="color: hsl(120, 100%, 40%);">+        iperf3srv_obj = iperf3.IPerf3Server(self, ip_address)</span><br><span style="color: hsl(120, 100%, 40%);">+        return iperf3srv_obj</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def msisdn(self):</span><br><span>         msisdn = self.resources_pool.next_msisdn(self)</span><br><span>         self.log('using MSISDN', msisdn)</span><br><span>diff --git a/suites/gprs/iperf3.py b/suites/gprs/iperf3.py</span><br><span>new file mode 100755</span><br><span>index 0000000..a1dd62c</span><br><span>--- /dev/null</span><br><span>+++ b/suites/gprs/iperf3.py</span><br><span>@@ -0,0 +1,74 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python3</span><br><span style="color: hsl(120, 100%, 40%);">+from osmo_gsm_tester.testenv import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def print_results(cli_res, srv_res):</span><br><span style="color: hsl(120, 100%, 40%);">+    cli_sent = cli_res['end']['sum_sent']</span><br><span style="color: hsl(120, 100%, 40%);">+    cli_recv = cli_res['end']['sum_received']</span><br><span style="color: hsl(120, 100%, 40%);">+    print("RESULT client:")</span><br><span style="color: hsl(120, 100%, 40%);">+    print("\tSEND: %d KB, %d kbps, %d seconds (%d retrans)" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000,  cli_sent['seconds'], cli_sent['retransmits']))</span><br><span style="color: hsl(120, 100%, 40%);">+    print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds']))</span><br><span style="color: hsl(120, 100%, 40%);">+    print("RESULT server:")</span><br><span style="color: hsl(120, 100%, 40%);">+    print("\tSEND: %d KB, %d kbps, %d seconds" % (cli_sent['bytes']/1000, cli_sent['bits_per_second']/1000, cli_sent['seconds']))</span><br><span style="color: hsl(120, 100%, 40%);">+    print("\tRECV: %d KB, %d kbps, %d seconds" % (cli_recv['bytes']/1000, cli_recv['bits_per_second']/1000, cli_recv['seconds']))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+hlr = suite.hlr()</span><br><span style="color: hsl(120, 100%, 40%);">+bts = suite.bts()</span><br><span style="color: hsl(120, 100%, 40%);">+pcu = bts.pcu()</span><br><span style="color: hsl(120, 100%, 40%);">+mgw_msc = suite.mgw()</span><br><span style="color: hsl(120, 100%, 40%);">+mgw_bsc = suite.mgw()</span><br><span style="color: hsl(120, 100%, 40%);">+stp = suite.stp()</span><br><span style="color: hsl(120, 100%, 40%);">+ggsn = suite.ggsn()</span><br><span style="color: hsl(120, 100%, 40%);">+sgsn = suite.sgsn(hlr, ggsn)</span><br><span style="color: hsl(120, 100%, 40%);">+msc = suite.msc(hlr, mgw_msc, stp)</span><br><span style="color: hsl(120, 100%, 40%);">+bsc = suite.bsc(msc, mgw_bsc, stp)</span><br><span style="color: hsl(120, 100%, 40%);">+ms = suite.modem()</span><br><span style="color: hsl(120, 100%, 40%);">+iperf3srv = suite.iperf3srv()</span><br><span style="color: hsl(120, 100%, 40%);">+iperf3cli = iperf3srv.create_client()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bsc.bts_add(bts)</span><br><span style="color: hsl(120, 100%, 40%);">+sgsn.bts_add(bts)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print('start iperfv3 server...')</span><br><span style="color: hsl(120, 100%, 40%);">+iperf3srv.start()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print('start network...')</span><br><span style="color: hsl(120, 100%, 40%);">+hlr.start()</span><br><span style="color: hsl(120, 100%, 40%);">+stp.start()</span><br><span style="color: hsl(120, 100%, 40%);">+ggsn.start()</span><br><span style="color: hsl(120, 100%, 40%);">+sgsn.start()</span><br><span style="color: hsl(120, 100%, 40%);">+msc.start()</span><br><span style="color: hsl(120, 100%, 40%);">+mgw_msc.start()</span><br><span style="color: hsl(120, 100%, 40%);">+mgw_bsc.start()</span><br><span style="color: hsl(120, 100%, 40%);">+bsc.start()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bts.start()</span><br><span style="color: hsl(120, 100%, 40%);">+wait(bsc.bts_is_connected, bts)</span><br><span style="color: hsl(120, 100%, 40%);">+print('Waiting for bts to be ready...')</span><br><span style="color: hsl(120, 100%, 40%);">+wait(bts.ready_for_pcu)</span><br><span style="color: hsl(120, 100%, 40%);">+pcu.start()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+hlr.subscriber_add(ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ms.connect(msc.mcc_mnc())</span><br><span style="color: hsl(120, 100%, 40%);">+ms.attach()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ms.log_info()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print('waiting for modems to attach...')</span><br><span style="color: hsl(120, 100%, 40%);">+wait(ms.is_connected, msc.mcc_mnc())</span><br><span style="color: hsl(120, 100%, 40%);">+wait(msc.subscriber_attached, ms)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print('waiting for modems to attach to data services...')</span><br><span style="color: hsl(120, 100%, 40%);">+wait(ms.is_attached)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# We need to use inet46 since ofono qmi only uses ipv4v6 eua (OS#2713)</span><br><span style="color: hsl(120, 100%, 40%);">+ctx_id_v4 = ms.activate_context(apn='inet46', protocol=ms.CTX_PROT_IPv4)</span><br><span style="color: hsl(120, 100%, 40%);">+print("Setting up data plan for %r" % repr(ctx_id_v4))</span><br><span style="color: hsl(120, 100%, 40%);">+ms.setup_context_data_plane(ctx_id_v4)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+print("Running iperf3 client to %s through %r" % (iperf3srv.addr(),repr(ctx_id_v4)))</span><br><span style="color: hsl(120, 100%, 40%);">+res = iperf3cli.run_test(ms.netns())</span><br><span style="color: hsl(120, 100%, 40%);">+iperf3srv.stop()</span><br><span style="color: hsl(120, 100%, 40%);">+print_results(res, iperf3srv.get_results())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ms.deactivate_context(ctx_id_v4)</span><br><span>diff --git a/suites/gprs/suite.conf b/suites/gprs/suite.conf</span><br><span>index 1590b7d..a24bc5d 100644</span><br><span>--- a/suites/gprs/suite.conf</span><br><span>+++ b/suites/gprs/suite.conf</span><br><span>@@ -1,6 +1,6 @@</span><br><span> resources:</span><br><span>   ip_address:</span><br><span style="color: hsl(0, 100%, 40%);">-  - times: 8 # msc, bsc, hlr, stp, mgw*2, sgsn, ggsn</span><br><span style="color: hsl(120, 100%, 40%);">+  - times: 9 # msc, bsc, hlr, stp, mgw*2, sgsn, ggsn, iperf3srv</span><br><span>   bts:</span><br><span>   - times: 1</span><br><span>   modem:</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/11476">change 11476</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/11476"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-tester </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I6ff6bef14feb535d98ca41b9788700d699e1ef1e </div>
<div style="display:none"> Gerrit-Change-Number: 11476 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Max <msuraev@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>