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

    Use buildout() macro to emit instance-level code to
4
    handle configured RUs.
5

6 7 8 9
    NOTE: before importing package slaplte.jinja2 needs to already loaded as

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

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

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


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

26 27 28 29 30 31 32 33 34
{#-   part emits new buildout section and registers it into buildout.parts  #}
{%-   set parts_list = [] %}
{%-   macro part(name) %}
{%-     do parts_list.append(name)  %}
[{{ name }}]
{%-   endmacro    %}

{#-   promise emits new buildout section for a promise    #}
{%-   macro promise(name)                                 %}
35 36
{#-     show in monitor RU1-... instead of COMP-ENB/RU1-  #}
{%-     set pretty_name = name.removeprefix('%s.' % root) %}
37 38
{{ part('promise-'+name) }}
<= monitor-promise-base
39
name = {{ pretty_name }}.py
40
config-testing = {{ testing }}
41 42 43 44
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
{%-   endmacro %}

{#-   import RU drivers                           #}
45 46 47 48
{%-   set J              = slaplte.J              %}
{%-   set jref_of_shared = slaplte.jref_of_shared %}
{%-   set jcell_ru_ref   = slaplte.jcell_ru_ref   %}
{%-   set ierror         = slaplte.ierror         %}
49 50 51 52 53 54
{%-   import 'ru_sdr_libinstance.jinja2.cfg'      as rudrv_sdr      with context %}
{%-   import 'ru_lopcomm_libinstance.jinja2.cfg'  as rudrv_lopcomm  with context %}
{%-   import 'ru_sunwave_libinstance.jinja2.cfg'  as rudrv_sunwave  with context %}
{%-   set rudrv_dict = namespace(sdr=rudrv_sdr,
                                 lopcomm=rudrv_lopcomm,
                                 sunwave=rudrv_sunwave) %}
55
{%-   set rudrv_init = {} %}
56

57
{#-   split slapos tap interface for each RU that needs its own tap.
58 59 60 61 62 63
      fallback to non-split approach for ntap <= 1 to avoid hard-dependecy on setcap/tapsplit

      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. #}
64
{%-   set ntap = len(list(iru_dict|dictsort | selectattr('1._.cpri_link', 'defined'))) %}
65
{%-   set vtap_list = []  %}
66 67 68
[vtap]
recipe = plone.recipe.command
ntap = {{ ntap }}
69
command = {{ netcapdo }} {{ pythonwitheggs }} {{ ru_tapsplit }} {{ slaplte.tap }} ${:ntap}
70 71
update-command = ${:command}
stop-on-error = true
72 73 74 75 76
{%-   if testing  %}
# StandaloneSlapOS does not provide slaptap
command = :
{%-   endif %}
{%-   set test_slapnet = netaddr.IPNetwork('1234::/71') %}
77 78 79 80 81 82 83

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

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

{%-   else  %}

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

111
  import netaddr
112 113 114 115 116 117 118
  # ~ 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 )
119 120 121 122
  if {{ testing }}:
    slapnet = netaddr.IPNetwork('{{ str(test_slapnet) }}')
  else:
    slapnet = tapsplit.ifnet6('{{ slaplte.tap }}')
123 124 125 126 127 128 129 130 131
  tapnet  = tapsplit.netsplit(slapnet, {{ 1+ntap }}) [{{ i }}]

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

{%-   endif %}

132 133 134 135 136 137 138 139 140 141 142 143
# 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 %}

144

145
{#-   provide CPRI-based RUs IP address via DHCP #}
146
{%-   if ntap > 0  %}
147 148 149 150 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 =
  import json_module json
  import netaddr netaddr
  section directory directory
  section vtap_jdict vtap_jdict
158
  json iru_dict {{ iru_dict | tojson }}
159 160 161 162 163 164 165 166 167 168 169

{{ 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
170 171
#config-host = ...
#config-port = ...
172 173 174
{%-   endif  %}


175
{#-   go through all RUs and for each RU emit generic promises and invoke
176
      RU-specific buildout handler #}
177 178 179 180 181 182 183 184 185 186
{%-   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 %}
187 188 189

# {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R  ({{ ru.ru_type}})
{%-     if ru.ru_link_type == 'sdr'  %}
190 191 192
{{ promise('%s-sdr-busy' % ru_ref) }}
promise = check_sdr_busy
config-sdr = {{ sdr }}
193
config-sdr_dev  = {{ ru.sdr_dev }}
194 195
config-dma_chan = 0

196
{%-     elif ru.ru_link_type == 'cpri'  %}
197 198 199
{{ promise('%s-sdr-busy' % ru_ref) }}
promise = check_sdr_busy
config-sdr = {{ sdr }}
200 201
config-sdr_dev  = {{ ru.cpri_link.sdr_dev }}
config-dma_chan = {{ ru.cpri_link.sfp_port }}
202

203 204
{{ promise('%s-cpri-lock' % ru_ref) }}
promise = check_cpri_lock
205 206
config-sdr_dev  = {{ ru.cpri_link.sdr_dev }}
config-sfp_port = {{ ru.cpri_link.sfp_port }}
207
config-amarisoft-rf-info-log = ${ru_amarisoft-rf-info-template:log-output}
208 209 210

{%-     else %}
{%-       do bug('unreachable') %}
211 212
{%-     endif %}

213 214
{{ promise('%s-rx-saturated' % ru_ref) }}
promise = check_rx_saturated
215
config-rf-rx-chan-list = {{ list(range(ru._rf_chan_rx, ru._rf_chan_rx + ru.n_antenna_ul)) }}
216
config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
217 218
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}

219
{#-     driver-specific part #}
220 221
{%-     set rudrv = rudrv_dict[ru.ru_type] %}
{%-     if not rudrv_init.get(ru.ru_type) %}
222
{{        rudrv.buildout()  }}
223
{%-       do rudrv_init.update({ru.ru_type: 1}) %}
224
{%-     endif %}
225
{{      rudrv.buildout_iru(iru, iru_icell_list) }}
226 227 228
{%-   endfor %}

{#-   handle configured cells #}
229 230 231 232 233
{%-   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['_'] %}
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255

{#-       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] }}
output = ${directory:etc}/{{'%s-drb.cfg' % cell_ref}}
extra-context =
    json cell_ref {{ cell_ref | tojson }}
    json cell     {{ cell     | tojson }}
    json ru_ref   {{ ru_ref   | tojson }}
    json ru       {{ ru       | tojson }}

{{ part('sib23-config-%s' % cell_ref) }}
<= config-base
url = {{ sib23_template }}
output = ${directory:etc}/{{'%s-sib23.asn' % cell_ref}}
extra-context =
    json cell_ref {{ cell_ref | tojson }}
    json cell     {{ cell     | tojson }}
    json ru_ref   {{ ru_ref   | tojson }}
    json ru       {{ ru       | tojson }}

256 257
{#-     publish information about the cell (skipping synthetic) #}
{%-     if icell.slave_reference  %}
258 259 260 261 262 263 264 265 266 267
[publish-connection-information]
{%- if cell.cell_type == 'lte' %}
{{cell_ref}}-dl_earfcn   = {{ dumps(cell.dl_earfcn) }}
{%- elif cell.cell_type == 'nr' %}
{{cell_ref}}-band        = {{ dumps('n%d' % cell.nr_band) }}
{{cell_ref}}-dl_nr_arfcn = {{ dumps(cell.dl_nr_arfcn) }}
{%- else  %}
{%-   do bug('unreachable') %}
{%- endif %}

268 269
{%-     endif %}

270
{%-   endfor %}
271

272 273 274 275 276 277 278 279 280
{#- 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
281
  key slapparameter_dict myslap:parameter_dict
282 283
  key log_file :log-output
  raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
284
  raw testing {{ testing }}
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
  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
304
  key slapparameter_dict myslap:parameter_dict
305 306
  key log_file :log-output
  raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
307
  raw testing {{ testing }}
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
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}

325 326 327 328 329 330 331

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