[buildout]
parts =
  switch-softwaretype

eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true

[directory]
recipe = slapos.cookbook:mkdirectory
software = ${buildout:directory}
home = $${buildout:directory}
etc = $${:home}/etc

[slap-configuration]
recipe = slapos.cookbook:slapconfiguration.serialised
computer = $${slap-connection:computer-id}
partition = $${slap-connection:partition-id}
url = $${slap-connection:server-url}
key = $${slap-connection:key-file}
cert = $${slap-connection:cert-file}

[jinja2-template-base]
recipe = slapos.recipe.template:jinja2
output = $${buildout:directory}/$${:filename}
extra-context =
depends = $${activate-eggs:recipe}
context =
    import xbuildout xbuildout
    import json_module json
    import netaddr netaddr
    import nrarfcn_module nrarfcn
    import xearfcn_module xlte.earfcn
    import xnrarfcn_module xlte.nrarfcn
    key eggs_directory buildout:eggs-directory
    key develop_eggs_directory buildout:develop-eggs-directory
    raw buildout_directory ${buildout:directory}
    section directory directory
    raw pythonwitheggs ${buildout:bin-directory}/pythonwitheggs
    section slap_connection slap-connection
    key slapparameter_dict slap-configuration:configuration
    key lan_ipv4 lan:ipv4
    key mac lan:mac
    key my_ipv4  slap-configuration:ipv4-random
    key my_ipv6  slap-configuration:ipv6-random
    raw nginx_template   ${nginx_conf.in:target}
    raw nginx_mime   ${nginx-output:mime}
    raw nginx_executable ${nginx-output:nginx}
    raw openssl_executable_location ${openssl:location}/bin/openssl
    $${:extra-context}
import-list =
    rawfile slaplte.jinja2 ${slaplte.jinja2:target}
    rawfile ru_libinstance.jinja2.cfg ${ru_libinstance.jinja2.cfg:target}
    rawfile ru_sdr_libinstance.jinja2.cfg ${ru_sdr_libinstance.jinja2.cfg:target}
    rawfile ru_sunwave_libinstance.jinja2.cfg ${ru_sunwave_libinstance.jinja2.cfg:target}

# activate eggs and modules used in jinja2 templates
[activate-eggs]
recipe = slapos.recipe.build
init =
  import pkg_resources as rpkg
  buildout = self.buildout['buildout']
  env = rpkg.Environment([buildout['develop-eggs-directory'],
                          buildout['eggs-directory']])
  env.scan()
  def activate(pkgspec):
    req = rpkg.Requirement.parse(pkgspec)
    for dist in rpkg.working_set.resolve([req], env):
      rpkg.working_set.add(dist)

  activate('xlte')
  activate('nrarfcn')

  # ~ import xbuildout
  import sys, types
  def readfile(path):
    with open(path) as f:
      return f.read()
  xbuildout = types.ModuleType('xbuildout')
  exec(readfile('${ru_xbuildout.py:target}'), xbuildout.__dict__)
  assert 'xbuildout' not in sys.modules
  sys.modules['xbuildout'] = xbuildout

[sdr]
recipe = slapos.recipe.build
configuration = $${slap-configuration:configuration}
init =
  # Set SDR directory
  options['dir'] = options['configuration'].get('sdr_dir', '/opt/sdr')

[amarisoft]
recipe = slapos.recipe.build
fixed_version = 2024-11-21.1734602361
configuration = $${slap-configuration:configuration}
init =
  import os, re
  mock = options['configuration'].get('lte_mock', False)
  # Set Amarisoft directory
  options['dir'] = options['configuration'].get('amarisoft_dir', '/opt/amarisoft')
  # Get Available Amarisoft versions
  if mock:
    version_installed = [options['fixed_version']]
  else:
    version_installed = [x[1:] for x in os.listdir(options['dir']) if re.match(r"v[0-9]{4}-[0-9]{2}-[0-9]{2}.[0-9]{10}", x)]
  options['version_installed'] = ', '.join(version_installed)
  # Set Amarisoft version to use
  slapconf_version = options['configuration'].get('amarisoft_version', False)
  if slapconf_version and \
     slapconf_version in version_installed:
    options['version'] = slapconf_version
  else:
    options['version'] = options['fixed_version']
  # Set Binaries and license directories
  binary_dir  = options['dir'] + "/v" + options['version']
  options['license_dir'] = options['dir'] + '/.amarisoft'
  options['sdr_dir'] = binary_dir + '/trx_sdr'
  options['enb_dir'] = binary_dir + '/enb'
  options['mme_dir'] = binary_dir + '/mme'
  options['ims_dir'] = binary_dir + '/mme'
  options['ue_dir']  = binary_dir + '/ue'
  if options['configuration'].get('lte_mock', False):
    options['enb_dir'] = '${buildout:directory}/bin'
    options['mme_dir'] = '${buildout:directory}/bin'
    options['ims_dir'] = '${buildout:directory}/bin'
    options['ue_dir'] = '${buildout:directory}/bin'
  # Get License expiration and host IDs
  if mock:
    options.update({'lteenb_expiration': '9999-99-99', 'ltemme_expiration': '9999-99-99', 'lteue_expiration': '9999-99-99'})
    options.update({'lteenb_host_id': '00-00-00-00-00-00-00-00', 'ltemme_host_id': '00-00-00-00-00-00-00-00', 'lteue_host_id': '00-00-00-00-00-00-00-00'})
  else:
    options.update({'lteenb_expiration': 'Unknown', 'ltemme_expiration': 'Unknown', 'lteue_expiration': 'Unknown'})
    options.update({'lteenb_host_id': 'Unknown', 'ltemme_host_id': 'Unknown', 'lteue_host_id': 'Unknown'})
  try:
    for filename in os.listdir(options['license_dir']):
      if filename.endswith('.key'):
        with open(os.path.join(options['license_dir'], filename), 'r') as f:
          f.seek(260)
          for l in f:
            if l.startswith('host_id='):
              host_id = l.split('=')[1].strip()
            if l.startswith('product_id='):
              product_id = l.split('=')[1].strip()
            if l.startswith('version='):
              expiration = l.split('=')[1].strip()
          options[product_id + '_expiration'] = expiration
          options[product_id + '_host_id'] = host_id
  except FileNotFoundError:
    pass

[lan]
recipe = slapos.recipe.build
init =
  import netifaces
  for i in netifaces.interfaces():
      if not (i.startswith("slaptun") or i.startswith("slaptap") or i.startswith("re6stnet") or i == "lo"):
          a = netifaces.ifaddresses(i)
          if netifaces.AF_INET in a:
              try:
                  options['ipv4'] = a[netifaces.AF_INET][0]['addr']
              except:
                  options['ipv4'] = "0.0.0.0"
              try:
                  options['mac'] = a[netifaces.AF_LINK][0]['addr']
              except:
                  options['mac'] = "00:00:00:00:00:00"

[ors-id]
recipe = slapos.recipe.build
configuration = $${slap-configuration:configuration}
init =
  import socket
  try:
    sn = int(socket.gethostname().split('ors')[1])
  except (IndexError, ValueError):
    sn = 0
  options['serial_number'] = sn
  options['enb_id'] = "0x{:05X}".format(sn % 2**20)
  options['gnb_id'] = "0x{:05X}".format((sn + 2**19) % 2**20)
  options['enb_pci'] = sn % 504
  options['gnb_pci'] = sn % 1008
  options['enb_cell_id'] = "0x{:02X}".format(sn % 2**8)
  options['gnb_cell_id'] = "0x{:02X}".format((sn + 2**7) % 2**8)
  options['root_sequence_index'] = sn % 138

  def to_int(x):
    try:
      if '0x' in enb_id:
        return int(x, 16)
      return int(x)
    except ValueError:
      return 0
  enb_id = options['configuration'].get('enb_id', options['enb_id'])
  gnb_id = options['configuration'].get('enb_id', options['enb_id'])
  cell_id = options['configuration'].get('cell_id', options['enb_cell_id'])
  gnb_id_bits = options['configuration'].get('gnb_id_bits', 28)
  
  options['eutra_cell_id'] = \
    "0x{:07X}".format(to_int(enb_id) * 2**8                  + to_int(cell_id))
  options['nr_cell_id']    = \
    "0x{:07X}".format(to_int(gnb_id) * 2**(36 - gnb_id_bits) + to_int(cell_id))

[comp-id]
recipe = slapos.recipe.build
computer = $${slap-connection:computer-id}
title = $${slap-configuration:root-instance-title}
init =
  import socket
  options['hostname'] = socket.gethostname()
  comp_id = '_'.join(options[x] for x in ('hostname', 'computer', 'title'))
  options['comp-id'] = comp_id

[switch-softwaretype]
recipe = slapos.cookbook:switch-softwaretype
# we don't select default software type to force user to choose the correct one
enb = dynamic-template-enb:output
core-network = dynamic-template-core-network:output
ue = dynamic-template-ue:output

[dynamic-template-enb]
< = jinja2-template-base
url = ${template-enb:target}
filename = instance-enb.cfg
extensions = jinja2.ext.do
extra-context =
    raw monitor_template ${monitor2-template:output}
    section comp_id comp-id
    section slap_configuration slap-configuration
    section amarisoft amarisoft
    section ors_id ors-id
    section sdr sdr
    raw enb_template   ${enb.jinja2.cfg:target}
    raw slaplte_template ${slaplte.jinja2:target}
    raw drb_lte_template ${drb_lte.jinja2.cfg:target}
    raw drb_nr_template  ${drb_nr.jinja2.cfg:target}
    raw sib23_template   ${sib23.jinja2.asn:target}
    raw ru_amarisoft_stats_template   ${ru_amarisoft-stats.jinja2.py:target}
    raw ru_amarisoft_rf_info_template   ${ru_amarisoft-rf-info.jinja2.py:target}
    raw ru_tapsplit ${ru_tapsplit:target}
    raw netcapdo ${netcapdo:exe}
    raw openssl_location ${openssl:location}
    raw ru_dnsmasq_template  ${ru_dnsmasq.jinja2.cfg:target}
    raw dnsmasq_location ${dnsmasq:location}
    raw fluent_bit_location ${fluent-bit:location}
    raw openssh_location ${openssh:location}
    raw openssh_output_keygen ${openssh-output:keygen}

[dynamic-template-core-network]
< = jinja2-template-base
url = ${template-core-network:target}
filename = instance-core-network.cfg
extensions = jinja2.ext.do
extra-context =
    raw monitor_template ${monitor2-template:output}
    section amarisoft amarisoft
    section sdr sdr
    raw mme_template   ${mme.jinja2.cfg:target}
    raw dnsmasq_template   ${dnsmasq-core-network.jinja2.cfg:target}
    raw ims_template   ${ims.jinja2.cfg:target}
    raw ue_db_template ${ue_db.jinja2.cfg:target}
    raw mt_call_template ${mt_call_qos.jinja2.sdp:target}
    raw netcapdo ${netcapdo:exe}
    raw openssl_location ${openssl:location}
    raw nghttp2_location ${nghttp2:location}
    raw iperf3_location ${iperf3:location}
    raw dnsmasq_location ${dnsmasq:location}
    key slave_instance_list slap-configuration:slave-instance-list
    section slap_configuration slap-configuration

[dynamic-template-ue]
< = jinja2-template-base
url = ${template-ue:target}
filename = instance-ue.cfg
extensions = jinja2.ext.do
extra-context =
    section slap_configuration slap-configuration
    section amarisoft amarisoft
    section sdr sdr
    raw monitor_template ${monitor2-template:output}
    raw ue_template   ${ue.jinja2.cfg:target}
    raw slaplte_template ${slaplte.jinja2:target}
    raw openssl_location ${openssl:location}
    raw ru_amarisoft_stats_template   ${ru_amarisoft-stats.jinja2.py:target}
    raw ru_amarisoft_rf_info_template   ${ru_amarisoft-rf-info.jinja2.py:target}
    raw ru_tapsplit ${ru_tapsplit:target}
    raw netcapdo ${netcapdo:exe}
    raw ru_dnsmasq_template   ${ru_dnsmasq.jinja2.cfg:target}
    raw dnsmasq_location ${dnsmasq:location}
    raw openssh_location ${openssh:location}
    raw openssh_output_keygen ${openssh-output:keygen}
