blob: 25efc7f85b314bf053393ccf42c82bc35fbae6e1 [file] [log] [blame]
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +01001# osmo_gsm_tester: specifics for running an ip.access nanoBTS
2#
3# Copyright (C) 2018 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
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010021import re
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +010022import json
Pau Espin Pedrole1a58bd2020-04-10 20:46:07 +020023from ..core import log, config, util, process
24from ..core.event_loop import MainLoop
Pau Espin Pedrole8bbcbf2020-04-10 19:51:31 +020025from . import pcap_recorder, bts, pcu
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010026from . import powersupply
27
28class NanoBts(bts.Bts):
29
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010030##############
31# PROTECTED
32##############
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020033 def __init__(self, testenv, conf):
34 super().__init__(testenv, conf, 'nanobts_%s' % conf.get('label', 'nolabel'), 'nanobts')
Pau Espin Pedrol926a4b82018-08-09 12:57:03 +020035 self.pwsup_list = []
36 self._pcu = None
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010037
38 def _configure(self):
39 if self.bsc is None:
40 raise log.Error('BTS needs to be added to a BSC or NITB before it can be configured')
41
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +020042 for trx_i in range(self.num_trx()):
43 pwsup_opt = self.conf.get('trx_list')[trx_i].get('power_supply', {})
44 if not pwsup_opt:
45 raise log.Error('No power_supply attribute provided in conf for TRX %d!' % trx_i)
46 pwsup_type = pwsup_opt.get('type')
47 if not pwsup_type:
48 raise log.Error('No type attribute provided in power_supply conf for TRX %d!' % trx_i)
49 self.pwsup_list.append(powersupply.get_instance_by_type(pwsup_type, pwsup_opt))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010050
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +020051
52 def get_pcap_filter_all_trx_ip(self):
53 ret = "("
54 for trx_i in range(self.num_trx()):
55 if trx_i != 0:
56 ret = ret + " or "
57 bts_trx_ip = self.conf.get('trx_list')[trx_i].get('addr')
58 ret = ret + "host " + bts_trx_ip
59 ret = ret + ")"
60 return ret
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010061
62########################
63# PUBLIC - INTERNAL API
64########################
65
66 def conf_for_bsc(self):
Pau Espin Pedrole5194622018-05-07 13:36:58 +020067 values = self.conf_for_bsc_prepare()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010068 config.overlay(values, { 'osmobsc_bts_type': 'nanobts' })
69
70 self.dbg(conf=values)
71 return values
72
73
74 def cleanup(self):
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +020075 for pwsup in self.pwsup_list:
76 self.dbg('Powering off NanoBTS TRX')
77 pwsup.power_set(False)
78 self.pwsup_list = []
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010079
80###################
81# PUBLIC (test API included)
82###################
83
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +020084 def start(self, keepalive=False):
Pau Espin Pedrola238ed92018-03-26 13:06:12 +020085 if self.conf.get('ipa_unit_id') is None:
Holger Hans Peter Freyther1ea4e522019-02-27 04:34:28 +000086 raise log.Error('No attribute ipa_unit_id provided in conf!')
Pau Espin Pedrol2a2d8462020-05-11 10:56:52 +020087 self.run_dir = util.Dir(self.testenv.test().get_run_dir().new_dir(self.name()))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010088 self._configure()
89
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010090 unitid = int(self.conf.get('ipa_unit_id'))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010091
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +020092 # Make sure all nanoBTS TRX are powered and in a clean state:
93 for pwsup in self.pwsup_list:
94 self.dbg('Powering cycling NanoBTS TRX')
95 pwsup.power_cycle(1.0)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010096
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020097 pcap_recorder.PcapRecorder(self.testenv, self.run_dir.new_dir('pcap'), None,
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +020098 '%s and port not 22' % self.get_pcap_filter_all_trx_ip())
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +010099
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100100
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200101 # TODO: If setting N TRX, we should set up them in parallel instead of waiting for each one.
102 for trx_i in range(self.num_trx()):
103 bts_trx_ip = self.conf.get('trx_list')[trx_i].get('addr')
104 # This fine for now, however concurrent tests using Nanobts may run into "address already in use" since dst is broadcast.
105 # Once concurrency is needed, a new config attr should be added to have an extra static IP assigned on the main-unit to each Nanobts resource.
106 local_bind_ip = util.dst_ip_get_local_bind(bts_trx_ip)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100107
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200108 self.log('Finding nanobts %s, binding on %s...' % (bts_trx_ip, local_bind_ip))
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200109 ipfind = AbisIpFind(self.testenv, self.run_dir, local_bind_ip, 'preconf')
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200110 ipfind.start()
111 ipfind.wait_bts_ready(bts_trx_ip)
112 running_unitid, running_trx = ipfind.get_unitid_by_ip(bts_trx_ip)
113 self.log('Found nanobts %s with unit_id %d trx %d' % (bts_trx_ip, running_unitid, running_trx))
114 ipfind.stop()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100115
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200116 ipconfig = IpAccessConfig(self.testenv, self.run_dir, bts_trx_ip)
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100117 running_oml_ip = ipconfig.get_oml_ip()
118
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200119 if running_unitid != unitid or running_trx != trx_i:
120 if not ipconfig.set_unit_id(unitid, trx_i, False):
121 raise log.Error('Failed configuring unit id %d trx %d' % (unitid, trx_i))
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200122
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100123 if running_oml_ip != self.bsc.addr():
124 # Apply OML IP and restart nanoBTS as it is required to apply the changes.
125 self.dbg('Current OML IPaddr "%s" does not match BSC IPaddr "%s", reconfiguring and restarting it' % (running_oml_ip, self.bsc.addr()))
126 if not ipconfig.set_oml_ip(self.bsc.addr(), True):
127 raise log.Error('Failed configuring OML IP %s' % bts_trx_ip)
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200128
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100129 # Let some time for BTS to restart. It takes much more than 20 secs, and
130 # this way we make sure we don't catch responses in abisip-find prior to
131 # BTS restarting.
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200132 MainLoop.sleep(20)
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100133
134 self.dbg('Starting to connect id %d trx %d to' % (unitid, trx_i), self.bsc)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200135 ipfind = AbisIpFind(self.testenv, self.run_dir, local_bind_ip, 'postconf')
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100136 ipfind.start()
137 ipfind.wait_bts_ready(bts_trx_ip)
138 self.log('nanoBTS id %d trx %d configured and running' % (unitid, trx_i))
139 ipfind.stop()
140 else:
141 self.dbg('nanoBTS id %d trx %d no need to change OML IP (%s) and restart' % (unitid, trx_i, running_oml_ip))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100142
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200143 MainLoop.wait(self.bsc.bts_is_connected, self, timeout=600)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100144 self.log('nanoBTS connected to BSC')
145
146 #According to roh, it can be configured to use a static IP in a permanent way:
147 # 1- use abisip-find to find the default address
148 # 2- use ./ipaccess-config --ip-address IP/MASK
149 # 3- use ./ipaccess-config --ip-gateway IP to set the IP of the main unit
150 # 4- use ./ipaccess-config --restart to restart and apply the changes
151
152 #Start must do the following:
153 # 1- use abisip-find to find the default address
154 # 2- use ./ipaccess-config --unit-id UNIT_ID
155 # 3- use ./ipaccess-config --oml-ip --restart to set the IP of the BSC and apply+restart.
156 # According to roh, using the 3 of them together was not reliable to work properly.
157
158 def ready_for_pcu(self):
159 """We don't really care as we use a Dummy PCU class."""
160 return True
161
162 def pcu(self):
163 if not self._pcu:
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200164 self._pcu = pcu.PcuDummy(self.testenv, self, self.conf)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100165 return self._pcu
166
167
168class AbisIpFind(log.Origin):
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200169 testenv = None
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100170 parent_run_dir = None
171 run_dir = None
172 inst = None
173 env = None
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200174 bind_ip = None
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100175 proc = None
176
177 BIN_ABISIP_FIND = 'abisip-find'
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200178 BTS_UNIT_ID_RE = re.compile("Unit_ID='(?P<unit_id>\d+)/\d+/(?P<trx_id>\d+)'")
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100179
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200180 def __init__(self, testenv, parent_run_dir, bind_ip, name_suffix):
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100181 super().__init__(log.C_RUN, AbisIpFind.BIN_ABISIP_FIND + '-' + name_suffix)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200182 self.testenv = testenv
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100183 self.parent_run_dir = parent_run_dir
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200184 self.bind_ip = bind_ip
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100185 self.env = {}
186
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100187 def start(self):
188 self.run_dir = util.Dir(self.parent_run_dir.new_dir(self.name()))
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200189 self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('osmo-bsc')))
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200190
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100191 lib = self.inst.child('lib')
192 if not os.path.isdir(lib):
193 raise log.Error('No lib/ in %r' % self.inst)
194 ipfind_path = self.inst.child('bin', AbisIpFind.BIN_ABISIP_FIND)
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200195 if not os.path.isfile(ipfind_path):
196 raise RuntimeError('Binary missing: %r' % ipfind_path)
197
198 env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
199 self.proc = process.Process(self.name(), self.run_dir,
Pau Espin Pedrol48fce862018-04-04 12:44:24 +0200200 (ipfind_path, '-i', '1', '-b', self.bind_ip),
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200201 env=env)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200202 self.testenv.remember_to_stop(self.proc)
Pau Espin Pedrola238ed92018-03-26 13:06:12 +0200203 self.proc.launch()
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100204
205 def stop(self):
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200206 self.testenv.stop_process(self.proc)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100207
208 def get_line_by_ip(self, ipaddr):
209 """Get latest line (more up to date) from abisip-find based on ip address."""
210 token = "IP_Address='%s'" % ipaddr
211 myline = None
212 for line in (self.proc.get_stdout() or '').splitlines():
213 if token in line:
214 myline = line
215 return myline
216
217 def get_unitid_by_ip(self, ipaddr):
218 line = self.get_line_by_ip(ipaddr)
219 if line is None:
220 return None
221 res = AbisIpFind.BTS_UNIT_ID_RE.search(line)
222 if res:
223 unit_id = int(res.group('unit_id'))
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200224 trx_id = int(res.group('trx_id'))
225 return (unit_id, trx_id)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100226 raise log.Error('abisip-find unit_id field for nanobts %s not found in %s' %(ipaddr, line))
227
228 def bts_ready(self, ipaddr):
229 return self.get_line_by_ip(ipaddr) is not None
230
231 def wait_bts_ready(self, ipaddr):
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200232 MainLoop.wait(self.bts_ready, ipaddr)
Pau Espin Pedrol48fce862018-04-04 12:44:24 +0200233 # There's a period of time after boot in which nanobts answers to
234 # abisip-find but tcp RSTs ipacces-config conns. Let's wait in here a
235 # bit more time to avoid failing after stating the BTS is ready.
Pau Espin Pedrol664e3832020-06-10 19:30:33 +0200236 MainLoop.sleep(2)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100237
238class IpAccessConfig(log.Origin):
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200239 testenv = None
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100240 parent_run_dir = None
241 run_dir = None
242 inst = None
243 env = None
244 bts_ip = None
245
246 BIN_IPACCESS_CONFIG = 'ipaccess-config'
247
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200248 def __init__(self, testenv, parent_run_dir, bts_ip):
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100249 super().__init__(log.C_RUN, IpAccessConfig.BIN_IPACCESS_CONFIG)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200250 self.testenv = testenv
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100251 self.parent_run_dir = parent_run_dir
252 self.bts_ip = bts_ip
253 self.env = {}
254
Pau Espin Pedrol1444f552018-11-12 18:23:34 +0100255 def create_process(self, binary_name, *args):
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100256 binary = os.path.abspath(self.inst.child('bin', binary_name))
257 run_dir = self.run_dir.new_dir(binary_name)
258 if not os.path.isfile(binary):
259 raise RuntimeError('Binary missing: %r' % binary)
260 proc = process.Process(binary_name, run_dir,
261 (binary,) + args,
262 env=self.env)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100263 return proc
264
265 def run(self, name_suffix, *args):
266 self.run_dir = util.Dir(self.parent_run_dir.new_dir(self.name()+'-'+name_suffix))
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200267 self.inst = util.Dir(os.path.abspath(self.testenv.suite().trial().get_inst('osmo-bsc')))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100268 lib = self.inst.child('lib')
269 self.env = { 'LD_LIBRARY_PATH': util.prepend_library_path(lib) }
Pau Espin Pedrol1444f552018-11-12 18:23:34 +0100270 self.proc = self.create_process(IpAccessConfig.BIN_IPACCESS_CONFIG, *args)
271 return self.proc.launch_sync(raise_nonsuccess=False)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100272
Pau Espin Pedrolf6a07122018-07-27 16:24:29 +0200273 def set_unit_id(self, unitid, trx_num, restart=False):
274 uid_str = '%d/0/%d' % (unitid, trx_num)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100275 if restart:
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100276 retcode = self.run('setunitid', '--restart', '--unit-id', '%s' % uid_str, self.bts_ip)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100277 else:
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100278 retcode = self.run('setunitid', '--unit-id', '%s' % uid_str, self.bts_ip)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100279 if retcode != 0:
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100280 self.err('ipaccess-config --unit-id %s returned error code %d' % (uid_str, retcode))
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100281 return retcode == 0
282
283 def set_oml_ip(self, omlip, restart=False):
284 if restart:
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100285 retcode = self.run('setoml', '--restart', '--oml-ip', omlip, self.bts_ip)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100286 else:
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100287 retcode = self.run('setoml', '--oml-ip', omlip, self.bts_ip)
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100288 if retcode != 0:
289 self.error('ipaccess-config --oml-ip %s returned error code %d' % (omlip, retcode))
290 return retcode == 0
291
Pau Espin Pedrol14e3e502018-11-12 18:44:47 +0100292 def get_oml_ip(self):
293 retcode = self.run('getoml', '-q', '-G', self.bts_ip)
294 if retcode != 0:
295 raise log.Error('ipaccess-config -q -G %s returned error code %d' % (self.bts_ip, retcode))
296 output = self.proc.get_stdout()
297 # Our logging system adds "launched on" line at the start, so let's skip until the json code:
298 output_json = output[output.index('{'):]
299 json_data = json.loads(output_json)
300 return json_data['primary_oml_ip']
301
Pau Espin Pedrol1b28a582018-03-08 21:01:26 +0100302# vim: expandtab tabstop=4 shiftwidth=4