{#- Package ru/lopcomm/libinstance provides instance code for handling Lopcomm ORAN Radio Units. #}

{%- macro buildout_iru(iru, icell_list)  %}
{%-  set ru_ref = J(jref_of_shared(iru))  %}
{%-  set ru     = iru['_']  %}

{%-  if len(icell_list) != 1  %}
{%-    do ierror(iru, 'ru/lopcomm supports only 1 cell  ; requested %d' % len(icell_list))  %}
{%-  endif %}
{%-  set icell = icell_list[0]  %}
{%-  set cell  = icell['_']  %}

{#-  indicate whether RU is listening for netconf #}

{%- if not testing  %}
{{ promise('%s-netconf-socket' % ru_ref) }}
promise = check_socket_listening
config-host = ${vtap.{{ru.cpri_link._tap}}:gateway}
config-port = 830
{%- endif %}


{#-  push firmware to RU #}

{{ part('%s-software-template' % ru_ref) }}
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
_logbase = ${directory:var}/log/{{B('%s-software' % ru_ref)}}
log-output = ${:_logbase}.log
software-reply-json-log-output = ${:_logbase}-reply.json.log
remote-file-path = sftp://${user-info:pw-name}@[${sshd-service:ipv6}]:${sshd-service:port}{{ru_lopcomm_firmware_path}}
is_firmware_updated = ${directory:etc}/{{B('%s.is_firmware_updated' % ru_ref)}}
context =
  section directory directory
  section vtap vtap.{{ ru.cpri_link._tap }}
  key slapparameter_dict myslap:parameter_dict
  key log_file :log-output
  key software_reply_json_log_file :software-reply-json-log-output
  key remote_file_path :remote-file-path
  raw testing {{ testing }}
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
  raw buildout_directory_path {{ buildout_directory }}
  key is_firmware_updated :is_firmware_updated
  raw firmware_name {{ru_lopcomm_firmware_filename}}
  import netaddr netaddr
mode = 0775
url = {{ ru_lopcomm_software_template }}
output = ${directory:script}/{{B('%s-software.py' % ru_ref)}}

{%- if not testing  %}
{{ promise('%s-firmware' % ru_ref) }}
promise = check_command_execute
config-command = [ -f ${ {{-B('%s-software-template' % ru_ref)}}:is_firmware_updated} ]
{%- endif %}


{#-  push config to RU #}

{%   if ru.get("cu_config_link", None) %}
[{{ B('%s-cu-config-dl' % ru_ref) }}]
recipe = slapos.recipe.build:download
url = {{ ru.cu_config_link }}
version = {{ ru.get("cu_config_version") }}
offline = false
{%   endif %}

[{{ B('%s-cu-config' % ru_ref) }}]
<= config-base
{% if ru.get("cu_config_link", None) %}
url = ${ {{-B('%s-cu-config-dl' % ru_ref)}}:target}
{% else %}
url = {{ ru_lopcomm_cu_config_template }}
{% endif %}
output = ${directory:etc}/{{B('%s-cu_config.xml' % ru_ref)}}
extra-context =
  import xearfcn_module  xlte.earfcn
  import xnrarfcn_module xlte.nrarfcn
  key ru   :ru
  key cell :cell
ru   = {{ dumps(ru) }}
cell = {{ dumps(cell) }}

[{{ B('%s-cu-inactive-config' % ru_ref) }}]
<= config-base
url = {{ ru_lopcomm_cu_inactive_config_template }}
output = ${directory:etc}/{{B('%s-cu_inactive_config.xml' % ru_ref)}}
extra-context =
  import xearfcn_module  xlte.earfcn
  import xnrarfcn_module xlte.nrarfcn
  key ru   :ru
  key cell :cell
ru   = {{ dumps(ru) }}
cell = {{ dumps(cell) }}

[{{ B('%s-config-template' % ru_ref) }}]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/{{B('%s-config.log' % ru_ref)}}
context =
  section directory directory
  section vtap vtap.{{ ru.cpri_link._tap }}
  key log_file :log-output
  raw testing {{ testing }}
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
  raw buildout_directory_path {{ buildout_directory }}
  raw CreateProcessingEle_template {{ ru_lopcomm_CreateProcessingEle_template }}
  key cu_config_template {{B('%s-cu-config' % ru_ref)}}:output
  key cu_inactive_config_template {{B('%s-cu-inactive-config' % ru_ref)}}:output
  import netaddr netaddr
mode = 0775
url = {{ ru_lopcomm_config_template }}
output = ${directory:script}/{{B('%s-config.py' % ru_ref)}}

{{ promise('%s-config-log' % ru_ref) }}
promise = check_lopcomm_config_log
config-config-log = ${ {{-B('%s-config-template' % ru_ref)}}:log-output}


{#-  handle notifications from RU + keep on touching RU watchdog #}

[{{ B('%s-stats-template' % ru_ref) }}]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
_logbase = ${directory:var}/log/{{B('%s' % ru_ref)}}
log-output = ${:_logbase}-stats.log
json-log-output = ${:_logbase}-stats.json.log
cfg-json-log-output = ${:_logbase}-config.json.log
supervision-json-log-output = ${:_logbase}-supervision.json.log
ncsession-json-log-output = ${:_logbase}-ncsession.json.log
software-json-log-output = ${:_logbase}-software.json.log
supervision-reply-json-log-output = ${:_logbase}-supervision-reply.json.log
is_netconf_connected = ${directory:etc}/{{B('%s.is_netconf_connected' % ru_ref)}}
context =
  section directory directory
  section vtap vtap.{{ ru.cpri_link._tap }}
  key slapparameter_dict myslap:parameter_dict
  key log_file :log-output
  key json_log_file :json-log-output
  key cfg_json_log_file :cfg-json-log-output
  key supervision_json_log_file :supervision-json-log-output
  key supervision_reply_json_log_file :supervision-reply-json-log-output
  key is_netconf_connected :is_netconf_connected
  key ncsession_json_log_file :ncsession-json-log-output
  key software_json_log_file :software-json-log-output
  raw testing {{ testing }}
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
  raw buildout_directory_path {{ buildout_directory }}
  import netaddr netaddr
mode = 0775
url = {{ ru_lopcomm_stats_template }}
output = ${directory:bin}/{{B('%s-stats.py' % ru_ref)}}

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

{%- if not testing  %}
{{ promise('%s-netconf-connection' % ru_ref) }}
promise = check_command_execute
config-command = [ -f ${ {{-B('%s-stats-template' % ru_ref)}}:is_netconf_connected} ]
{%- endif %}

{{ promise('%s-vswr' % ru_ref) }}
promise = check_lopcomm_vswr
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-rssi' % ru_ref) }}
promise = check_lopcomm_rssi
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-pa-current' % ru_ref) }}
promise = check_lopcomm_pa_current
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-pa-output-power' % ru_ref) }}
promise = check_lopcomm_pa_output_power
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-sync' % ru_ref) }}
promise = check_lopcomm_sync
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-lof' % ru_ref) }}
promise = check_lopcomm_lof
config-netconf-log = ${ {{-B('%s-stats-template' % ru_ref)}}:json-log-output}

{{ promise('%s-stats-log' % ru_ref) }}
promise = check_lopcomm_stats_log
config-stats-log = ${ {{-B('%s-stats-template' % ru_ref)}}:log-output}


{#-  reset RU periodically #}

{%- if ru.get("reset_schedule") %}

[{{ B('%s-reset-info-template' % ru_ref) }}]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
_logbase = ${directory:var}/log/{{B('%s-reset-info' % ru_ref)}}
log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log
context =
  section vtap vtap.{{ ru.cpri_link._tap }}
  key log_file :log-output
  key json_log_file :json-log-output
  raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
  raw testing {{ testing }}
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
  import netaddr netaddr
mode = 0775
url = {{ ru_lopcomm_reset_info_template }}
output = ${directory:bin}/{{B('%s-reset-info.py' % ru_ref)}}

[{{ B('%s-reset-template' % ru_ref) }}]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
_logbase = ${directory:var}/log/{{B('%s-reset' % ru_ref)}}
log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log
context =
  section vtap vtap.{{ ru.cpri_link._tap }}
  key log_file :log-output
  raw python_path {{ buildout_directory}}/bin/pythonwitheggs
  raw buildout_directory_path {{ buildout_directory }}
  import netaddr netaddr
mode = 0775
url = {{ ru_lopcomm_reset_template }}
output = ${directory:etc}/{{B('%s-reset.py' % ru_ref)}}

{{ part('%s-reset-cron' % ru_ref) }}
recipe = slapos.cookbook:cron.d
cron-entries = ${cron:cron-entries}
name = {{B('%s-reset' % ru_ref)}}
frequency = {{ ru.reset_schedule }}
command = {{ buildout_directory}}/bin/pythonwitheggs ${ {{-B('%s-reset-template' % ru_ref)}}:output}

{{ part('%s-reset-info-service' % ru_ref) }}
recipe = slapos.cookbook:wrapper
command-line = ${ {{-B('%s-reset-info-template' % ru_ref)}}:output}
wrapper-path = ${directory:service}/{{B('%s-reset-info' % ru_ref)}}
mode = 0775
hash-files =
  ${:command-line}

{%- endif %}


{#- amend RU-published information with Lopcomm-specific bits #}
[{{ B('ipublish-%s' % ru_ref) }}]
bbu-ssh-command = ssh ${user-info:pw-name}@${sshd-service:ipv6} -p ${sshd-service:port}
bbu-ssh-url = ssh://${user-info:pw-name}@[${sshd-service:ipv6}]:${sshd-service:port}
firmware = {{ru_lopcomm_firmware_filename}}

{%- endmacro  %}


{%- macro buildout()  %}

# deploy openssh-server for software upgrade
#
# FIXME user-authorized-key is global for eNB. Either we need to put SSH server
# to be also global, or unroll an SSH server via paramiko inside
# ru/lopcomm/software.py just to handle software upgrades there.
[user-info]
recipe = slapos.cookbook:userinfo

[sshd-port]
recipe = slapos.cookbook:free_port
minimum = 22222
maximum = 22231
ip = {{my_ipv6}}

[sshd-config]
recipe = slapos.recipe.template:jinja2
output = ${directory:etc}/sshd.conf
path_pid = ${directory:run}/sshd.pid
inline =
  PidFile ${:path_pid}
  Port ${sshd-port:port}
  ListenAddress ${sshd-port:ip}
  Protocol 2
  HostKey ${sshd-ssh-host-rsa-key:output}
  HostKey ${sshd-ssh-host-ecdsa-key:output}
  PasswordAuthentication no
  PubkeyAuthentication yes
  HostKeyAlgorithms ssh-rsa,ssh-dss,rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521
  AuthorizedKeysFile ${buildout:directory}/.ssh/authorized_keys
  Subsystem sftp {{ openssh_location }}/libexec/sftp-server

{{ part('sshd-service') }}
recipe = slapos.cookbook:wrapper
command-line = {{ openssh_location }}/sbin/sshd -D -e -f ${sshd-config:output}
wrapper-path = ${directory:service}/sshd
hash-files = ${sshd-config:output}
environment =
  HOME=${directory:home}
ipv6 = ${sshd-port:ip}
port = ${sshd-port:port}

{{ part('sshd-add-authorized-key') }}
recipe = slapos.cookbook:dropbear.add_authorized_key
home = ${buildout:directory}
key = {{ slapparameter_dict.get("user-authorized-key", '') }}

[sshd-ssh-keygen-base]
recipe = plone.recipe.command
output = ${directory:etc}/${:_buildout_section_name_}
command = {{ openssh_output_keygen }} -f ${:output} -N '' ${:extra-args}

[sshd-ssh-host-rsa-key]
<=sshd-ssh-keygen-base
extra-args=-t rsa
[sshd-ssh-host-ecdsa-key]
<=sshd-ssh-keygen-base
extra-args=-t ecdsa -b 521

{{ promise('sshd') }}
promise = check_socket_listening
config-host = ${sshd-service:ipv6}
config-port = ${sshd-service:port}

{%- endmacro  %}
