# instance-enb implements eNB/gNB service.

{#- defaults for global eNB/gNB parameters.
    TODO automatically load enb defaults from JSON schema #}
{%- set enb_defaults = {
  'com_ws_port':  9001,
  'com_addr':     '127.0.1.2',
  'use_ipv4':     False,
  'gnb_id_bits':  28,
  'nssai':        {'1': {'sst': 1}},
  'wendelin_telecom_software_release_url': 'wendelin-telecom-enb-shared-instance',
  'xlog_fluentbit_forward_host': 'fluentd.rapid.space',
  'xlog_fluentbit_forward_port': 24224,
} %}
{%- set gtp_addr_lo = '127.0.1.1' %}
{%- for k,v in enb_defaults|dictsort %}
{%-   do slapparameter_dict.setdefault(k, v) %}
{%- endfor %}
{%- set B = xbuildout.encode %}


[buildout]
extra-parts =
parts =
  directory
  enb-config
  enb-service
  xamari-xlog-service
  xlog-fluentbit-service
  request-wendelin-telecom-shared
  check-baseband-latency.py
  monitor-base
  publish-connection-information
  ${:extra-parts}

extends = {{ monitor_template }}

eggs-directory = {{ eggs_directory }}
develop-eggs-directory = {{ develop_eggs_directory }}
offline = true

{%- set icell_kind='enb'                                        %}
{%- import 'slaplte.jinja2'            as slaplte with context  %}
{%- import 'ru_libinstance.jinja2.cfg' as rulib   with context  %}
{%- set ipeer_dict = {}                                         %}
{%- set ipeercell_dict = {}                                     %}
{%- do slaplte.load_ipeer(ipeer_dict)                           %}
{%- do slaplte.load_ipeercell(ipeercell_dict)                   %}
{%- do slaplte.check_loaded_everything()                        %}

{{ rulib.buildout() }}

[myslap]
# NOTE we don't query slapos.cookbook:slapconfiguration the second time because
# slapparameter_dict is potentially modified with defaults.
parameter_dict = {{ dumps(slapparameter_dict) }}
configuration = {{ dumps(slap_configuration) }}


[monitor-httpd-conf-parameter]
httpd-include-file = {{ buildout_directory }}/etc/httpd-include-file.conf
port = ${monitor-instance-parameter:monitor-httpd-port}
url = https://[${monitor-instance-parameter:monitor-httpd-ipv6}]:${:port}

[monitor-instance-parameter]
monitor-httpd-port = ${monitor-address:port}

[monitor-address]
recipe = slapos.cookbook:free_port
minimum = 8035
maximum = 8055
ip = ${monitor-instance-parameter:monitor-httpd-ipv6}


[directory]
recipe = slapos.cookbook:mkdirectory
software = {{ buildout_directory }}
home = ${buildout:directory}
var = ${:home}/var
etc = ${:home}/etc
bin = ${:home}/bin
tmp = ${:home}/tmp
run = ${:var}/run
script = ${:etc}/run
service = ${:etc}/service
promise = ${:etc}/promise
log = ${:var}/log
xlog-fluentbit = ${:var}/xlog-fluentbit

[enb-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
enb-info-log = ${directory:log}/enb-info.log
enb-info-archive-log = ${directory:log}/enb-info.log
enb-radio-log = ${directory:log}/enb.log
enb-start-date = ${directory:run}/enb-start.date
inline =
  #!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
  # Amarisoft init scripts
  sudo -n /opt/amarisoft/rm-tmp-lte
  sudo -n /opt/amarisoft/init-sdr
  sudo -n /opt/amarisoft/init-enb
  # Add useful information to enb-info log
  (echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting eNB software...") >> ${:enb-info-log}
  (echo -n "PCB: " ; for o in t b v s ; do sudo -n /opt/amarisoft/get-sdr-info -$o 2> /dev/null ; echo -n " " ; done ; echo) >> ${:enb-info-log}
  (AMARISOFT_PATH=/dev/null {{ enb }}/lteenb ${directory:etc}/enb.cfg 2>&1 >/dev/null | sed -n 's/^.*\(Host ID.*\)$/\1/gp') >> ${:enb-info-log}
  echo "System info: $(uname -a)" >> ${:enb-info-log}
  ({{ sdr }}/sdr_util version && echo) >> ${:enb-info-log}
  # Keep the 50 latest enb radio log
  stat ${:enb-start-date} && mv ${:enb-radio-log} ${directory:log}/enb-$(cat ${:enb-start-date}).log
  rm -f $(ls -1t ${directory:log}/enb-2* | tail -n+50)
  rm -f $(ls -1t ${directory:log}/enb-info-2* | tail -n+50)
  date +"%Y-%m-%d-%T" > ${:enb-start-date}
  # Trim enb info log to 500k and keep a 100M archive of enb info log
  head -c -500k ${:enb-info-log} >> ${:enb-info-archive-log}
  tail -c 500k ${:enb-info-log} > ${:enb-info-log}.tmp
  mv ${:enb-info-log}.tmp ${:enb-info-log}
  tail -c 100M ${:enb-info-archive-log} > ${:enb-info-archive-log}.tmp
  mv ${:enb-info-archive-log}.tmp ${:enb-info-archive-log}
  # Launch lteenb
  {{ enb }}/lteenb ${directory:etc}/enb.cfg >> ${:enb-info-log} 2>> ${:enb-info-log}
{% endif %}

[enb-service]
recipe = slapos.cookbook:wrapper
command-line = ${enb-sh-wrapper:output}
wrapper-path = ${directory:service}/enb
mode = 0775
reserve-cpu = True
pidfile = ${directory:run}/enb.pid
hash-files =
  ${enb-config:output}
  ${enb-sh-wrapper:output}
environment =
  LD_LIBRARY_PATH={{ openssl_location }}/lib
  AMARISOFT_PATH=/opt/amarisoft/.amarisoft

[xamari-xlog-script]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
stats_logspec = stats[samples,rf]/${:period}s
{%- if slapparameter_dict.get("enb_drb_stats_enabled", True) %}
drb_stats_logspec = x.drb_stats/${:period}s
{%- else %}
drb_stats_logspec =
{%- endif %}
rotatespec = 100MB.9
logspec = ${:stats_logspec} ${:drb_stats_logspec}
{%- if slapparameter_dict.get("websocket_password", "") %}
websock = ws://[{{my_ipv6}}]:9001
{%- else %}
websock = ws://127.0.1.2:9001
{%- endif %}
xamari = {{ buildout_directory }}/bin/xamari
logfile = ${monitor-directory:public}/enb.xlog
inline =
  #!/bin/sh
  exec ${:xamari} xlog --rotate ${:rotatespec} ${:websock} ${:logfile} ${:logspec}

[xamari-xlog-service]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:service}/${:_buildout_section_name_}
command-line = ${xamari-xlog-script:output}
hash-files = ${:command-line}

[xlog-fluentbit-tag]
recipe = slapos.recipe.build
computer = ${slap-connection:computer-id}
enb-id = {{ slapparameter_dict.get("enb_id") }}
gnb-id = {{ slapparameter_dict.get("gnb_id") }}
init =
  import socket

  options['hostname'] = socket.gethostname()

  radio_id = ''
  if options['enb-id']:
    radio_id = 'e%s' % options['enb-id'] 
  elif options['gnb-id']:
    radio_id = 'g%s' % options['gnb-id']
  options['radio-id'] = radio_id

  xlog_fluentbit_tag = '_'.join(options[x] for x in ('hostname', 'computer', 'radio-id') if options[x])
  options['xlog-fluentbit-tag'] = xlog_fluentbit_tag

[xlog-fluentbit-config]
recipe = slapos.recipe.template
output = ${directory:etc}/${:_buildout_section_name_}.cfg
logfile = ${xamari-xlog-script:logfile}
forward-host = {{ slapparameter_dict.xlog_fluentbit_forward_host }}
forward-port = {{ slapparameter_dict.xlog_fluentbit_forward_port }}
forward-shared-key = {{ slapparameter_dict.get('xlog_fluentbit_forward_shared_key', '') }}
forward-self-hostname = {{ B(comp_id['comp-id']) }}
inline =
  [SERVICE]
      flush           5
  [INPUT]
      name            tail
      path            ${:logfile}
      tag             ${xlog-fluentbit-tag:xlog-fluentbit-tag}
      Read_from_Head  True
      db              ${directory:xlog-fluentbit}/tail-state
  [OUTPUT]
      name            forward
      match           *
      Host            ${:forward-host}
      Port            ${:forward-port}
{%- if slapparameter_dict.get('xlog_fluentbit_forward_shared_key') %}
      Shared_Key      ${:forward-shared-key}
{%- endif %}
      Self_Hostname   ${:forward-self-hostname}
      tls             on
      tls.verify      off

[xlog-fluentbit-service]
recipe  = slapos.cookbook:wrapper
fluentbit =  {{ fluent_bit_location }}/bin/fluent-bit
fluentbit-config = ${xlog-fluentbit-config:output}
command-line = ${:fluentbit} -c ${:fluentbit-config}
wrapper-path = ${directory:service}/${:_buildout_section_name_}
hash-files = ${:fluentbit-config}

[request-wendelin-telecom-shared]
<= slap-connection
recipe = slapos.cookbook:requestoptional
name = Wendelin Telecom Registration
software-url = {{ slapparameter_dict.wendelin_telecom_software_release_url }}
shared = true
config-fluentbit-tag = ${xlog-fluentbit-tag:xlog-fluentbit-tag}

[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
extra-context =
context =
  json ors false
  section directory directory
  key slap_configuration myslap:configuration
  key slapparameter_dict myslap:parameter_dict
  raw gtp_addr_v6 {{ my_ipv6 }}
  raw gtp_addr_v4 {{ lan_ipv4 }}
  raw gtp_addr_lo {{ gtp_addr_lo }}
  import  xbuildout xbuildout
  import  netaddr netaddr
  ${:extra-context}

[enb-config]
<= config-base
url = {{ enb_template }}
output = ${directory:etc}/enb.cfg
import-list =
    rawfile slaplte.jinja2 {{ slaplte_template }}
extra-context =
    import json_module json
    key iru_dict       :iru_dict
    key icell_dict     :icell_dict
    key ipeer_dict     :ipeer_dict
    key ipeercell_dict :ipeercell_dict
iru_dict       = {{ dumps(rulib.iru_dict) }}
icell_dict     = {{ dumps(rulib.icell_dict) }}
ipeer_dict     = {{ dumps(ipeer_dict) }}
ipeercell_dict = {{ dumps(ipeercell_dict) }}


[publish-connection-information]
<= monitor-publish
recipe = slapos.cookbook:publish.serialised
{%- if slapparameter_dict.get("websocket_password", "") %}
websocket_url = ws://[{{my_ipv6}}]:9001
{%- endif %}
enb-ipv6 = {{ my_ipv6 }}
enb-ipv4 = {{ lan_ipv4 }}
amarisoft-version = {{ lte_version }}
license-expiration = {{ lte_expiration }}
monitor-gadget-url = ${:monitor-base-url}/gadget/software.cfg.html
ru-list   = {{ dumps(rulib.iru_dict.keys()   | sort) }}
cell-list = {{ dumps(rulib.icell_dict.keys() | sort) }}
peer-list = {{ dumps(ipeer_dict.keys()       | sort) }}
peer-cell-list = {{ dumps(ipeercell_dict.keys() | sort) }}
fluentbit-tag = ${xlog-fluentbit-tag:xlog-fluentbit-tag}


[monitor-instance-parameter]
{% if slapparameter_dict.get("name", None) %}
monitor-title = {{ slapparameter_dict['name'] | string }}
{% endif %}
{% if slapparameter_dict.get("monitor-password", None) %}
password = {{ slapparameter_dict['monitor-password'] | string }}
{% endif %}

[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}

[check-baseband-latency.py]
<= macro.promise
promise = check_baseband_latency
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-rxtx-delay = {{ slapparameter_dict.get("min_rxtx_delay", 0) }}