blob: dae8a5673f0b751f35b18f10d18510c598d46426 [file] [log] [blame]
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +01001# osmo_gsm_tester: specifics for running an SRS EPC process
2#
3# Copyright (C) 2020 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 pprint
22
Pau Espin Pedrole1a58bd2020-04-10 20:46:07 +020023from ..core import log, util, config, template, process, remote
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020024from ..core import schema
Pau Espin Pedrolda2e31f2020-03-31 13:45:01 +020025from . import epc
Andre Puschmann99fb78b2020-09-14 18:14:15 +020026from .srslte_common import srslte_common
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010027
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020028def on_register_schemas():
29 config_schema = {
30 'enable_pcap': schema.BOOL_STR,
Andre Puschmann82ced3f2020-06-18 14:52:39 +020031 'log_all_level': schema.STR,
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020032 }
33 schema.register_config_schema('epc', config_schema)
34
Andre Puschmann99fb78b2020-09-14 18:14:15 +020035class srsEPC(epc.EPC, srslte_common):
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010036
37 REMOTE_DIR = '/osmo-gsm-tester-srsepc'
38 BINFILE = 'srsepc'
39 CFGFILE = 'srsepc.conf'
40 DBFILE = 'srsepc_user_db.csv'
41 PCAPFILE = 'srsepc.pcap'
42 LOGFILE = 'srsepc.log'
43
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020044 def __init__(self, testenv, run_node):
45 super().__init__(testenv, run_node, 'srsepc')
Andre Puschmann99fb78b2020-09-14 18:14:15 +020046 srslte_common.__init__(self)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010047 self.run_dir = None
48 self.config_file = None
49 self.db_file = None
50 self.log_file = None
51 self.pcap_file = None
52 self.process = None
53 self.rem_host = None
Pau Espin Pedrol33737032020-04-16 13:55:57 +020054 self.remote_inst = None
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010055 self.remote_config_file = None
56 self.remote_db_file = None
57 self.remote_log_file = None
58 self.remote_pcap_file = None
Pau Espin Pedrol1e81b5a2020-03-16 12:42:17 +010059 self.enable_pcap = False
Andre Puschmann99fb78b2020-09-14 18:14:15 +020060 self.kpis = None
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010061 self.subscriber_list = []
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010062
63 def cleanup(self):
64 if self.process is None:
65 return
66 if self._run_node.is_local():
67 return
68 # copy back files (may not exist, for instance if there was an early error of process):
69 try:
70 self.rem_host.scpfrom('scp-back-log', self.remote_log_file, self.log_file)
71 except Exception as e:
72 self.log(repr(e))
Pau Espin Pedrol1e81b5a2020-03-16 12:42:17 +010073 if self.enable_pcap:
74 try:
75 self.rem_host.scpfrom('scp-back-pcap', self.remote_pcap_file, self.pcap_file)
76 except Exception as e:
77 self.log(repr(e))
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010078
79 def start(self):
80 self.log('Starting srsepc')
Pau Espin Pedrol2a2d8462020-05-11 10:56:52 +020081 self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010082 self.configure()
83 if self._run_node.is_local():
84 self.start_locally()
85 else:
86 self.start_remotely()
87
88 def start_remotely(self):
Pau Espin Pedrol33737032020-04-16 13:55:57 +020089 remote_lib = self.remote_inst.child('lib')
90 remote_binary = self.remote_inst.child('bin', srsEPC.BINFILE)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010091
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +010092 # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
93 self.log('Setting RPATH for srsepc')
94 self.rem_host.change_elf_rpath(remote_binary, remote_lib)
95 # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
96 self.log('Applying CAP_NET_ADMIN capability to srsepc')
97 self.rem_host.setcap_net_admin(remote_binary)
98
Pau Espin Pedrol33737032020-04-16 13:55:57 +020099 args = (remote_binary, self.remote_config_file)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100100
101 self.process = self.rem_host.RemoteProcess(srsEPC.BINFILE, args)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200102 self.testenv.remember_to_stop(self.process)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100103 self.process.launch()
104
105 def start_locally(self):
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100106 binary = inst.child('bin', BINFILE)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100107 lib = inst.child('lib')
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100108 env = {}
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200109
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100110 # setting capabilities will later disable use of LD_LIBRARY_PATH from ELF loader -> modify RPATH instead.
111 self.log('Setting RPATH for srsepc')
112 # srsepc binary needs patchelf <= 0.9 (0.10 and current master fail) to avoid failing during patch. OS#4389, patchelf-GH#192.
113 util.change_elf_rpath(binary, util.prepend_library_path(lib), self.run_dir.new_dir('patchelf'))
114 # srsepc requires CAP_NET_ADMIN to create tunnel devices: ioctl(TUNSETIFF):
115 self.log('Applying CAP_NET_ADMIN capability to srsepc')
116 util.setcap_net_admin(binary, self.run_dir.new_dir('setcap_net_admin'))
117
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200118 args = (binary, os.path.abspath(self.config_file))
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100119
120 self.process = process.Process(self.name(), self.run_dir, args, env=env)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200121 self.testenv.remember_to_stop(self.process)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100122 self.process.launch()
123
124 def configure(self):
Pau Espin Pedrole9219952020-05-25 20:04:52 +0200125 self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('srslte', self._run_node.run_label())))
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200126 if not os.path.isdir(self.inst.child('lib')):
127 raise log.Error('No lib/ in', self.inst)
128 if not self.inst.isfile('bin', srsEPC.BINFILE):
129 raise log.Error('No %s binary in' % srsEPC.BINFILE, self.inst)
130
Pau Espin Pedrol1e81b5a2020-03-16 12:42:17 +0100131 self.config_file = self.run_dir.child(srsEPC.CFGFILE)
132 self.db_file = self.run_dir.child(srsEPC.DBFILE)
133 self.log_file = self.run_dir.child(srsEPC.LOGFILE)
134 self.pcap_file = self.run_dir.child(srsEPC.PCAPFILE)
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200135
136 if not self._run_node.is_local():
137 self.rem_host = remote.RemoteHost(self.run_dir, self._run_node.ssh_user(), self._run_node.ssh_addr())
138 remote_prefix_dir = util.Dir(srsEPC.REMOTE_DIR)
139 self.remote_inst = util.Dir(remote_prefix_dir.child(os.path.basename(str(self.inst))))
140 remote_run_dir = util.Dir(remote_prefix_dir.child(srsEPC.BINFILE))
141
142 self.remote_config_file = remote_run_dir.child(srsEPC.CFGFILE)
143 self.remote_db_file = remote_run_dir.child(srsEPC.DBFILE)
144 self.remote_log_file = remote_run_dir.child(srsEPC.LOGFILE)
145 self.remote_pcap_file = remote_run_dir.child(srsEPC.PCAPFILE)
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100146
Pau Espin Pedrolc04528c2020-04-01 13:55:51 +0200147 values = super().configure(['srsepc'])
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100148
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200149 dbfile = self.db_file if self._run_node.is_local() else self.remote_db_file
150 logfile = self.log_file if self._run_node.is_local() else self.remote_log_file
151 pcapfile = self.pcap_file if self._run_node.is_local() else self.remote_pcap_file
152 config.overlay(values, dict(epc=dict(db_filename=dbfile,
153 log_filename=logfile,
154 pcap_filename=pcapfile)))
155
Pau Espin Pedrol1e81b5a2020-03-16 12:42:17 +0100156 # Convert parsed boolean string to Python boolean:
157 self.enable_pcap = util.str2bool(values['epc'].get('enable_pcap', 'false'))
158 config.overlay(values, dict(epc={'enable_pcap': self.enable_pcap}))
159
Pau Espin Pedrolb6937712020-02-27 18:02:20 +0100160 # Set qci for each subscriber:
Pau Espin Pedrol04ad3b52020-04-06 12:25:22 +0200161 qci = values['epc'].get('qci', None)
162 assert qci is not None
Pau Espin Pedrolb6937712020-02-27 18:02:20 +0100163 for i in range(len(self.subscriber_list)):
Pau Espin Pedrol04ad3b52020-04-06 12:25:22 +0200164 self.subscriber_list[i]['qci'] = qci
Pau Espin Pedrolb6937712020-02-27 18:02:20 +0100165 config.overlay(values, dict(epc=dict(hss=dict(subscribers=self.subscriber_list))))
166
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100167 self.dbg('SRSEPC CONFIG:\n' + pprint.pformat(values))
168
169 with open(self.config_file, 'w') as f:
170 r = template.render(srsEPC.CFGFILE, values)
171 self.dbg(r)
172 f.write(r)
173 with open(self.db_file, 'w') as f:
174 r = template.render(srsEPC.DBFILE, values)
175 self.dbg(r)
176 f.write(r)
177
Pau Espin Pedrol33737032020-04-16 13:55:57 +0200178 if not self._run_node.is_local():
179 self.rem_host.recreate_remote_dir(self.remote_inst)
180 self.rem_host.scp('scp-inst-to-remote', str(self.inst), remote_prefix_dir)
181 self.rem_host.recreate_remote_dir(remote_run_dir)
182 self.rem_host.scp('scp-cfg-to-remote', self.config_file, self.remote_config_file)
183 self.rem_host.scp('scp-db-to-remote', self.db_file, self.remote_db_file)
184
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100185 def subscriber_add(self, modem, msisdn=None, algo_str=None):
186 if msisdn is None:
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200187 msisdn = self.testenv.msisdn()
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100188 modem.set_msisdn(msisdn)
189
190 if algo_str is None:
191 algo_str = modem.auth_algo() or util.OSMO_AUTH_ALGO_NONE
192
193 if algo_str != util.OSMO_AUTH_ALGO_NONE and not modem.ki():
194 raise log.Error("Auth algo %r selected but no KI specified" % algo_str)
195
Pau Espin Pedrol0f7f2652020-07-13 12:01:10 +0200196 if algo_str == 'milenage':
197 if not modem.opc():
198 raise log.Error("Auth algo milenage selected but no OPC specified")
199 # srsepc's used_db uses token 'mil' for milenage:
200 algo_str = 'mil'
201
202 opc = (modem.opc() or '')
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100203 subscriber_id = len(self.subscriber_list) # list index
Pau Espin Pedrol0f7f2652020-07-13 12:01:10 +0200204 self.subscriber_list.append({'id': subscriber_id, 'imsi': modem.imsi(), 'msisdn': msisdn, 'auth_algo': algo_str, 'ki': modem.ki(), 'opc': opc, 'apn_ipaddr': modem.apn_ipaddr()})
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100205
206 self.log('Add subscriber', msisdn=msisdn, imsi=modem.imsi(), subscriber_id=subscriber_id,
207 algo_str=algo_str)
208 return subscriber_id
209
210 def enb_is_connected(self, enb):
Pau Espin Pedrol491f77c2020-04-20 14:20:43 +0200211 # Match against sample line: "S1 Setup Request - eNB Name: srsenb01, eNB id: 0x19"
212 stdout_lines = (self.process.get_stdout() or '').splitlines()
213 for l in stdout_lines:
214 if l.startswith('S1 Setup Request') and l.endswith('eNB id: %s' % hex(enb.id()).lower()):
215 return True
216 return False
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100217
218 def running(self):
219 return not self.process.terminated()
220
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100221 def tun_addr(self):
222 return '172.16.0.1'
223
Pau Espin Pedrolc8b0f932020-02-11 17:45:26 +0100224# vim: expandtab tabstop=4 shiftwidth=4