From 3ce672594ff67402f2eea72b0ff2a7b2c4fdaf5d Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Fri, 29 Jan 2021 17:45:18 +0100 Subject: [PATCH] enb,epc,ms: refactor KPI API we previously mixed component specific and component agnostic APIs (stdout vs. log file for example) for setting and retrieving KPI. This patch propose to use a single abstract get_kpis() method for all components that can be enriched with component-specific stuff as desired. In the case of srsLTE blocks, the main implementation will remain in srslte_common() and is shared among srsENB/srsUE/srsEPC. The KPI analyzer in srslte_common() extract and also manages all three KPI sources (log, csv and stdout) independently. In addition to the get_kpis() method that always returns a flat dictionary, it also exposes get_kpi_tree() that return a dict of KPI dicts that will be used for the Junit.xml generation. Change-Id: I4bacc6b8a0cb92a581edfb947100b57022265265 --- src/osmo_gsm_tester/obj/enb.py | 6 +- src/osmo_gsm_tester/obj/enb_amarisoft.py | 3 + src/osmo_gsm_tester/obj/enb_srs.py | 5 +- src/osmo_gsm_tester/obj/epc.py | 4 ++ src/osmo_gsm_tester/obj/epc_amarisoft.py | 3 + src/osmo_gsm_tester/obj/epc_srs.py | 3 + src/osmo_gsm_tester/obj/ms_srs.py | 2 +- src/osmo_gsm_tester/obj/srslte_common.py | 75 +++++++++++------------- 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/osmo_gsm_tester/obj/enb.py b/src/osmo_gsm_tester/obj/enb.py index 32ead698..15a0033f 100644 --- a/src/osmo_gsm_tester/obj/enb.py +++ b/src/osmo_gsm_tester/obj/enb.py @@ -356,4 +356,8 @@ class eNodeB(log.Origin, metaclass=ABCMeta): def get_counter(self, counter_name): pass -# vim: expandtab tabstop=4 shiftwidth=4 + @abstractmethod + def get_kpis(self): + pass + +# vim: expandtab tabstop=4 shiftwidth=4 \ No newline at end of file diff --git a/src/osmo_gsm_tester/obj/enb_amarisoft.py b/src/osmo_gsm_tester/obj/enb_amarisoft.py index e97bb90f..01aed189 100644 --- a/src/osmo_gsm_tester/obj/enb_amarisoft.py +++ b/src/osmo_gsm_tester/obj/enb_amarisoft.py @@ -260,6 +260,9 @@ class AmarisoftENB(enb.eNodeB): return self.process.get_counter_stdout('PRACH:') raise log.Error('counter %s not implemented!' % counter_name) + def get_kpis(self): + return {} + def get_rfemu(self, cell=0, dl=True): cell_list = self.gen_conf['enb'].get('cell_list', None) if cell_list is None or len(cell_list) < cell + 1: diff --git a/src/osmo_gsm_tester/obj/enb_srs.py b/src/osmo_gsm_tester/obj/enb_srs.py index aee3f616..83df5ed7 100644 --- a/src/osmo_gsm_tester/obj/enb_srs.py +++ b/src/osmo_gsm_tester/obj/enb_srs.py @@ -101,7 +101,7 @@ class srsENB(enb.eNodeB, srslte_common): self.log(repr(e)) # Collect KPIs for each TC - self.testenv.test().set_kpis(self.get_kpis()) + self.testenv.test().set_kpis(self.get_kpi_tree()) # Clean up for parent class: super().cleanup() @@ -267,6 +267,9 @@ class srsENB(enb.eNodeB, srslte_common): return self.process.get_counter_stdout('RACH:') raise log.Error('counter %s not implemented!' % counter_name) + def get_kpis(self): + return srslte_common.get_kpis(self) + def get_rfemu(self, cell=0, dl=True): cell_list = self.gen_conf['enb'].get('cell_list', None) if cell_list is None or len(cell_list) < cell + 1: diff --git a/src/osmo_gsm_tester/obj/epc.py b/src/osmo_gsm_tester/obj/epc.py index 6f056fcd..aaa96b7c 100644 --- a/src/osmo_gsm_tester/obj/epc.py +++ b/src/osmo_gsm_tester/obj/epc.py @@ -116,4 +116,8 @@ class EPC(log.Origin, metaclass=ABCMeta): def run_node(self): return self._run_node + @abstractmethod + def get_kpis(self): + pass + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/obj/epc_amarisoft.py b/src/osmo_gsm_tester/obj/epc_amarisoft.py index 1291891f..4c3bf07f 100644 --- a/src/osmo_gsm_tester/obj/epc_amarisoft.py +++ b/src/osmo_gsm_tester/obj/epc_amarisoft.py @@ -199,4 +199,7 @@ class AmarisoftEPC(epc.EPC): # TODO: set proper addr return '192.168.4.1' + def get_kpis(self): + return {} + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/obj/epc_srs.py b/src/osmo_gsm_tester/obj/epc_srs.py index 6a7a20e5..6a0a7bb8 100644 --- a/src/osmo_gsm_tester/obj/epc_srs.py +++ b/src/osmo_gsm_tester/obj/epc_srs.py @@ -219,4 +219,7 @@ class srsEPC(epc.EPC, srslte_common): def tun_addr(self): return '172.16.0.1' + def get_kpis(self): + return srslte_common.get_kpis(self) + # vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/osmo_gsm_tester/obj/ms_srs.py b/src/osmo_gsm_tester/obj/ms_srs.py index 2f19f3f9..aaeeca53 100644 --- a/src/osmo_gsm_tester/obj/ms_srs.py +++ b/src/osmo_gsm_tester/obj/ms_srs.py @@ -126,7 +126,7 @@ class srsUE(MS, srslte_common): self.log(repr(e)) # Collect KPIs for each TC - self.testenv.test().set_kpis(self.get_kpis()) + self.testenv.test().set_kpis(self.get_kpi_tree()) def features(self): return self._conf.get('features', []) diff --git a/src/osmo_gsm_tester/obj/srslte_common.py b/src/osmo_gsm_tester/obj/srslte_common.py index 21001b7c..cbc360fd 100644 --- a/src/osmo_gsm_tester/obj/srslte_common.py +++ b/src/osmo_gsm_tester/obj/srslte_common.py @@ -27,7 +27,9 @@ class srslte_common(): # don't inherit from log.Origin here but instead use .nam self.process = None self.metrics_file = None self.stop_sleep_time = 6 # We require at most 5s to stop - self.kpis = None + self.log_kpi = None + self.stdout_kpi = None + self.csv_kpi = None def sleep_after_stop(self): # Only sleep once @@ -42,61 +44,50 @@ class srslte_common(): # don't inherit from log.Origin here but instead use .nam self.sleep_after_stop() def get_kpis(self): - ''' Return all KPI ''' - if self.kpis is None: - self.extract_kpis() - return self.kpis + ''' Merge all KPI and return as flat dict ''' + self.extract_kpis() + kpi_flat = {} + kpi_flat.update(self.log_kpi) + kpi_flat.update(self.stdout_kpi) + kpi_flat.update(self.csv_kpi) + return kpi_flat - def get_log_kpis(self): - ''' Return KPIs extracted from log ''' - if self.kpis is None: - self.extract_kpis() - - # Use log KPIs if they exist for this node - if "log_" + self.name() in self.kpis: - log_kpi = self.kpis["log_" + self.name()] - else: - log_kpi = {} - - # Make sure we have the errors and warnings counter in the dict - if 'total_errors' not in log_kpi: - log_kpi['total_errors'] = 0 - if 'total_warnings' not in log_kpi: - log_kpi['total_warnings'] = 0 - return log_kpi + def get_kpi_tree(self): + ''' Return all KPI as dict of dict in which the source (e.g. stdout_srsue1) is the key of the first dict ''' + self.extract_kpis() + kpi_tree = {} + kpi_tree["log_" + self.name()] = self.log_kpi + kpi_tree["csv_" + self.name()] = self.csv_kpi + kpi_tree["stdout_" + self.name()] = self.stdout_kpi + return kpi_tree def extract_kpis(self): ''' Use the srsLTE KPI analyzer module (part of srsLTE.git) if available to collect KPIs ''' + # Make sure this only runs once + if self.csv_kpi is not None or self.log_kpi is not None or self.stdout_kpi is not None: + return + + # Start with empty KPIs + self.log_kpi = {} + self.stdout_kpi = {} + self.csv_kpi = {} + # Stop application, copy back logs and process them if self.running(): self.stop() self.cleanup() - - self.kpis = {} try: # Please make sure the srsLTE scripts folder is included in your PYTHONPATH env variable from kpi_analyzer import kpi_analyzer analyzer = kpi_analyzer(self.name()) if self.log_file is not None: - self.kpis["log_" + self.name()] = analyzer.get_kpi_from_logfile(self.log_file) + self.log_kpi = analyzer.get_kpi_from_logfile(self.log_file) if self.process.get_output_file('stdout') is not None: - self.kpis["stdout_" + self.name()] = analyzer.get_kpi_from_stdout(self.process.get_output_file('stdout')) + self.stdout_kpi = analyzer.get_kpi_from_stdout(self.process.get_output_file('stdout')) if self.metrics_file is not None: - self.kpis["csv_" + self.name()] = analyzer.get_kpi_from_csv(self.metrics_file) + self.csv_kpi = analyzer.get_kpi_from_csv(self.metrics_file) + # PHY errors for either UE or eNB components from parsed KPI vector as extra entry in dict + self.log_kpi["num_phy_errors"] = analyzer.get_num_phy_errors(self.log_kpi) except ImportError: - self.log("Can't load KPI analyzer module.") - self.kpis = {} - - return self.kpis - - def get_num_phy_errors(self, kpi): - """ Use KPI analyzer to calculate the number PHY errors for either UE or eNB components from parsed KPI vector """ - try: - # Same as above, make sure the srsLTE scripts folder is included in your PYTHONPATH env variable - from kpi_analyzer import kpi_analyzer - analyzer = kpi_analyzer(self.name()) - return analyzer.get_num_phy_errors(kpi) - except ImportError: - self.log("Can't load KPI analyzer module.") - return 0 + self.log("Can't load KPI analyzer module.") \ No newline at end of file