Change in osmo-gsm-tester[master]: Modems: Introduce Android UEs

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

srs_andre gerrit-no-reply at lists.osmocom.org
Tue Nov 17 16:47:06 UTC 2020


Hello Nils,

I'd like you to do a code review. Please visit

    https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21217

to review the following change.


Change subject: Modems: Introduce Android UEs
......................................................................

Modems: Introduce Android UEs

To expand the test capacities we would like to introduce
Android UEs as a new modem. Currently the following tests
are supported:
- Ping
- iPerf3 DL/UL
- RRC Mobile MT Ping

Change-Id: Iab37a0de59d6643d21bced34ddca06ff40fa62df
---
M src/osmo_gsm_tester/core/process.py
M src/osmo_gsm_tester/core/remote.py
M src/osmo_gsm_tester/core/schema.py
M src/osmo_gsm_tester/obj/enb_srs.py
M src/osmo_gsm_tester/obj/iperf3.py
M src/osmo_gsm_tester/obj/ms.py
A src/osmo_gsm_tester/obj/ms_android.py
M src/osmo_gsm_tester/obj/run_node.py
M sysmocom/default-suites.conf
A sysmocom/scenarios/androidue at .conf
M sysmocom/suites/4g/iperf3_dl.py
M sysmocom/suites/4g/iperf3_ul.py
A utils/bin/osmo-gsm-tester_androidue_conn_chk.sh
A utils/bin/osmo-gsm-tester_androidue_diag_parser.sh
14 files changed, 607 insertions(+), 12 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-gsm-tester refs/changes/17/21217/1

diff --git a/src/osmo_gsm_tester/core/process.py b/src/osmo_gsm_tester/core/process.py
index eaf8de7..5840881 100644
--- a/src/osmo_gsm_tester/core/process.py
+++ b/src/osmo_gsm_tester/core/process.py
@@ -381,12 +381,24 @@
 
 class RemoteProcess(Process):
 
-    def __init__(self, name, run_dir, remote_user, remote_host, remote_cwd, popen_args, remote_env={}, **popen_kwargs):
+    def __init__(self, name, run_dir, remote_user, remote_host, remote_cwd, popen_args,
+                 remote_env={}, remote_port=None, **popen_kwargs):
         super().__init__(name, run_dir, popen_args, **popen_kwargs)
         self.remote_user = remote_user
         self.remote_host = remote_host
         self.remote_cwd = remote_cwd
         self.remote_env = remote_env
+        self.remote_port = remote_port
+
+        # check if the command should be executed on an Android UE
+        is_ue_cmd = False
+        android_cmds = ['iperf3-cli', 'ping', 'set-airplane-mode', 'set-apn', 'get-ip',
+                        'rx-monitor', 'tx-monitor', 'diag-monitor', 'kill-diag-monitor', 'clear-diag_logs']
+        if self.remote_port:
+            for cmd in android_cmds:
+                if cmd in name:
+                    self.remote_user = 'root'
+                    is_ue_cmd = True
 
         # hacky: instead of just prepending ssh, i.e. piping stdout and stderr
         # over the ssh link, we should probably run on the remote side,
@@ -402,6 +414,12 @@
                            '%s %s %s' % (cd,
                                          ' '.join(['%s=%r'%(k,v) for k,v in self.remote_env.items()]),
                                          ' '.join(self.popen_args))]
+
+        # add arguments to directly connect to the Android UE
+        if is_ue_cmd:
+            self.popen_args.insert(1, '-p')  # add ssh parameters
+            self.popen_args.insert(2, str(self.remote_port))
+
         self.dbg(self.popen_args, dir=self.run_dir, conf=self.popen_kwargs, remote_env=self.remote_env)
 
     def RunError(self, msg_prefix):
@@ -443,14 +461,21 @@
         super().__init__(name, run_dir, remote_user, remote_host, remote_cwd, args, **popen_kwargs)
 
 def run_local_sync(run_dir, name, popen_args):
-    run_dir =run_dir.new_dir(name)
+    run_dir = run_dir.new_dir(name)
     proc = Process(name, run_dir, popen_args)
     proc.launch_sync()
     return proc
 
+def run_local(run_dir, name, popen_args):
+    run_dir = run_dir.new_dir(name)
+    proc = Process(name, run_dir, popen_args)
+    proc.launch()
+    return proc
+
 def run_local_netns_sync(run_dir, name, netns, popen_args):
-    run_dir =run_dir.new_dir(name)
+    run_dir = run_dir.new_dir(name)
     proc = NetNSProcess(name, run_dir, netns, popen_args)
     proc.launch_sync()
     return proc
+
 # vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/core/remote.py b/src/osmo_gsm_tester/core/remote.py
index 8deb25d..ef77c2cd 100644
--- a/src/osmo_gsm_tester/core/remote.py
+++ b/src/osmo_gsm_tester/core/remote.py
@@ -28,13 +28,14 @@
 
     WRAPPER_SCRIPT = 'ssh_sigkiller.sh'
 
-    def __init__(self, run_dir, remote_user = 'root', remote_host = 'localhost', remote_cwd=None):
+    def __init__(self, run_dir, remote_user = 'root', remote_host = 'localhost', remote_cwd=None, remote_port=None):
         super().__init__(log.C_RUN, 'host-' + remote_user + '@' + remote_host)
         self.run_dir = util.Dir(run_dir.new_dir(self.name()))
         self.remote_user = remote_user
         self.remote_host = remote_host
         self.remote_cwd = remote_cwd
         self.remote_env = {}
+        self.remote_port = remote_port
 
     def user(self):
         return self.remote_user
@@ -51,9 +52,13 @@
     def get_remote_env(self):
         return self.remote_env
 
+    def ssh_port(self):
+        return self.remote_port
+
     def RemoteProcess(self, name, popen_args, remote_env={}, **popen_kwargs):
         run_dir = self.run_dir.new_dir(name)
-        return process.RemoteProcess(name, run_dir, self.user(), self.host(), self.cwd(), popen_args, remote_env=remote_env, **popen_kwargs)
+        return process.RemoteProcess(name, run_dir, self.user(), self.host(), self.cwd(), popen_args,
+                                     remote_env=remote_env, remote_port=self.ssh_port(), **popen_kwargs)
 
     def generate_wrapper_script(self, wait_time_sec):
         wrapper_script = self.run_dir.new_file(RemoteHost.WRAPPER_SCRIPT)
diff --git a/src/osmo_gsm_tester/core/schema.py b/src/osmo_gsm_tester/core/schema.py
index d56d6ec..6496e57 100644
--- a/src/osmo_gsm_tester/core/schema.py
+++ b/src/osmo_gsm_tester/core/schema.py
@@ -111,7 +111,7 @@
     raise ValueError('Unknown Cipher value: %r' % val)
 
 def modem_feature(val):
-    if val in ('sms', 'gprs', 'voice', 'ussd', 'sim', '2g', '3g', '4g', 'dl_qam256', 'ul_qam64'):
+    if val in ('sms', 'gprs', 'voice', 'ussd', 'sim', '2g', '3g', '4g', 'dl_qam256', 'ul_qam64', 'qc_diag'):
         return True
     raise ValueError('Unknown Modem Feature: %r' % val)
 
diff --git a/src/osmo_gsm_tester/obj/enb_srs.py b/src/osmo_gsm_tester/obj/enb_srs.py
index 53beda8..307368f 100644
--- a/src/osmo_gsm_tester/obj/enb_srs.py
+++ b/src/osmo_gsm_tester/obj/enb_srs.py
@@ -114,6 +114,7 @@
             self.start_remotely()
 
         # send t+Enter to enable console trace
+        MainLoop.sleep(3)
         self.dbg('Enabling console trace')
         self.process.stdin_write('t\n')
 
diff --git a/src/osmo_gsm_tester/obj/iperf3.py b/src/osmo_gsm_tester/obj/iperf3.py
index 4785f78..f0309d2 100644
--- a/src/osmo_gsm_tester/obj/iperf3.py
+++ b/src/osmo_gsm_tester/obj/iperf3.py
@@ -232,7 +232,7 @@
         locally = not self._run_node or self._run_node.is_local()
         return locally
 
-    def prepare_test_proc(self, dir=None, netns=None, time_sec=None, proto=None, bitrate=0, tos=None):
+    def prepare_test_proc(self, dir=None, netns=None, time_sec=None, proto=None, bitrate=0, tos=None, ue=None):
         values = config.get_defaults('iperf3cli')
         config.overlay(values, self.testenv.suite().config().get('iperf3cli', {}))
 
@@ -264,6 +264,12 @@
         popen_args = ('iperf3', '-c',  self.server.addr(),
                       '-p', str(self.server.port()), '-J',
                       '-t', str(time_sec))
+
+        # In case the modem is an Android UE we have to extend the iPerf3 command for local execution
+        if self._run_node.is_local() and ue.ue_serial:
+            adb_cmd_pref = ['adb', '-s', ue.ue_serial, 'exec-out', 'su', '-c']
+            popen_args = adb_cmd_pref + list(popen_args)
+
         if dir == IPerf3Client.DIR_DL:
             popen_args += ('-R',)
         elif dir == IPerf3Client.DIR_BI:
@@ -281,7 +287,8 @@
         return proc
 
     def prepare_test_proc_remotely(self, netns, popen_args):
-        self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
+        self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr(),
+                                          None, self._run_node.ssh_port())
 
         remote_prefix_dir = util.Dir(IPerf3Client.REMOTE_DIR)
         remote_run_dir = util.Dir(remote_prefix_dir.child('cli-' + str(self)))
diff --git a/src/osmo_gsm_tester/obj/ms.py b/src/osmo_gsm_tester/obj/ms.py
index 70ce558..bfd3f3b 100644
--- a/src/osmo_gsm_tester/obj/ms.py
+++ b/src/osmo_gsm_tester/obj/ms.py
@@ -72,6 +72,9 @@
         elif ms_type == 'srsue':
             from .ms_srs import srsUE
             ms_class = srsUE
+        elif ms_type == 'androidue':
+            from .ms_android import AndroidUE
+            ms_class = AndroidUE
         elif ms_type == 'amarisoftue':
             from .ms_amarisoft import AmarisoftUE
             ms_class = AmarisoftUE
diff --git a/src/osmo_gsm_tester/obj/ms_android.py b/src/osmo_gsm_tester/obj/ms_android.py
new file mode 100644
index 0000000..0d9ed16
--- /dev/null
+++ b/src/osmo_gsm_tester/obj/ms_android.py
@@ -0,0 +1,526 @@
+# Author: Nils Fürste <nils.fuerste at softwareradiosystems.com>
+# Author: Bedran Karakoc <bedran.karakoc at softwareradiosystems.com>
+
+import pprint
+import time
+import re
+
+from ..core import log, util, config, remote, process
+from ..core import schema
+from .run_node import RunNode
+from .ms import MS
+from .srslte_common import srslte_common
+from .ms_srs import srsUEMetrics
+
+
+def on_register_schemas():
+    resource_schema = {
+        'additional_args[]': schema.STR,
+        'apn': schema.STR,
+        'apn_name': schema.STR,
+        'apn_mcc': schema.STR,
+        'apn_mnc': schema.STR,
+        'sel_apn': schema.STR,
+        'ue_serial': schema.STR,
+        'enable_pcap': schema.BOOL_STR,
+        }
+    for key, val in RunNode.schema().items():
+        resource_schema['run_node.%s' % key] = val
+    schema.register_resource_schema('modem', resource_schema)
+
+    config_schema = {
+        'enable_pcap': schema.BOOL_STR,
+        'log_all_level': schema.STR,
+        }
+    schema.register_config_schema('modem', config_schema)
+
+
+class AndroidUE(MS, srslte_common):
+
+    REMOTEDIR = '/osmo-gsm-tester-androidue'
+    METRICSFILE = 'android_ue_metrics.csv'
+    PCAPFILE = 'android_ue.pcap'
+
+    def __init__(self, testenv, conf):
+        self._run_node = RunNode.from_conf(conf.get('run_node', {}))
+        super().__init__('androidue_%s' % self.addr(), testenv, conf)
+        srslte_common.__init__(self)
+        self.run_dir = None
+        self.rem_host = None
+        self.testenv = testenv
+        self.emm_connected = False
+        self.rrc_connected = False
+        self.connect_timeout = 300  # sec
+        self.ue_serial = None  # stores the UE serial if available
+        self.remote_port = None
+        self.conn_reset_intvl = 20
+        self.remote_pcap_file = None
+        self.pcap_file = None
+        self.ue_data_intf = None
+        self.remote_metrics_file = None
+        self.metrics_file = None
+        self.remote_run_dir = None
+        self.have_metrics_file = False
+        self.rx_monitor_proc = None
+        self.tx_monitor_proc = None
+        self.diag_monitor_proc = None
+        self.diag_parser_proc = None
+        self.enable_pcap = None
+
+    def cleanup(self):
+        if self.process is None:
+            return
+        if self._run_node.is_local():
+            return
+
+        # stop monitoring
+        self.stop()
+
+    def scp_back_pcap(self):
+        if self.enable_pcap:
+            DIAG_PARSER = 'osmo-gsm-tester_androidue_diag_parser.sh'
+            if not self._run_node.is_local():
+                popen_args_diag_parser = [DIAG_PARSER, str(self.ue_serial), str(self.remote_run_dir), str(self.remote_pcap_file)]
+                diag_parser_proc = self.rem_host.RemoteProcess("diag-parser", popen_args_diag_parser, env={})
+                diag_parser_proc.launch()
+            else:
+                popen_args_diag_parser = [DIAG_PARSER, str(self.ue_serial), str(self.run_dir), str(self.pcap_file)]
+                diag_parser_proc = process.run_local(self.run_dir, "diag-parser", popen_args_diag_parser)
+
+            timeout_ = 300  # timeout in s
+            while timeout_ > 0:
+                diag_parser_stdout = diag_parser_proc.get_stdout()
+                if diag_parser_stdout.count('Pulling new .qmdl file...') > 1:
+                    # If the parsers pulls the .qmdl file for the second time we know that
+                    # the parsing of the first one is done
+                    break
+                time.sleep(2)
+                timeout_ -= 2
+
+            if timeout_ <= 0:
+                raise log.Error("Writing PCAP failed")
+
+            if not self._run_node.is_local():
+                try:
+                    self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file)
+                except Exception as e:
+                    self.log(repr(e))
+            else:
+                pass
+
+    def stop(self):
+        # terminate bitrate monitors and save metrics
+        brate_rx_raw = self.rx_monitor_proc.get_stdout().split('\n')
+        brate_tx_raw = self.tx_monitor_proc.get_stdout().split('\n')
+        brate_rx_raw.remove('')
+        self.rx_monitor_proc.terminate()
+        brate_tx_raw.remove('')
+        self.tx_monitor_proc.terminate()
+        self.save_metrics(brate_rx_raw[1:], brate_tx_raw[1:])
+
+        # kill diag monitor if started
+        if self.diag_monitor_proc:
+            kill_diag_proc = self.rem_host.RemoteProcess("kill-diag-monitor", ['/vendor/bin/diag_mdlog', '-k'], env={})
+            kill_diag_proc.launch_sync()
+            time.sleep(3)
+            if self.diag_monitor_proc.is_running():
+                self.diag_monitor_proc.terminate()
+            self.diag_parser_proc.terminate()
+
+        # enable airplane mode
+        self.set_airplane_mode(True)
+
+        # scp back pcap file
+        self.scp_back_pcap()
+
+    def start(self):
+        # check if device is available over ADB
+        if not self.check_device_availability():
+            self.log("Can't find requested device. Restarting ADB and check again")
+            self.restart_adb_inst(True)
+            if not self.check_device_availability():
+                raise log.Error("Device with serial %s is not available" % self.ue_serial)
+
+        # enable airplane mode
+        self.set_airplane_mode(True)
+
+        # clear working directories
+        self.clear_work_dirs()
+
+    def clear_work_dirs(self):
+        # clear remote_run_dir
+        popen_args_clear_run_dir = ['sudo', 'rm', '-rf', '-v', str(self.remote_run_dir) + '/*', '||', 'true']
+        clear_run_dir_proc = self.run_netns_wait('clear-run_dir', popen_args_clear_run_dir)
+        self.dbg("Deleted the following files: %s" % clear_run_dir_proc.get_stdout())
+
+        # clear diag_logs
+        popen_args_clear_diag_logs = ['rm', '-r', '/data/local/tmp/diag_logs/*', '||', 'true']
+        clear_diag_logs_proc = self.run_netns_wait('clear-diag_logs', popen_args_clear_diag_logs)
+        self.dbg("Deleted the following files: %s" % clear_diag_logs_proc.get_stdout())
+
+    def check_device_availability(self):
+        serials_cmd = ['adb', 'devices']
+        proc = self.run_netns_wait("devices", serials_cmd)
+        return self.ue_serial in proc.get_stdout()
+
+    def restart_adb_inst(self, as_root=False):
+        self.run_netns_wait("kill-adb",  ['sudo', 'adb', 'kill-server'])
+        if as_root:
+            self.run_netns_wait("start-adb", ['adb', 'start-server'])
+        else:
+            self.run_netns_wait("start-adb", ['sudo', 'adb', 'start-server'])
+        return
+
+    def connect(self, enb):
+        self.log('Starting AndroidUE')
+        self.configure()
+        CONN_CHK = 'osmo-gsm-tester_androidue_conn_chk.sh'
+        popen_args_emm_conn_chk = [CONN_CHK, self.ue_serial]
+
+        if not self._run_node.is_local():
+            emm_conn_chk_proc = self.rem_host.RemoteProcess('emm-conn-chk', popen_args_emm_conn_chk, env={})
+            emm_conn_chk_proc.launch()
+        else:
+            emm_conn_chk_proc = process.run_local(self.run_dir, 'emm-conn-chk', popen_args_emm_conn_chk)
+
+        # check connection status
+        timer = self.connect_timeout
+        while timer > 0:
+            if 'LTE' in emm_conn_chk_proc.get_stdout():
+                if self.get_assigned_addr():
+                    self.emm_connected = True
+                    self.rrc_connected = True
+                    emm_conn_chk_proc.terminate()
+                    break
+
+            # reset connection
+            if timer % self.conn_reset_intvl == 0:
+                self.set_airplane_mode(True)
+                time.sleep(2)
+                timer -= 2
+                self.set_airplane_mode(False)
+            time.sleep(1)
+            timer -= 1
+
+        if timer == 0:
+            raise log.Error("Android UE %s connection timer expired" % self.ue_serial)
+
+        # start bit rate monitoring on Android UE
+        popen_args_rx = ['while', 'true;', 'do',
+                         'echo', '`cat', '/sys/class/net/' + self.ue_data_intf + '/statistics/rx_bytes`;',
+                         'sleep', '1;', 'done']
+        popen_args_tx = ['while', 'true;', 'do',
+                         'echo', '`cat', '/sys/class/net/' + self.ue_data_intf + '/statistics/tx_bytes`;',
+                         'sleep', '1;', 'done']
+
+        if not self._run_node.is_local():
+            self.rx_monitor_proc = self.rem_host.RemoteProcess("rx-monitor", popen_args_rx, env={})
+            self.rx_monitor_proc.launch()
+            self.tx_monitor_proc = self.rem_host.RemoteProcess("tx-monitor", popen_args_tx, env={})
+            self.tx_monitor_proc.launch()
+        else:
+            self.rx_monitor_proc = process.run_local(self.run_dir, "rx-monitor", popen_args_rx)
+            self.tx_monitor_proc = process.run_local(self.run_dir, "tx-monitor", popen_args_tx)
+
+        # start diag_mdlog and parsing
+        if 'qc_diag' in self.features():
+            DIAG_PARSER = 'osmo-gsm-tester_androidue_diag_parser.sh'
+            self.pcap_file = self.run_dir.child(AndroidUE.PCAPFILE)
+            self.remote_pcap_file = self.remote_run_dir.child(AndroidUE.PCAPFILE)
+
+            popen_args_diag = ['/vendor/bin/diag_mdlog', '-s', '90000', '-f', '/data/local/tmp/ogt_diag.cfg',
+                               '-o', '/data/local/tmp/diag_logs']
+
+            if not self._run_node.is_local():
+                self.diag_monitor_proc = self.rem_host.RemoteProcess("diag-monitor", popen_args_diag, env={})
+                self.diag_monitor_proc.launch()
+
+                popen_args_diag_parser = [DIAG_PARSER, str(self.ue_serial), str(self.remote_run_dir), str(self.remote_pcap_file)]
+                self.diag_parser_proc = self.rem_host.RemoteProcess("diag-parser", popen_args_diag_parser, env={})
+                self.diag_parser_proc.launch()
+            else:
+                self.diag_monitor_proc = process.run_local(self.run_dir, "diag-monitor", popen_args_diag)
+                self.diag_monitor_proc.launch()
+
+                popen_args_diag_parser = [DIAG_PARSER, str(self.ue_serial), str(self.run_dir), str(self.pcap_file)]
+                self.diag_parser_proc = process.run_local(self.run_dir, "diag-parser", popen_args_diag_parser)
+
+    def configure(self):
+        values = dict(ue=config.get_defaults('androidue'))
+        config.overlay(values, dict(ue=self.testenv.suite().config().get('modem', {})))
+        config.overlay(values, dict(ue=self._conf))
+        self.dbg('AndroidUE CONFIG:\n' + pprint.pformat(values))
+
+        self.remote_port = values['ue']['run_node']['remote_port']
+        self.ue_serial = values['ue']['ue_serial']
+
+        if 'qc_diag' in self.features():
+            self.enable_pcap = util.str2bool(values['ue'].get('enable_pcap', 'false'))
+
+        self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
+        self.metrics_file = self.run_dir.child(AndroidUE.METRICSFILE)
+        if not self._run_node.is_local():
+            self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(),
+                                              self._run_node.ssh_addr(), None, remote_port=self.remote_port)
+            self.remote_run_dir = util.Dir(AndroidUE.REMOTEDIR)
+            self.remote_metrics_file = self.remote_run_dir.child(AndroidUE.METRICSFILE)
+
+        # set APN
+        apn_params = {
+            "carrier": str(values['ue']['apn_name'] or 'default'),
+            "apn": str(values['ue']['apn']),  # mandatory
+            "proxy": str(''),
+            "port": str(''),
+            "user": str(''),
+            "password": str(''),
+            "server": str(''),
+            "mmsc": str(''),
+            "mmsport": str(''),
+            "mmsproxy": str(''),
+            "mcc": str(values['ue']['apn_mcc']),  # mandatory
+            "mnc": str(values['ue']['apn_mnc']),  # mandatory
+            "auth": str('-1'),
+            "type": str(''),
+            "protocol": str(''),
+            "mvnotype": str(''),
+            "mvnoval": str(''),
+            "groupid": str('-1')
+        }
+
+        # make sure UE is not in airplane mode
+        self.set_airplane_mode(False)
+        time.sleep(1)
+
+        self.dbg("Set APN parameters: " + str(apn_params))
+        # self.set_apn(apn_params, True)
+
+        # move back in airplane mode
+        time.sleep(1)
+        self.set_airplane_mode(False)
+
+        # clear working directories
+        self.clear_work_dirs()
+
+    def is_rrc_connected(self):
+        if not ('qc_diag' in self.features()):
+            raise log.Error("Checking RRC state not supported (missing qc_diag feature?)")
+
+        if not self.diag_monitor_proc:
+            raise log.Error("Diag monitor crashed or not started")
+
+        diag_mon_stdout_l = self.diag_monitor_proc.get_stdout().split("\n")
+        rrc_connected = self.rrc_connected
+        for line in diag_mon_stdout_l:
+            if "rrc_state=RRC_IDLE_CAMPED" in line:
+                rrc_connected = False
+            # elif "LTE_RRC_STATE_CHANGE: RRC_IDLE_NOT_CAMPED" in line:  # UE lost connection, throw exception
+            #    raise log.Error("Android UE lost connection to the eNB")
+            elif "rrc_state=" in line:
+                rrc_connected = True
+        return rrc_connected
+
+    def is_registered(self, mcc_mnc=None):
+        return self.emm_connected
+
+    def get_counter(self, counter_name):
+        self.stop()
+        if counter_name == 'prach_sent':
+            # not implemented so far, return 2 to pass tests
+            return 2
+        elif counter_name == 'paging_received':
+            diag_parser_stdout_l = self.diag_parser_proc.get_stdout().split("\n")
+            return diag_parser_stdout_l.count('Paging received')
+        else:
+            raise log.Error("Counter %s not implemented" % counter_name)
+
+    def get_assigned_addr(self, ipv6=False):
+        ip_prefix = "172.16.0"
+        proc = self.run_netns_wait("get-ip", ['ip', 'addr', 'show'])
+        out_l = proc.get_stdout().split('\n')
+        ip = None
+        for line in out_l:
+            if ip_prefix in line:
+                ip = line.split(' ')[5][:-3]  # UE's IP
+                self.ue_data_intf = line.split(' ')[-1]  # UE's data interface
+        return ip
+
+    def running(self):
+        # check if device is available over ADB
+        if not (self.ue_serial is None):
+            serials_cmd = ['adb', 'devices']
+            proc = self.run_netns_wait("devices", serials_cmd)
+            if self.ue_serial in proc.get_stdout():
+                return True
+        self.dbg("Device with serial %s is currently not available" % self.ue_serial)
+        return False
+
+    def addr(self):
+        return self._run_node.run_addr()
+
+    def run_node(self):
+        return self._run_node
+
+    def features(self):
+        return self._conf.get('features', [])
+
+    def run_netns_wait(self, name, popen_args):
+        if self._run_node.is_local():
+            # use adb locally instead of ssh
+            adb_cmd_pref = ['adb', '-s', self.ue_serial, 'exec-out', 'su', '-c']
+            popen_args = adb_cmd_pref + list(popen_args)
+            proc = process.run_local_sync(self.run_dir, name, popen_args)
+        else:
+            proc = self.rem_host.RemoteProcess(name, popen_args, env={})
+        proc.launch_sync()
+        return proc
+
+    def verify_metric(self, value, operation='avg', metric='dl_brate', criterion='gt', window=1):
+        self.stop()
+        if self.have_metrics_file:
+            metrics = srsUEMetrics(self.metrics_file)
+            return metrics.verify(value, operation, metric, criterion, window)
+        else:
+            raise log.Error("Metrics file not available")
+
+    def netns(self):
+        return None  # no netns needed
+
+    def set_airplane_mode(self, apm_state):
+        self.log("Setting airplane mode: " + str(apm_state))
+        popen_args = ['settings', 'put', 'global', 'airplane_mode_on', str(int(apm_state)),
+                      '&&', 'su', '-c', '\"', 'am', 'broadcast', '-a', 'android.intent.action.AIRPLANE_MODE', '\"']
+        self.run_netns_wait("set-airplane-mode", popen_args)
+
+    def get_carrier_id(self, carrier_name):
+        qry_carrier_cmd = "content query --uri \"content://telephony/carriers\" "
+
+        proc = self.run_netns_wait("set-apn", [qry_carrier_cmd])
+        available_carriers = proc.get_stdout().split("\n")
+        carr_id = -1
+        for carr in available_carriers:
+            if 'name=' + carrier_name in carr:  # found carrier
+                carr_id = re.findall(r'_id=(\S+),', carr)[0]
+                break
+
+        return carr_id
+
+    def set_new_carrier(self, apn_parameter, carr_id):
+        # check if carrier was found, delete it if exists
+        if carr_id != -1:
+            self.delete_apn(apn_parameter["carrier"])
+
+        # build command to add/update carrier
+        set_carrier_cmd = "content insert --uri content://telephony/carriers" \
+                          + " --bind name:s:\"" + apn_parameter["carrier"] + "\"" \
+                          + " --bind numeric:s:\"" + apn_parameter["mcc"] + apn_parameter["mnc"] + "\"" \
+                          + " --bind mcc:s:\"" + apn_parameter["mcc"] + "\"" \
+                          + " --bind mnc:s:\"" + apn_parameter["mnc"] + "\""\
+                          + " --bind apn:s:\"" + apn_parameter["apn"] + "\"" \
+                          + " --bind user:s:\"" + apn_parameter["user"] + "\"" \
+                          + " --bind password:s:\"" + apn_parameter["password"] + "\"" \
+                          + " --bind mmsc:s:\"" + apn_parameter["mmsc"] + "\"" \
+                          + " --bind mmsport:s:\"" + apn_parameter["mmsport"] + "\"" \
+                          + " --bind mmsproxy:s:\"" + apn_parameter["mmsproxy"] + "\"" \
+                          + " --bind authtype:s:\"" + apn_parameter["auth"] + "\"" \
+                          + " --bind type:s:\"" + apn_parameter["type"] + "\"" \
+                          + " --bind protocol:s:\"" + apn_parameter["protocol"] + "\"" \
+                          + " --bind mvno_type:s:\"" + apn_parameter["mvnotype"] + "\"" \
+                          + " --bind mvno_match_data:s:\"" + apn_parameter["mvnoval"] + "\"" \
+                          + " --bind sub_id:s:\"" + apn_parameter["groupid"] + "\""
+
+        self.run_netns_wait("set-apn", [set_carrier_cmd])
+
+        # return carrier ID
+        return self.get_carrier_id(apn_parameter["carrier"])
+
+    def set_preferred_apn(self, carr_id):
+        if carr_id != -1:
+            set_apn_cmd = "content insert --uri content://telephony/carriers/preferapn --bind apn_id:s:\"" + str(carr_id) + "\""
+            self.run_netns_wait("set-apn", [set_apn_cmd])
+
+    def set_apn(self, apn_param, sel_apn):
+        # search for carrier in APN database
+        carrier_id = self.get_carrier_id(apn_param["carrier"])
+
+        # add/update carrier
+        carrier_id = self.set_new_carrier(apn_param, carrier_id)
+
+        # select as preferred APN
+        if sel_apn:
+            self.set_preferred_apn(carrier_id)
+
+    def select_apn(self, carr_name):
+        # search for carrier_id
+        carr_id = self.get_carrier_id(carr_name)
+        if carr_id == 0:
+            return False
+
+        # select carrier by id
+        sel_apn_cmd = "content update --uri content://telephony/carriers/preferapn --bind apn_id:s:\"" + str(carr_id) + "\""
+        self.run_netns_wait("set-apn", [sel_apn_cmd])
+        return True
+
+    def delete_apn(self, carr_name):
+        set_apn_cmd = "content delete --uri content://telephony/carriers --where \'name=\"" + str(carr_name) + "\" \'"
+        self.run_netns_wait("set-apn", [set_apn_cmd])
+
+    def is_emm_connected(self):
+        time.sleep(2)
+        proc = self.run_netns_wait("emm-conn-chk", ['getprop', '\"gsm.network.type\"'])
+        return 'LTE' in proc.get_stdout()
+
+    def save_metrics(self, brate_rx_l, brate_tx_l):
+        if len(brate_rx_l) < 2 or len(brate_tx_l) < 2:
+            raise log.Error("Insufficient data available to write metrics file")
+
+        # cut of elements if lists don't have the same length
+        if len(brate_rx_l) > len(brate_tx_l):
+            brate_rx_l = brate_rx_l[:len(brate_tx_l) - len(brate_rx_l)]
+
+        if len(brate_rx_l) < len(brate_tx_l):
+            brate_tx_l = brate_tx_l[:len(brate_rx_l) - len(brate_tx_l)]
+
+        # get start value
+        brate_rx_last = int(brate_rx_l[0])
+        brate_tx_last = int(brate_tx_l[0])
+
+        with open(self.metrics_file, "w") as ue_metrics_fh:
+            ue_metrics_fh.write("time;cc;earfcn;pci;rsrp;pl;cfo;pci_neigh;rsrp_neigh;cfo_neigh;"
+                                + "dl_mcs;dl_snr;dl_turbo;dl_brate;dl_bler;"
+                                + "ul_ta;ul_mcs;ul_buff;ul_brate;ul_bler;rf_o;rf_u;rf_l;"
+                                + "is_attached\n")
+            for i in range(1, len(brate_rx_l)):
+                time = "0"
+                cc = "0"
+                earfcn = "0"
+                pci = "0"
+                rsrp = "0"
+                pl = "0"
+                cfo = "0"
+                pci_neigh = "0"
+                rsrp_neigh = "0"
+                cfo_neigh = "0"
+                dl_mcs = "0"
+                dl_snr = "0"
+                dl_turbo = "0"
+                dl_brate = str((int(brate_rx_l[i]) - brate_rx_last) * 8)
+                brate_rx_last = int(brate_rx_l[i])
+                dl_bler = "0"
+                ul_ta = "0"
+                ul_mcs = "0"
+                ul_buff = "0"
+                ul_brate = str((int(brate_tx_l[i]) - brate_tx_last) * 8)
+                brate_tx_last = int(brate_tx_l[i])
+                ul_bler = "0"
+                rf_o = "0"
+                rf_u = "0"
+                rf_l = "0"
+                is_attached = "0"
+
+                line = time + ";" + cc + ";" + earfcn + ";" + pci + ";" + rsrp + ";" + pl + ";" + cfo + ";" \
+                       + pci_neigh + ";" + rsrp_neigh + ";" + cfo_neigh + ";" + dl_mcs + ";" + dl_snr + ";" \
+                       + dl_turbo + ";" + dl_brate + ";" + dl_bler + ";" + ul_ta + ";" + ul_mcs + ";" + ul_buff + ";" \
+                       + ul_brate + ";" + ul_bler + ";" + rf_o + ";" + rf_u + ";" + rf_l + ";" + is_attached
+                ue_metrics_fh.write(line + "\n")
+
+        self.have_metrics_file = True
diff --git a/src/osmo_gsm_tester/obj/run_node.py b/src/osmo_gsm_tester/obj/run_node.py
index 6a030ac..c907944 100644
--- a/src/osmo_gsm_tester/obj/run_node.py
+++ b/src/osmo_gsm_tester/obj/run_node.py
@@ -30,13 +30,14 @@
     T_LOCAL = 'local'
     T_REM_SSH = 'ssh'
 
-    def __init__(self, type=None, run_addr=None, ssh_user=None, ssh_addr=None, run_label=None):
+    def __init__(self, type=None, run_addr=None, ssh_user=None, ssh_addr=None, run_label=None, remote_port=None):
         super().__init__(log.C_RUN, 'runnode')
         self._type = type
         self._run_addr = run_addr
         self._ssh_user = ssh_user
         self._ssh_addr = ssh_addr
         self._run_label = run_label
+        self._remote_port = remote_port
         if not self._type:
             raise log.Error('run_type not set')
         if not self._run_addr:
@@ -55,7 +56,7 @@
     def from_conf(cls, conf):
         return cls(conf.get('run_type', None), conf.get('run_addr', None),
                    conf.get('ssh_user', None), conf.get('ssh_addr', None),
-                   conf.get('run_label', None))
+                   conf.get('run_label', None), conf.get('remote_port', None))
 
     @classmethod
     def schema(cls):
@@ -65,6 +66,7 @@
             'ssh_user': schema.STR,
             'ssh_addr': schema.IPV4,
             'run_label': schema.STR,
+            'remote_port': schema.STR,
             }
         return resource_schema
 
@@ -89,4 +91,7 @@
     def run_label(self):
         return self._run_label
 
+    def ssh_port(self):
+        return self._remote_port
+
 # vim: expandtab tabstop=4 shiftwidth=4
diff --git a/sysmocom/default-suites.conf b/sysmocom/default-suites.conf
index b32d7b1..4c196b4 100644
--- a/sysmocom/default-suites.conf
+++ b/sysmocom/default-suites.conf
@@ -172,3 +172,4 @@
 - encryption
 
 - 4g:srsenb-rftype at zmq+srsue-rftype@zmq
+- 4g:androidue at mi5g+srsenb-rftype@uhd+mod-enb-nprb at 25+mod-enb-txmode@1
diff --git a/sysmocom/scenarios/androidue at .conf b/sysmocom/scenarios/androidue at .conf
new file mode 100644
index 0000000..a129c0e
--- /dev/null
+++ b/sysmocom/scenarios/androidue at .conf
@@ -0,0 +1,3 @@
+resources:
+  modem:
+  - label: ${param1}
diff --git a/sysmocom/suites/4g/iperf3_dl.py b/sysmocom/suites/4g/iperf3_dl.py
index bf5b1f0..ed137bd 100755
--- a/sysmocom/suites/4g/iperf3_dl.py
+++ b/sysmocom/suites/4g/iperf3_dl.py
@@ -23,7 +23,7 @@
 max_rate = enb.ue_max_rate(downlink=True, num_carriers=ue.num_carriers)
 
 iperf3srv.start()
-proc = iperf3cli.prepare_test_proc(iperf3cli.DIR_DL, ue.netns(), bitrate=max_rate)
+proc = iperf3cli.prepare_test_proc(iperf3cli.DIR_DL, ue.netns(), bitrate=max_rate, ue=ue)
 
 print('waiting for UE to attach...')
 wait(ue.is_registered)
diff --git a/sysmocom/suites/4g/iperf3_ul.py b/sysmocom/suites/4g/iperf3_ul.py
index 6c0d25d..6cc9f21 100755
--- a/sysmocom/suites/4g/iperf3_ul.py
+++ b/sysmocom/suites/4g/iperf3_ul.py
@@ -23,7 +23,7 @@
 max_rate = enb.ue_max_rate(downlink=False, num_carriers=ue.num_carriers)
 
 iperf3srv.start()
-proc = iperf3cli.prepare_test_proc(iperf3cli.DIR_UL, ue.netns(), bitrate=max_rate)
+proc = iperf3cli.prepare_test_proc(iperf3cli.DIR_UL, ue.netns(), bitrate=max_rate, ue=ue)
 
 print('waiting for UE to attach...')
 wait(ue.is_registered)
diff --git a/utils/bin/osmo-gsm-tester_androidue_conn_chk.sh b/utils/bin/osmo-gsm-tester_androidue_conn_chk.sh
new file mode 100644
index 0000000..8e126cd
--- /dev/null
+++ b/utils/bin/osmo-gsm-tester_androidue_conn_chk.sh
@@ -0,0 +1,9 @@
+# osmo-gsm-tester_androidue_conn_chk.sh $serial
+while true; do
+    sudo adb -s "$1" shell getprop "gsm.network.type";
+    sleep 1;
+done
+
+
+
+
diff --git a/utils/bin/osmo-gsm-tester_androidue_diag_parser.sh b/utils/bin/osmo-gsm-tester_androidue_diag_parser.sh
new file mode 100644
index 0000000..00a2f81
--- /dev/null
+++ b/utils/bin/osmo-gsm-tester_androidue_diag_parser.sh
@@ -0,0 +1,10 @@
+# format ./osmo-gsm-tester_androidue_diag_parser.sh $serial $run_dir $pcap_path
+while true; do
+    echo "Pulling new qmdl file...";
+    sudo adb -s "$1" pull /data/local/tmp/diag_logs "$2" > /dev/null;
+    wait $!;
+    QMDL_FN=$(find "$2" -maxdepth 2 -type f -name "*.qmdl");
+    wait $!;
+    sudo scat -t qc --event -d "$QMDL_FN" -F "$3";
+    wait $!;
+done
\ No newline at end of file

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21217
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-gsm-tester
Gerrit-Branch: master
Gerrit-Change-Id: Iab37a0de59d6643d21bced34ddca06ff40fa62df
Gerrit-Change-Number: 21217
Gerrit-PatchSet: 1
Gerrit-Owner: srs_andre <andre at softwareradiosystems.com>
Gerrit-Reviewer: Nils <nils.fuerste at softwareradiosystems.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201117/295edab8/attachment.htm>


More information about the gerrit-log mailing list