libinstance.jinja2.cfg 12.5 KB
Newer Older
1 2
{#- Package ru/libinstance provides common instance code for handling Radio Units and cells.

3 4 5 6
    Set global icell_kind=enb|ue before importing to indicate which kind of
    cells (server- or client-level) need to be configured. Then, after
    importing, use buildout() macro to emit instance-level code to
    handle configured RUs and cells.
7

8
    NOTE: before importing package slaplte.jinja2 needs to be already loaded as
9 10 11

      {%- import 'slaplte.jinja2'  as slaplte     with context %}

12
    NOTE: driver-specific logic is implemented in rudrv .buildout_iru() and .buildout() .
13 14
#}

15 16 17
{#- iru_dict and icell_dict keep RU and cell registries
    iru_dict:    reference -> iru
    icell_dict:  reference -> icell
18
#}
19 20
{%- set iru_dict   = {} %}
{%- set icell_dict = {} %}
21
{%- do slaplte.load_iru_and_icell(iru_dict, icell_dict, icell_kind)  %}
22 23


24
{%- macro buildout()  %}
25
{%-   set root = slap_configuration['instance-title']   %}
26 27
{%-   set testing = slapparameter_dict.get("testing", False)  %}

28 29 30
{#-   B(name) returns buildout-encoded form of name #}
{%-   set B = xbuildout.encode  %}

31 32 33
{#-   part emits new buildout section and registers it into buildout.parts  #}
{%-   set parts_list = [] %}
{%-   macro part(name) %}
34 35
{%-     do parts_list.append(B(name))  %}
[{{ B(name) }}]
36 37 38 39
{%-   endmacro    %}

{#-   promise emits new buildout section for a promise    #}
{%-   macro promise(name)                                 %}
40 41
{#-     show in monitor RU1-... instead of COMP-ENB/RU1-  #}
{%-     set pretty_name = name.removeprefix('%s.' % root) %}
42 43
{{ part('promise-'+name) }}
<= monitor-promise-base
44 45
name = {{ dumps('%s.py' % pretty_name) }}
output = {{ dumps('%s/plugin/%s.py' % (directory.etc, pretty_name)) }}
46
config-testing = {{ testing }}
47 48 49 50
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
{%-   endmacro %}

{#-   import RU drivers                           #}
51 52 53 54
{%-   set J              = slaplte.J              %}
{%-   set jref_of_shared = slaplte.jref_of_shared %}
{%-   set jcell_ru_ref   = slaplte.jcell_ru_ref   %}
{%-   set ierror         = slaplte.ierror         %}
55 56 57 58
{%-   import 'ru_sdr_libinstance.jinja2.cfg'      as rudrv_sdr      with context %}
{%-   import 'ru_sunwave_libinstance.jinja2.cfg'  as rudrv_sunwave  with context %}
{%-   set rudrv_dict = namespace(sdr=rudrv_sdr,
                                 sunwave=rudrv_sunwave) %}
59
{%-   set rudrv_init = {} %}
60

61
{#-   split slapos tap interface for each RU that needs its own tap.
62
      fallback to non-split approach for ntap <= 1 to avoid hard-dependency on setcap/tapsplit
63 64 65 66 67

      TODO Relying on setcap and tapsplit should be removed once SlapOS is improved to
           provide several TAP interfaces to instances. See discussion at
           https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
           for details. #}
68
{%-   set ntap = len(list(iru_dict|dictsort | selectattr('1._.cpri_link', 'defined'))) %}
69
{%-   set vtap_list = []  %}
70 71 72
[vtap]
recipe = plone.recipe.command
ntap = {{ ntap }}
73
command = {{ netcapdo }} {{ pythonwitheggs }} {{ ru_tapsplit }} {{ slaplte.tap }} ${:ntap}
74 75
update-command = ${:command}
stop-on-error = true
76 77 78 79 80
{%-   if testing  %}
# StandaloneSlapOS does not provide slaptap
command = :
{%-   endif %}
{%-   set test_slapnet = netaddr.IPNetwork('1234::/71') %}
81 82 83 84 85 86 87

{%-   if ntap <= 1  %}
[vtap]
ntap = 0
stop-on-error = false

{%-     if ntap == 1  %}
88 89
{%-       do vtap_list.append(slaplte.tap) %}
[vtap.{{ slaplte.tap }}]
90 91 92 93 94
{%-       if testing  %}
network = {{ str(test_slapnet) }}
gateway = {{ str(test_slapnet[1]) }}
addr    = {{ str(test_slapnet[-1]) }}
{%-       else  %}
95 96 97
network = {{ slap_configuration['tap-ipv6-network'] }}
gateway = {{ slap_configuration['tap-ipv6-gateway'] }}
addr    = {{ slap_configuration['tap-ipv6-addr'] }}
98
{%-       endif %}
99 100 101 102 103
{%-     endif %}

{%-   else  %}

{%-   for i in range(1,1+ntap)  %}
104
{%-     set tap = '%s-%d' % (slaplte.tap, i)   %}
105
{%-     do vtap_list.append(tap) %}
106 107 108 109 110 111 112 113 114
[vtap.{{ tap }}]
recipe = slapos.recipe.build
depends = ${vtap:recipe}
init =
  import types
  def readfile(path):
    with open(path) as f:
      return f.read()

115
  import netaddr
116 117 118 119 120 121 122
  # ~ import tapsplit
  tapsplit = types.ModuleType('tapsplit')
  exec(readfile('{{ ru_tapsplit }}'), tapsplit.__dict__)

  # simulate what tapsplit would assign to the tap
  # ( tap subinterface will be created for real later at install time - when it
  #   is too late to update section options )
123 124 125 126
  if {{ testing }}:
    slapnet = netaddr.IPNetwork('{{ str(test_slapnet) }}')
  else:
    slapnet = tapsplit.ifnet6('{{ slaplte.tap }}')
127 128 129 130 131 132 133 134 135
  tapnet  = tapsplit.netsplit(slapnet, {{ 1+ntap }}) [{{ i }}]

  options['network'] = str(tapnet)
  options['gateway'] = str(tapnet[1])
  options['addr']    = str(tapnet[-1])
{%-   endfor  %}

{%-   endif %}

136 137 138 139 140 141 142 143 144 145 146 147
# vtap_jdict maps tapname -> json(interface-info)
[vtap_jdict]
recipe = slapos.recipe.build
depends = {% for tap in vtap_list %}  ${vtap.{{tap}}:addr}  {% endfor %}
init =
  import json
{%- for tap in vtap_list %}
  tap = self.buildout['vtap.{{tap}}']
  tap = {k: tap[k]  for k in ('network', 'gateway', 'addr')}
  options['{{tap}}'] = json.dumps(tap)
{%- endfor %}

148

149
{#-   provide CPRI-based RUs IP address via DHCP #}
150
{%-   if ntap > 0  %}
151 152 153 154 155 156 157
[dnsmasq-config]
recipe = slapos.recipe.template:jinja2
url = {{ru_dnsmasq_template}}
filename = dnsmasq.cfg
extensions = jinja2.ext.do
output = ${directory:etc}/${:filename}
context =
158
  import xbuildout xbuildout
159 160 161 162
  import json_module json
  import netaddr netaddr
  section directory directory
  section vtap_jdict vtap_jdict
163 164
  key iru_dict :iru_dict
iru_dict = {{ dumps(iru_dict) }}
165 166 167 168 169 170 171 172 173 174 175

{{ part('dnsmasq-service') }}
recipe = slapos.cookbook:wrapper
command-line = {{ dnsmasq_location }}/sbin/dnsmasq --conf-file=${dnsmasq-config:output} -x ${directory:run}/dnsmasq.pid --local-service --keep-in-foreground
wrapper-path = ${directory:service}/dnsmasq
mode = 0775
hash-files =
  ${dnsmasq-config:output}

# {# promise('dnsmasq-listen') #}
#promise = check_socket_listening
176 177
#config-host = ...
#config-port = ...
178 179 180
{%-   endif  %}


181
{#-   go through all RUs and for each RU emit generic promises and invoke
182
      RU-specific buildout handler #}
183 184 185 186 187 188 189 190 191 192
{%-   for ru_ref, iru in iru_dict|dictsort %}
{%-     set ru = iru['_']  %}

{#-     cells that are using iru  #}
{%-     set iru_icell_list = [] %}
{%-     for cell_ref, icell in icell_dict|dictsort %}
{%-       if ru_ref == J(jcell_ru_ref(icell, icell_dict)) %}
{%-         do iru_icell_list.append(icell) %}
{%-       endif   %}
{%-     endfor %}
193

194
# {{ dumps(ru_ref) }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R  ({{ ru.ru_type }})
195
{%-     if ru.ru_link_type == 'sdr'  %}
196 197
{%-       for (i, n) in enumerate(ru.sdr_dev_list)  %}
{{ promise('%s-sdr-busy%s' % (ru_ref, '-%d' % (i+1)  if i > 0  else '')) }}
198 199
promise = check_sdr_busy
config-sdr = {{ sdr }}
200
config-sdr_dev  = {{ n }}
201
config-dma_chan = 0
202
{%-       endfor  %}
203

204
{%-     elif ru.ru_link_type == 'cpri'  %}
205 206 207
{{ promise('%s-sdr-busy' % ru_ref) }}
promise = check_sdr_busy
config-sdr = {{ sdr }}
208 209
config-sdr_dev  = {{ ru.cpri_link.sdr_dev }}
config-dma_chan = {{ ru.cpri_link.sfp_port }}
210

211 212
{{ promise('%s-cpri-lock' % ru_ref) }}
promise = check_cpri_lock
213 214
config-sdr_dev  = {{ ru.cpri_link.sdr_dev }}
config-sfp_port = {{ ru.cpri_link.sfp_port }}
215
config-amarisoft-rf-info-log = ${ru_amarisoft-rf-info-template:log-output}
216 217 218

{%-     else %}
{%-       do bug('unreachable') %}
219 220
{%-     endif %}

221 222
{{ promise('%s-rx-saturated' % ru_ref) }}
promise = check_rx_saturated
223
config-rf-rx-chan-list = {{ list(range(ru._rf_chan_rx, ru._rf_chan_rx + ru.n_antenna_ul)) }}
224
config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
225 226
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}

227
{#-     driver-specific part #}
228 229
{%-     set rudrv = rudrv_dict[ru.ru_type] %}
{%-     if not rudrv_init.get(ru.ru_type) %}
230
{{        rudrv.buildout()  }}
231
{%-       do rudrv_init.update({ru.ru_type: 1}) %}
232
{%-     endif %}
233
{{      rudrv.buildout_iru(iru, iru_icell_list) }}
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

{#-     publish information about RU (skipping synthetic)  #}
{%-     if iru.slave_reference  %}
{{ part('ipublish-%s' % ru_ref) }}
recipe = slapos.cookbook:publish.serialised
-slave-reference = {{ dumps(iru.slave_reference) }}
{{ slap_configuration['slap-software-type'] }} = {{ dumps(root) }}
{%-       set iru_icell_ref_list = [] %}
{%-       for icell in iru_icell_list %}
{%-         do iru_icell_ref_list.append(J(jref_of_shared(icell)))  %}
{%-       endfor  %}
cell-list = {{ dumps(iru_icell_ref_list) }}
{%-       if ru.ru_link_type == 'cpri'  %}
ipv6 = ${vtap.{{ ru.cpri_link._tap }}:gateway}
{%-       endif %}
249 250
tx_gain = {{ dumps(ru.tx_gain) }}
rx_gain = {{ dumps(ru.rx_gain) }}
251
txrx_active = {{ dumps(ru.txrx_active) }}
252 253
{%-     endif %}

254 255 256
{%-   endfor %}

{#-   handle configured cells #}
257 258 259 260 261
{%-   for cell_ref, icell in icell_dict|dictsort %}
{%-     set cell   = icell['_'] %}
{%-     set ru_ref = J(jcell_ru_ref(icell, icell_dict)) %}
{%-     set iru    = iru_dict[ru_ref] %}
{%-     set ru     = iru['_'] %}
262

263
{%-     if icell_kind == 'enb'  %}
264 265 266 267
{#-       generate CELL-drb.cfg and CELL-sib23.asn  #}
{{ part('drb-config-%s' % cell_ref) }}
<= config-base
url = {{ {'lte': drb_lte_template, 'nr': drb_nr_template} [cell.cell_type] }}
268
output = ${directory:etc}/{{B('%s-drb.cfg' % cell_ref)}}
269
extra-context =
270 271 272 273 274 275 276 277
    key cell_ref :cell_ref
    key cell     :cell
    key ru_ref   :ru_ref
    key ru       :ru
cell_ref = {{ dumps(cell_ref) }}
cell     = {{ dumps(cell    ) }}
ru_ref   = {{ dumps(ru_ref  ) }}
ru       = {{ dumps(ru      ) }}
278 279 280 281

{{ part('sib23-config-%s' % cell_ref) }}
<= config-base
url = {{ sib23_template }}
282
output = ${directory:etc}/{{B('%s-sib23.asn' % cell_ref)}}
283
extra-context =
284 285 286 287 288 289 290 291
    key cell_ref :cell_ref
    key cell     :cell
    key ru_ref   :ru_ref
    key ru       :ru
cell_ref = {{ dumps(cell_ref) }}
cell     = {{ dumps(cell    ) }}
ru_ref   = {{ dumps(ru_ref  ) }}
ru       = {{ dumps(ru      ) }}
292
{%-     endif %}
293

294 295
{#-     publish information about the cell (skipping synthetic) #}
{%-     if icell.slave_reference  %}
296 297 298 299
{{ part('ipublish-%s' % cell_ref) }}
recipe = slapos.cookbook:publish.serialised
-slave-reference = {{ dumps(icell.slave_reference) }}
{{ slap_configuration['slap-software-type'] }} = {{ dumps(root) }}
300
ru  = {{ dumps(ru_ref) }}
301
{%- if cell.cell_type == 'lte' %}
302
band        = {{ dumps('b%d' % xearfcn_module.band(cell.dl_earfcn)[0].band) }}
303
dl_earfcn   = {{ dumps(cell.dl_earfcn) }}
304
ul_earfcn   = {{ dumps(cell.ul_earfcn) }}
305
{%- elif cell.cell_type == 'nr' %}
306 307
band        = {{ dumps('n%d' % cell.nr_band) }}
dl_nr_arfcn = {{ dumps(cell.dl_nr_arfcn) }}
308 309
ul_nr_arfcn = {{ dumps(cell.ul_nr_arfcn) }}
ssb_nr_arfcn= {{ dumps(cell.ssb_nr_arfcn) }}
310 311 312 313
{%- else  %}
{%-   do bug('unreachable') %}
{%- endif %}

314 315
{%-     endif %}

316
{%-   endfor %}
317

318 319 320 321 322 323 324 325 326
{#- retrieve rf and stats[rf,samples] data from amarisoft service for promises
    such as check_cpri_lock and check_rx_saturated.
#}
[ru_amarisoft-rf-info-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-rf-info.json.log
context =
  section directory directory
327
  key slapparameter_dict myslap:parameter_dict
328 329
  key log_file :log-output
  raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
330
  raw testing {{ testing }}
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ ru_amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py

{{ part('amarisoft-rf-info-service') }}
recipe = slapos.cookbook:wrapper
command-line = ${ru_amarisoft-rf-info-template:output}
wrapper-path = ${directory:service}/amarisoft-rf-info
mode = 0775
hash-files =
  ${ru_amarisoft-rf-info-template:output}

[ru_amarisoft-stats-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-stats.json.log
context =
  section directory directory
350
  key slapparameter_dict myslap:parameter_dict
351 352
  key log_file :log-output
  raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
353
  raw testing {{ testing }}
354
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
355 356
  key iru_dict :iru_dict
iru_dict = {{ dumps(iru_dict) }}
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
mode = 0775
url = {{ ru_amarisoft_stats_template }}
output = ${directory:bin}/amarisoft-stats.py

{{ part('amarisoft-stats-service') }}
recipe = slapos.cookbook:wrapper
command-line = ${ru_amarisoft-stats-template:output}
wrapper-path = ${directory:service}/amarisoft-stats
mode = 0775
hash-files =
  ${ru_amarisoft-stats-template:output}

{{ promise('amarisoft-stats-log') }}
promise = check_amarisoft_stats_log
config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}

373 374 375 376 377 378 379

[buildout]
parts +=
{%- for part in parts_list %}
    {{ part }}
{%- endfor %}
{%- endmacro  %}