Change in osmo-gsm-tester[master]: srsue: Introduce metrics verification procedures

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/.

pespin gerrit-no-reply at lists.osmocom.org
Thu Mar 5 15:55:05 UTC 2020


pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-gsm-tester/+/17323 )

Change subject: srsue: Introduce metrics verification procedures
......................................................................

srsue: Introduce metrics verification procedures

Change-Id: Ib1da58615cdc4f53ac1a27080e94e5b47760c508
---
M check_dependencies.py
M src/osmo_gsm_tester/srs_enb.py
M src/osmo_gsm_tester/srs_ue.py
M suites/4g/iperf3.py
M suites/4g/suite.conf
5 files changed, 122 insertions(+), 7 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/check_dependencies.py b/check_dependencies.py
index 28bfdf7..c3b1d64 100755
--- a/check_dependencies.py
+++ b/check_dependencies.py
@@ -28,5 +28,6 @@
 import smpplib
 import urllib.request
 import xml.etree.ElementTree
+import numpy
 
 print('dependencies ok')
diff --git a/src/osmo_gsm_tester/srs_enb.py b/src/osmo_gsm_tester/srs_enb.py
index 1cfd212..1fb2db1 100644
--- a/src/osmo_gsm_tester/srs_enb.py
+++ b/src/osmo_gsm_tester/srs_enb.py
@@ -76,6 +76,7 @@
         self.remote_config_drb_file = None
         self.remote_log_file = None
         self._num_prb = 0
+        self._txmode = 0
         self.suite_run = suite_run
         self.remote_user = conf.get('remote_user', None)
         if not rf_type_valid(conf.get('rf_dev_type', None)):
@@ -179,10 +180,13 @@
         config.overlay(values, dict(enb=self._conf))
         config.overlay(values, dict(enb={ 'mme_addr': self.epc.addr() }))
 
+        self._num_prb = int(values['enb'].get('num_prb', None))
+        assert self._num_prb
+        self._txmode = int(values['enb'].get('transmission_mode', None))
+        assert self._txmode
+
         # We need to set some specific variables programatically here to match IP addresses:
         if self._conf.get('rf_dev_type') == 'zmq':
-            self._num_prb = int(values['enb'].get('num_prb', None))
-            assert self._num_prb
             base_srate = num_prb2base_srate(self._num_prb)
             rf_dev_args = 'fail_on_disconnect=true,tx_port=tcp://' + self.addr() \
                         + ':2000,rx_port=tcp://' + self.ue.addr() \
@@ -222,4 +226,31 @@
     def num_prb(self):
         return self._num_prb
 
+    def ue_max_rate(self, downlink=True):
+        # The max rate for a single UE per PRB in TM1
+        max_phy_rate_tm1_dl = { 6 : 2.3e6,
+                               15 : 8e6,
+                               25 : 16e6,
+                               50 : 36e6,
+                               75 : 54e6,
+                               100 : 75e6 }
+        # TODO: proper values for this table:
+        max_phy_rate_tm1_ul = { 6 : 0.23e6,
+                               15 : 0.8e6,
+                               25 : 1.6e6,
+                               50 : 3.6e6,
+                               75 : 5.4e6,
+                               100 : 7.5e6 }
+        if downlink:
+            max_rate = max_phy_rate_tm1_dl[self.num_prb()]
+        else:
+            max_rate = max_phy_rate_tm1_ul[self.num_prb()]
+        #TODO: calculate for non-standard prb numbers.
+        if self._txmode > 2:
+            max_rate *= 2
+        # We use 3 control symbols for 6, 15 and 25 PRBs which results in lower max rate
+        if self.num_prb() < 50:
+          max_rate *= 0.9
+        return max_rate
+
 # vim: expandtab tabstop=4 shiftwidth=4
diff --git a/src/osmo_gsm_tester/srs_ue.py b/src/osmo_gsm_tester/srs_ue.py
index cbd8a68..f90ea32 100644
--- a/src/osmo_gsm_tester/srs_ue.py
+++ b/src/osmo_gsm_tester/srs_ue.py
@@ -22,6 +22,7 @@
 
 from . import log, util, config, template, process, remote
 from .run_node import RunNode
+from .event_loop import MainLoop
 from .ms import MS
 
 def rf_type_valid(rf_type_str):
@@ -91,10 +92,6 @@
             self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file)
         except Exception as e:
             self.log(repr(e))
-        try:
-            self.rem_host.scpfrom('scp-back-metrics', self.remote_metrics_file, self.metrics_file)
-        except Exception as e:
-            self.log(repr(e))
 
     def setup_runs_locally(self):
         return self.remote_user is None
@@ -102,6 +99,9 @@
     def netns(self):
         return "srsue1"
 
+    def stop(self):
+        self.suite_run.stop_process(self.process)
+
     def connect(self, enb):
         self.log('Starting srsue')
         self.enb = enb
@@ -247,4 +247,80 @@
             proc = self.rem_host.RemoteNetNSProcess(name, self.netns(), popen_args, env={})
         proc.launch_sync()
 
+    def verify_metric(self, value, operation='avg', metric='dl_brate', criterion='gt'):
+        # file is not properly flushed until the process has stopped.
+        if self.running():
+            self.stop()
+            # metrics file is not flushed immediatelly by the OS during process
+            # tear down, we need to wait some extra time:
+            MainLoop.sleep(self, 2)
+            if not self.setup_runs_locally():
+                try:
+                    self.rem_host.scpfrom('scp-back-metrics', self.remote_metrics_file, self.metrics_file)
+                except Exception as e:
+                    self.err('Failed copying back metrics file from remote host')
+                    raise e
+        metrics = srsUEMetrics(self.metrics_file)
+        return metrics.verify(value, operation, metric, criterion)
+
+import numpy
+
+class srsUEMetrics(log.Origin):
+
+    VALID_OPERATIONS = ['avg', 'sum']
+    VALID_CRITERION = ['eq','gt','lt']
+    CRITERION_TO_SYM = { 'eq' : '==', 'gt' : '>', 'lt' : '<' }
+    CRYTERION_TO_SYM_OPPOSITE = { 'eq' : '!=', 'gt' : '<=', 'lt' : '>=' }
+
+
+    def __init__(self, metrics_file):
+        super().__init__(log.C_RUN, 'srsue_metrics')
+        self.raw_data = None
+        self.metrics_file = metrics_file
+        # read CSV, guessing data type with first row being the legend
+        try:
+            self.raw_data = numpy.genfromtxt(self.metrics_file, names=True, delimiter=';', dtype=None)
+        except (ValueError, IndexError, IOError) as error:
+            self.err("Error parsing metrics CSV file %s" % self.metrics_file)
+            raise error
+
+    def verify(self, value, operation='avg', metric='dl_brate', criterion='gt'):
+        if operation not in self.VALID_OPERATIONS:
+            raise log.Error('Unknown operation %s not in %r' % (operation, self.VALID_OPERATIONS))
+        if criterion not in self.VALID_CRITERION:
+            raise log.Error('Unknown operation %s not in %r' % (operation, self.VALID_CRITERION))
+        # check if given metric exists in data
+        try:
+            sel_data = self.raw_data[metric]
+        except ValueError as err:
+            print('metric %s not available' % metric)
+            raise err
+
+        if operation == 'avg':
+            result = numpy.average(sel_data)
+        elif operation == 'sum':
+            result = numpy.sum(sel_data)
+        self.dbg(result=result, value=value)
+
+        success = False
+        if criterion == 'eq' and result == value or \
+           criterion == 'gt' and result > value or \
+           criterion == 'lt' and result < value:
+            success = True
+
+        # Convert bitrate in Mbit/s:
+        if metric.find('brate') > 0:
+            result /= 1e6
+            value /= 1e6
+            mbit_str = ' Mbit/s'
+        else:
+            mbit_str = ''
+
+        if not success:
+            result_msg = "{:.2f}{} {} {:.2f}{}".format(result, mbit_str, self.CRYTERION_TO_SYM_OPPOSITE[criterion], value, mbit_str)
+            raise log.Error(result_msg)
+        result_msg = "{:.2f}{} {} {:.2f}{}".format(result, mbit_str, self.CRITERION_TO_SYM[criterion], value, mbit_str)
+        # TODO: overwrite test system-out with this text.
+        return result_msg
+
 # vim: expandtab tabstop=4 shiftwidth=4
diff --git a/suites/4g/iperf3.py b/suites/4g/iperf3.py
index 371376f..20489b5 100755
--- a/suites/4g/iperf3.py
+++ b/suites/4g/iperf3.py
@@ -32,7 +32,7 @@
 ue.connect(enb)
 
 iperf3srv.start()
-proc = iperf3cli.prepare_test_proc(False, ue.netns())
+proc = iperf3cli.prepare_test_proc(False, ue.netns(), time_sec=60)
 
 print('waiting for UE to attach...')
 wait(ue.is_connected, None)
@@ -42,3 +42,7 @@
 proc.launch_sync()
 iperf3srv.stop()
 print_results(iperf3cli.get_results(), iperf3srv.get_results())
+
+max_rate = enb.ue_max_rate(downlink=False)
+res_str = ue.verify_metric(max_rate * 0.9, operation='avg', metric='ul_brate', criterion='gt')
+print(res_str + '\n')
diff --git a/suites/4g/suite.conf b/suites/4g/suite.conf
index 352293a..59e393a 100644
--- a/suites/4g/suite.conf
+++ b/suites/4g/suite.conf
@@ -7,3 +7,6 @@
   modem:
   - times: 1
     type: srsue
+
+defaults:
+  timeout: 180s

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-gsm-tester/+/17323
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: Ib1da58615cdc4f53ac1a27080e94e5b47760c508
Gerrit-Change-Number: 17323
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200305/5ecd7dec/attachment.htm>


More information about the gerrit-log mailing list