blob: fd9fa3a4ceea898687d5053b3361f8f4414610c5 [file] [log] [blame]
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +01001# osmo_gsm_tester: base classes to share code among BTS subclasses.
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
Pau Espin Pedrol698ad4c2018-07-27 16:15:59 +020020import copy
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010021from abc import ABCMeta, abstractmethod
Pau Espin Pedrol680ba032020-10-14 14:49:05 +020022from ..core import log
23from ..core import config
24from ..core import schema
25from ..core import util
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010026
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020027def on_register_schemas():
28 resource_schema = {
29 'label': schema.STR,
30 'type': schema.STR,
31 'addr': schema.IPV4,
32 'band': schema.BAND,
33 'direct_pcu': schema.BOOL_STR,
Nils Fürstea8180152020-12-03 14:14:31 +010034 'ciphers[]': schema.CIPHER_2G,
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020035 'channel_allocator': schema.CHAN_ALLOCATOR,
36 'gprs_mode': schema.GPRS_MODE,
Pau Espin Pedrol680ba032020-10-14 14:49:05 +020037 'emergency_calls_allowed': schema.BOOL_STR,
Neels Hofmeyraa67f952020-11-27 08:22:21 +010038 'base_station_id_code': schema.UINT,
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020039 'num_trx': schema.UINT,
40 'max_trx': schema.UINT,
41 'trx_list[].addr': schema.IPV4,
42 'trx_list[].hw_addr': schema.HWADDR,
43 'trx_list[].net_device': schema.STR,
44 'trx_list[].nominal_power': schema.UINT,
45 'trx_list[].max_power_red': schema.UINT,
46 'trx_list[].timeslot_list[].phys_chan_config': schema.PHY_CHAN,
47 'trx_list[].power_supply.type': schema.STR,
48 'trx_list[].power_supply.device': schema.STR,
49 'trx_list[].power_supply.port': schema.STR,
Neels Hofmeyraa67f952020-11-27 08:22:21 +010050 'trx_list[].arfcn': schema.UINT,
Pau Espin Pedrolea8c3d42020-05-04 12:05:05 +020051 }
52 schema.register_resource_schema('bts', resource_schema)
53
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010054class Bts(log.Origin, metaclass=ABCMeta):
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010055
56##############
57# PROTECTED
58##############
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020059 def __init__(self, testenv, conf, name, defaults_cfg_name):
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010060 super().__init__(log.C_RUN, name)
Pau Espin Pedrol58603672018-08-09 13:45:55 +020061 self.bsc = None
62 self.sgsn = None
63 self.lac = None
64 self.rac = None
65 self.cellid = None
66 self.bvci = None
67 self._num_trx = 1
68 self._max_trx = None
69 self.overlay_trx_list = []
Pau Espin Pedrola442cb82020-05-05 12:54:37 +020070 self.testenv = testenv
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +010071 self.conf = conf
Pau Espin Pedrole5194622018-05-07 13:36:58 +020072 self.defaults_cfg_name = defaults_cfg_name
Pau Espin Pedrol39df7f42018-05-07 13:49:33 +020073 self._init_num_trx()
74
75 def _resolve_bts_cfg(self, cfg_name):
76 res = None
77 val = config.get_defaults('bsc_bts').get(cfg_name)
78 if val is not None:
79 res = val
80 val = config.get_defaults(self.defaults_cfg_name).get(cfg_name)
81 if val is not None:
82 res = val
83 val = self.conf.get(cfg_name)
84 if val is not None:
85 res = val
86 return res
87
88 def _init_num_trx(self):
89 self._num_trx = 1
90 self._max_trx = None
91 val = self._resolve_bts_cfg('num_trx')
92 if val is not None:
93 self._num_trx = int(val)
94 val = self._resolve_bts_cfg('max_trx')
95 if val is not None:
96 self._max_trx = int(val)
97 self._validate_new_num_trx(self._num_trx)
98 self.overlay_trx_list = [Bts._new_default_trx_cfg() for trx in range(self._num_trx)]
99
100 def _validate_new_num_trx(self, num_trx):
101 if self._max_trx is not None and num_trx > self._max_trx:
102 raise log.Error('Amount of TRX requested is too high for maximum allowed: %u > %u' %(num_trx, self._max_trx))
103
104 @staticmethod
105 def _new_default_trx_cfg():
106 return {'timeslot_list':[{} for ts in range(8)]}
107
108 @staticmethod
109 def _trx_list_recreate(trx_list, new_size):
110 curr_len = len(trx_list)
111 if new_size < curr_len:
112 trx_list = trx_list[0:new_size]
113 elif new_size > curr_len:
114 for i in range(new_size - curr_len):
115 trx_list.append(Bts._new_default_trx_cfg())
116 return trx_list
Pau Espin Pedrole6999122018-05-08 14:38:24 +0200117
118 def conf_for_bsc_prepare(self):
119 values = config.get_defaults('bsc_bts')
Pau Espin Pedrol39df7f42018-05-07 13:49:33 +0200120 # Make sure the trx_list is adapted to num of trx configured at runtime
121 # to avoid overlay issues.
122 trx_list = values.get('trx_list')
123 if trx_list and len(trx_list) != self.num_trx():
124 values['trx_list'] = Bts._trx_list_recreate(trx_list, self.num_trx())
125
126 bts_defaults = config.get_defaults(self.defaults_cfg_name)
127 trx_list = bts_defaults.get('trx_list')
128 if trx_list and len(trx_list) != self.num_trx():
129 bts_defaults['trx_list'] = Bts._trx_list_recreate(trx_list, self.num_trx())
130
131 config.overlay(values, bts_defaults)
Pau Espin Pedrole6999122018-05-08 14:38:24 +0200132 if self.lac is not None:
133 config.overlay(values, { 'location_area_code': self.lac })
134 if self.rac is not None:
135 config.overlay(values, { 'routing_area_code': self.rac })
136 if self.cellid is not None:
137 config.overlay(values, { 'cell_identity': self.cellid })
138 if self.bvci is not None:
139 config.overlay(values, { 'bvci': self.bvci })
Pau Espin Pedrol698ad4c2018-07-27 16:15:59 +0200140
Pau Espin Pedrol680ba032020-10-14 14:49:05 +0200141 config.overlay(values, { 'emergency_calls_allowed': util.str2bool(values.get('emergency_calls_allowed', 'false')) } )
142
Pau Espin Pedrol698ad4c2018-07-27 16:15:59 +0200143 conf = copy.deepcopy(self.conf)
144 trx_list = conf.get('trx_list')
145 if trx_list and len(trx_list) != self.num_trx():
146 conf['trx_list'] = Bts._trx_list_recreate(trx_list, self.num_trx())
147 config.overlay(values, conf)
Pau Espin Pedrole6999122018-05-08 14:38:24 +0200148
149 sgsn_conf = {} if self.sgsn is None else self.sgsn.conf_for_client()
150 config.overlay(values, sgsn_conf)
Pau Espin Pedrol39df7f42018-05-07 13:49:33 +0200151
152 config.overlay(values, { 'trx_list': self.overlay_trx_list })
Pau Espin Pedrole6999122018-05-08 14:38:24 +0200153 return values
154
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100155########################
156# PUBLIC - INTERNAL API
157########################
158 @abstractmethod
159 def conf_for_bsc(self):
160 'Used by bsc objects to get path to socket.'
161 pass
162
163 def remote_addr(self):
164 return self.conf.get('addr')
165
Pau Espin Pedrol29b71322020-04-06 18:14:29 +0200166 def egprs_enabled(self):
167 return self.conf_for_bsc()['gprs_mode'] == 'egprs'
168
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100169 def cleanup(self):
170 'Nothing to do by default. Subclass can override if required.'
171 pass
172
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200173 def get_instance_by_type(testenv, conf):
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200174 """Allocate a BTS child class based on type. Opts are passed to the newly created object."""
175 bts_type = conf.get('type')
176 if bts_type is None:
177 raise RuntimeError('BTS type is not defined!')
178
179 if bts_type == 'osmo-bts-sysmo':
180 from .bts_sysmo import SysmoBts
181 bts_class = SysmoBts
182 elif bts_type == 'osmo-bts-trx':
183 from .bts_osmotrx import OsmoBtsTrx
184 bts_class = OsmoBtsTrx
185 elif bts_type == 'osmo-bts-oc2g':
186 from .bts_oc2g import OsmoBtsOC2G
187 bts_class = OsmoBtsOC2G
188 elif bts_type == 'osmo-bts-octphy':
189 from .bts_octphy import OsmoBtsOctphy
190 bts_class = OsmoBtsOctphy
191 elif bts_type == 'osmo-bts-virtual':
192 from .bts_osmovirtual import OsmoBtsVirtual
193 bts_class = OsmoBtsVirtual
194 elif bts_type == 'nanobts':
195 from .bts_nanobts import NanoBts
196 bts_class = NanoBts
197 else:
198 raise log.Error('BTS type not supported:', bts_type)
Pau Espin Pedrola442cb82020-05-05 12:54:37 +0200199 return bts_class(testenv, conf)
Pau Espin Pedrol1ee5ec52020-05-04 17:16:39 +0200200
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100201###################
202# PUBLIC (test API included)
203###################
204 @abstractmethod
Pau Espin Pedrolb1526b92018-05-22 20:32:30 +0200205 def start(self, keepalive=False):
206 '''Starts BTS. If keepalive is set, it will expect internal issues and
207 respawn related processes when detected'''
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100208 pass
209
210 @abstractmethod
211 def ready_for_pcu(self):
212 'True if the BTS is prepared to have a PCU connected, false otherwise'
213 pass
214
215 @abstractmethod
216 def pcu(self):
217 'Get the Pcu object associated with the BTS'
218 pass
219
Pau Espin Pedrolf6166142018-10-11 17:49:34 +0200220 def bts_type(self):
221 'Get the type of BTS'
222 return self.conf.get('type')
223
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100224 def set_bsc(self, bsc):
225 self.bsc = bsc
226
227 def set_sgsn(self, sgsn):
228 self.sgsn = sgsn
229
230 def set_lac(self, lac):
231 self.lac = lac
232
233 def set_rac(self, rac):
234 self.rac = rac
235
236 def set_cellid(self, cellid):
237 self.cellid = cellid
238
239 def set_bvci(self, bvci):
240 self.bvci = bvci
241
Pau Espin Pedrol39df7f42018-05-07 13:49:33 +0200242 def set_num_trx(self, num_trx):
243 assert num_trx > 0
244 self._validate_new_num_trx(num_trx)
245 if num_trx == self._num_trx:
246 return
247 self._num_trx = num_trx
248 self.overlay_trx_list = Bts._trx_list_recreate(self.overlay_trx_list, num_trx)
249
250 def num_trx(self):
251 return self._num_trx
252
253 def set_trx_phy_channel(self, trx_idx, ts_idx, config):
254 assert trx_idx < self._num_trx
255 assert ts_idx < 8
256 schema.phy_channel_config(config) # validation
257 self.overlay_trx_list[trx_idx]['timeslot_list'][ts_idx]['phys_chan_config'] = config
258
Pau Espin Pedrol52ad3a62018-03-08 17:50:14 +0100259# vim: expandtab tabstop=4 shiftwidth=4