Commit e78eea3f authored by Kirill Smelkov's avatar Kirill Smelkov

software/ors-amarisoft: enb: Start to generalize the code to handle multiple Radio Units

Add code to organize a registry of Radio Units and handle that registry generally everywhere.

RU registry is still populated from cell_list and in practice there still can
be radio units only of the same type, but besides slaplte.load_ru_and_cell
which is aware of that, the rest of the code tries to do everything as if
RUs of different type could be present simultaneously.

Now it is not only lopcomm who can do multiCELL, but also sunwave and SDR as well.

Later we will change the loading to load RU descriptions from shared instances
and they won't be constrained to be Radio Units of the same type. But we need
to prepare a lot to be able to do that.

One more step forward towards MultiRU.

Tests will be added later as full tests for generic MultiRU.

--------

Appendix. Diff for rendered enb.cfg and gnb.cfg before and after this patch:

```
$ ./pythonwitheggs slapos-render-config.py && git diff -w --no-index config/{old,out}
```

```diff
diff --git a/config/old/enb.cfg b/config/out/enb.cfg
index 884483b0a..cafdf42be 100644
--- a/config/old/enb.cfg
+++ b/config/out/enb.cfg
@@ -1,24 +1,22 @@

-
 {
   log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,s1ap.level=debug,s1ap.max_size=1,x2ap.level=debug,x2ap.max_size=1,rrc.level=debug,rrc.max_size=1,ngap.level=debug,ngap.max_size=1,xnap.level=debug,xnap.max_size=1,phy.level=info,file.rotate=1G,file.path=/dev/null",
   log_filename: "log/enb.log",

-
+  // Radio Units
   rf_driver: {
+      // default-RU 2T2R  (ors)
       name: "sdr",
       args: "dev0=/dev/sdr0",
-
       rx_antenna:"tx_rx",
       tdd_tx_mod: 1,
   },
-  tx_gain: 62,
-  rx_gain: 43,
-
+  tx_gain: [62, 62],
+  rx_gain: [43, 43],
   com_addr: "127.0.1.2:9001",
   // LTE core network
   mme_list: [
@@ -36,6 +34,8 @@

   // LTE cells
   cell_list: [
+
+    // default  (default-RU)
     {
       rf_port:      0,
       n_antenna_dl: 2,
diff --git a/config/old/gnb.cfg b/config/out/gnb.cfg
index fd57ca3dc..7818b4ea8 100644
--- a/config/old/gnb.cfg
+++ b/config/out/gnb.cfg
@@ -1,24 +1,22 @@

-
 {
   log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,s1ap.level=debug,s1ap.max_size=1,x2ap.level=debug,x2ap.max_size=1,rrc.level=debug,rrc.max_size=1,ngap.level=debug,ngap.max_size=1,xnap.level=debug,xnap.max_size=1,phy.level=info,file.rotate=1G,file.path=/dev/null",
   log_filename: "log/enb.log",

-
+  // Radio Units
   rf_driver: {
+      // default-RU 2T2R  (ors)
       name: "sdr",
       args: "dev0=/dev/sdr0",
-
       rx_antenna:"tx_rx",
       tdd_tx_mod: 1,
   },
-  tx_gain: 62,
-  rx_gain: 43,
-
+  tx_gain: [62, 62],
+  rx_gain: [43, 43],
   com_addr: "127.0.1.2:9001",
   // NR core network
   amf_list: [
```
parent 907d3890
......@@ -6,8 +6,9 @@
{#- for standalone testing via slapos-render-config.py
NOTE: keep in sync with ru/libinstance.jinja2.cfg #}
{%- if _standalone is defined %}
{%- set ru_dict = {} %}
{%- set cell_dict = {} %}
{%- do slaplte.load_cell(cell_dict) %}
{%- do slaplte.load_ru_and_cell(ru_dict, cell_dict) %}
{%- endif %}
......@@ -16,13 +17,6 @@
{%- do assert(not (do_lte and do_nr)) %}
{#- ru is namespace object that holds Radio Unit related parameters #}
{%- set ru = namespace(ru_type=ru_type) %}
{%- if ru.ru_type == 'm2ru' %}
{%- do ru.update({'ru_type': 'sunwave'}) %}
{%- endif %}
{#- handover_config emits handover configuration #}
{%- macro handover_config() %}
ncell_list: [
......@@ -87,12 +81,6 @@
{%- endif %}
{%- endif %}
{%- set ru.n_antenna_dl = slapparameter_dict.get('n_antenna_dl', int(default_n_antenna_dl)) %}
{% if ru.ru_type == "sunwave" %}
{%- set ru.n_antenna_ul = slapparameter_dict.get('n_antenna_ul', 1) %}
{% else %}
{%- set ru.n_antenna_ul = slapparameter_dict.get('n_antenna_ul', int(default_n_antenna_ul)) %}
{% endif %}
{
log_options: "all.level=error,all.max_size=0,nas.level=debug,nas.max_size=1,s1ap.level=debug,s1ap.max_size=1,x2ap.level=debug,x2ap.max_size=1,rrc.level=debug,rrc.max_size=1,ngap.level=debug,ngap.max_size=1,xnap.level=debug,xnap.max_size=1,
......@@ -105,7 +93,7 @@
log_filename: "{{ directory['log'] }}/enb.log",
{# instantiate radio units #}
{{ slaplte.ru_config(ru, cell_dict, slapparameter_dict) }}
{{ slaplte.ru_config(ru_dict, slapparameter_dict) }}
{%- if slapparameter_dict.get('websocket_password', '') %}
com_addr: "[{{ gtp_addr_v6 }}]:{{ slap_configuration['configuration.com_ws_port'] }}",
......@@ -194,17 +182,21 @@
// LTE cells
cell_list: [
{%- if do_lte %}
{%- for i, k in enumerate(cell_dict) %}
{%- for i, (cell_ref, cell) in enumerate(cell_dict|dictsort) %}
{%- set ru_ref = cell.ru_ref %}
{%- set ru = ru_dict[ru_ref] %}
// {{ cell_ref }} ({{ ru_ref }})
{
rf_port: {{ i }},
rf_port: {{ ru._rf_port }},
n_antenna_dl: {{ ru.n_antenna_dl }},
n_antenna_ul: {{ ru.n_antenna_ul }},
cell_id: {{ cell_dict[k].get('cell_id', slapparameter_dict.get('cell_id', '0x0' + i|string)) }},
tac: {{ cell_dict[k].get('tac', slapparameter_dict.get('tac', '0x0001')) }},
n_id_cell: {{ cell_dict[k].get('pci', slapparameter_dict.get('pci', i)) }},
root_sequence_index: {{ cell_dict[k].get('root_sequence_index', slapparameter_dict.get('root_sequence_index', 204 + i)) }},
dl_earfcn: {{ cell_dict[k].get('dl_earfcn', slapparameter_dict.get('dl_earfcn', earfcn)) }},
cell_id: {{ cell.get('cell_id', slapparameter_dict.get('cell_id', '0x0' + i|string)) }},
tac: {{ cell.get('tac', slapparameter_dict.get('tac', '0x0001')) }},
n_id_cell: {{ cell.get('pci', slapparameter_dict.get('pci', i)) }},
root_sequence_index: {{ cell.get('root_sequence_index', slapparameter_dict.get('root_sequence_index', 204 + i)) }},
dl_earfcn: {{ cell.get('dl_earfcn', slapparameter_dict.get('dl_earfcn', earfcn)) }},
inactivity_timer: {{ slapparameter_dict.get('inactivity_timer', default_lte_inactivity_timer) }},
// Handover
......@@ -417,10 +409,12 @@
{%- endif %}
{% if do_nr %}
{%- do assert(len(ru_dict) == 1, 'MultiCELL/MultiRU is TODO for NR') %}
{%- set ru = list(ru_dict.values())[0] %}
// NR cells
nr_cell_list: [
{
rf_port: 0,
rf_port: {{ ru._rf_port }},
n_antenna_dl: {{ ru.n_antenna_dl }},
n_antenna_ul: {{ ru.n_antenna_ul }},
......@@ -442,9 +436,9 @@
{%- if ors %}
manual_ref_signal_power: true,
{%- if ors['one-watt'] %}
ss_pbch_block_power: {{ (tx_gain | int) - 54 }},
ss_pbch_block_power: {{ ru.tx_gain - 54 }},
{%- else %}
ss_pbch_block_power: {{ (tx_gain | int) - 35 }},
ss_pbch_block_power: {{ ru.tx_gain - 35 }},
{%- endif -%}
{%- endif %}
......
......@@ -201,8 +201,6 @@ context =
raw nr_band {{ RF.nr_band }}
raw software_name {{ software_name }}
raw rf_mode {{ rf_mode }}
raw trx {{ trx }}
raw bbu {{ bbu }}
raw ru_type {{ ru_type }}
raw default_lte_bandwidth {{ default_lte_bandwidth }}
raw default_lte_inactivity_timer {{ default_lte_inactivity_timer }}
......@@ -240,6 +238,7 @@ url = {{ enb_template }}
output = ${directory:etc}/enb.cfg
extra-context =
import json_module json
json ru_dict {{ rulib.ru_dict | tojson }}
json cell_dict {{ rulib.cell_dict | tojson }}
key sib23_file sib-config:output
key drb_file drb-config:output
......
......@@ -36,8 +36,6 @@ context =
key lan_ipv4 lan-ip:ipv4
raw rf_mode ${rf-mode:rf-mode}
raw software_name ${rf-mode:software-name}
raw trx ${rf-mode:trx}
raw bbu ${rf-mode:bbu}
raw ru_type ${rf-mode:ru}
$${:extra-context}
import-list =
......
dhcp-leasefile={{ directory['etc'] }}/dnsmasq.leases
port=5354
{%- for (cell_ref, cell) in cell_dict|dictsort | selectattr('1._tap', 'defined') %}
{%- set ru_tap = cell._tap %}
{%- for (ru_ref, ru) in ru_dict|dictsort | selectattr('1.cpri_link', 'defined') %}
{%- set ru_tap = ru.cpri_link._tap %}
{%- set vtap = json_module.loads(vtap_jdict[ru_tap]) %}
{%- set plen = netaddr.IPNetwork(vtap.network).prefixlen %}
# {{ cell_ref }} @ {{ ru_tap }}
# {{ ru_ref }} @ {{ ru_tap }}
{#- TODO consider using /128 as we give only 1 address to RU #}
dhcp-range=tag:{{ ru_tap }},{{ vtap.gateway }},{{ vtap.gateway }},static,{{ max(plen,64) }},5m
dhcp-host={{ cell.ru_mac_addr }},tag:{{ ru_tap }},[{{ vtap.gateway }}]
dhcp-host={{ ru.mac_addr }},tag:{{ ru_tap }},[{{ vtap.gateway }}]
# option 17 used for RU callhome
# dhcp-option=option6:17,[{{ vtap.addr }}]
{%- endfor %}
......
......@@ -10,9 +10,13 @@
NOTE: driver-specific logic is implemented in rudrv .buildout_ru() and .buildout() .
#}
{#- cell_dict keeps cell registry #}
{#- ru_dict and cell_dict keep RU and cell registries
ru_dict: reference -> ru
cell_dict: reference -> cell
#}
{%- set ru_dict = {} %}
{%- set cell_dict = {} %}
{%- do slaplte.load_cell(cell_dict) %}
{%- do slaplte.load_ru_and_cell(ru_dict, cell_dict) %}
{%- macro buildout() %}
......@@ -43,14 +47,14 @@ config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
sunwave=rudrv_sunwave) %}
{%- set rudrv_init = {} %}
{#- split slapos tap interface for each RU
{#- split slapos tap interface for each RU that needs its own tap.
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. #}
{%- set ntap = len(list(cell_dict|dictsort)) %}
{%- set ntap = len(list(ru_dict|dictsort | selectattr('1.cpri_link', 'defined'))) %}
{%- set vtap_list = [] %}
[vtap]
recipe = plone.recipe.command
......@@ -132,7 +136,7 @@ init =
{#- provide CPRI-based RUs IP address via DHCP #}
{%- if trx == 'cpri' %}
{%- if ntap > 0 %}
[dnsmasq-config]
recipe = slapos.recipe.template:jinja2
url = {{ru_dnsmasq_template}}
......@@ -144,7 +148,7 @@ context =
import netaddr netaddr
section directory directory
section vtap_jdict vtap_jdict
json cell_dict {{ cell_dict | tojson }}
json ru_dict {{ ru_dict | tojson }}
{{ part('dnsmasq-service') }}
recipe = slapos.cookbook:wrapper
......@@ -165,31 +169,27 @@ hash-files =
{#- go through all RUs and for each RU emit generic promises and invoke
RU-specific buildout handler #}
{%- set ru_type = {'lopcomm': 'lopcomm', 'm2ru': 'sunwave'}.get(ru, 'sdr') %}
{%- set rudrv = rudrv_dict[ru_type] %}
{%- for i, (cell_ref, cell) in enumerate(cell_dict|dictsort) %}
{%- set ru_ref = cell_ref if cell_ref != 'default' else 'RU' %}
{%- set n_antenna_ul = int(slapparameter_dict.get('n_antenna_ul', default_n_antenna_ul)) %}
# {{ ru_ref }} ({{ ru_type}})
{%- if trx == 'sdr' %}
{%- for ru_ref, ru in ru_dict|dictsort %}
# {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R ({{ ru.ru_type}})
{%- if ru.ru_link_type == 'sdr' %}
{{ promise('%s-sdr-busy' % ru_ref) }}
promise = check_sdr_busy
config-sdr = {{ sdr }}
config-sdr_dev = 0
config-sdr_dev = {{ ru.sdr_dev }}
config-dma_chan = 0
{%- elif trx == 'cpri' %}
{%- elif ru.ru_link_type == 'cpri' %}
{{ promise('%s-sdr-busy' % ru_ref) }}
promise = check_sdr_busy
config-sdr = {{ sdr }}
config-sdr_dev = {{ slapparameter_dict.get('sdr_number', 0) }}
config-dma_chan = {{ cell.cpri_port_number }}
config-sdr_dev = {{ ru.cpri_link.sdr_dev }}
config-dma_chan = {{ ru.cpri_link.sfp_port }}
{{ promise('%s-cpri-lock' % ru_ref) }}
promise = check_cpri_lock
config-sdr_dev = {{ slapparameter_dict.get('sdr_number', 0) }}
config-sfp_port = {{ cell.cpri_port_number }}
config-sdr_dev = {{ ru.cpri_link.sdr_dev }}
config-sfp_port = {{ ru.cpri_link.sfp_port }}
config-amarisoft-rf-info-log = ${ru_amarisoft-rf-info-template:log-output}
{%- else %}
......@@ -198,16 +198,17 @@ config-amarisoft-rf-info-log = ${ru_amarisoft-rf-info-template:log-output}
{{ promise('%s-rx-saturated' % ru_ref) }}
promise = check_rx_saturated
config-rf-rx-chan-list = {{ list(range(i*n_antenna_ul, (i+1)*n_antenna_ul)) }}
config-rf-rx-chan-list = {{ list(range(ru._rf_chan_rx, ru._rf_chan_rx + ru.n_antenna_ul)) }}
config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
{#- driver-specific part #}
{%- if not rudrv_init.get(ru_type) %}
{%- set rudrv = rudrv_dict[ru.ru_type] %}
{%- if not rudrv_init.get(ru.ru_type) %}
{{ rudrv.buildout() }}
{%- do rudrv_init.update({ru_type: 1}) %}
{%- do rudrv_init.update({ru.ru_type: 1}) %}
{%- endif %}
{{ rudrv.buildout_ru(ru_ref, cell) }}
{{ rudrv.buildout_ru(ru_ref, ru) }}
{%- endfor %}
{#- retrieve rf and stats[rf,samples] data from amarisoft service for promises
......
<xc:config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
{%- set n_antenna_dl = slapparameter_dict.get('n_antenna_dl', int(default_n_antenna_dl)) %}
{%- set n_antenna_ul = slapparameter_dict.get('n_antenna_ul', int(default_n_antenna_ul)) %}
<user-plane-configuration xc:operation="replace" xmlns="urn:o-ran:uplane-conf-option8:1.0">
<!-- TX path: eaxcid → TxEndpoint
......@@ -11,7 +9,7 @@
(static TxEndpoint, TxArray and their association are defined by RU itself)
-->
{%- set TxCarrier = 'TXA0CC00' %}
{%- for ant in range(n_antenna_dl) %}
{%- for ant in range(ru.n_antenna_dl) %}
{%- set port = ant // 2 %}
{%- set chan = ant % 2 %}
{%- set txep = 'TXA0P%02dC%02d' % (port, chan) %}
......@@ -47,7 +45,7 @@
(static RxEndpoint, RxArray and their association are defined by RU itself)
-->
{%- set RxCarrier = 'RXA0CC00' %}
{%- for ant in range(n_antenna_ul) %}
{%- for ant in range(ru.n_antenna_ul) %}
{%- set port = ant // 2 %}
{%- set chan = ant % 2 %}
{%- set rxep = 'RXA0P%02dC%02d' % (port, chan) %}
......
{#- Package ru/lopcomm/libinstance provides instance code for handling Lopcomm ORAN Radio Units. #}
{%- macro buildout_ru(ru_ref, cell) %}
{%- macro buildout_ru(ru_ref, ru) %}
{#- indicate whether RU is listening for netconf #}
{%- if not testing %}
{{ promise('%s-netconf-socket' % ru_ref) }}
promise = check_socket_listening
config-host = ${vtap.{{cell._tap}}:gateway}
config-host = ${vtap.{{ru.cpri_link._tap}}:gateway}
config-port = 830
{%- endif %}
......@@ -24,7 +24,7 @@ remote-file-path = sftp://${user-info:pw-name}@[${sshd-service:ipv6}]:${sshd-ser
is_firmware_updated = ${directory:etc}/{{ru_ref}}.is_firmware_updated
context =
section directory directory
section vtap vtap.{{ cell._tap }}
section vtap vtap.{{ ru.cpri_link._tap }}
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
key software_reply_json_log_file :software-reply-json-log-output
......@@ -64,6 +64,8 @@ url = ${cu-config-dl:target}
url = {{ ru_lopcomm_cu_config_template }}
{% endif %}
output = ${directory:etc}/{{ru_ref}}-cu_config.xml
extra-context =
json ru {{ ru | tojson }}
[{{ru_ref}}-config-template]
recipe = slapos.recipe.template:jinja2
......@@ -71,7 +73,7 @@ extensions = jinja2.ext.do
log-output = ${directory:var}/log/{{ru_ref}}-config.log
context =
section directory directory
section vtap vtap.{{ cell._tap }}
section vtap vtap.{{ ru.cpri_link._tap }}
key log_file :log-output
raw testing {{ testing }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
......@@ -104,7 +106,7 @@ supervision-reply-json-log-output = ${:_logbase}-supervision-reply.json.log
is_netconf_connected = ${directory:etc}/{{ru_ref}}.is_netconf_connected
context =
section directory directory
section vtap vtap.{{ cell._tap }}
section vtap vtap.{{ ru.cpri_link._tap }}
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
key json_log_file :json-log-output
......@@ -176,7 +178,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset-info
log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log
context =
section vtap vtap.{{ cell._tap }}
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) }}
......@@ -194,7 +196,7 @@ _logbase = ${directory:var}/log/{{ru_ref}}-reset
log-output = ${:_logbase}.log
json-log-output = ${:_logbase}.json.log
context =
section vtap vtap.{{ cell._tap }}
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 }}
......
{#- Package ru/sdr/libinstance provides instance code for handling SDR Radio Units. #}
{%- macro buildout_ru(ru_ref, cell) %}
{%- macro buildout_ru(ru_ref, ru) %}
{#- nothing SDR-specific #}
{%- endmacro %}
......
{#- Package ru/sunwave/libinstance provides instance code for handling SunWave Radio Units. #}
{%- macro buildout_ru(ru_ref, cell) %}
{%- macro buildout_ru(ru_ref, ru) %}
{#- nothing SunWave-specific #}
{%- endmacro %}
......
This diff is collapsed.
......@@ -56,8 +56,6 @@ def do(src, out, rat, slapparameter_dict):
"rf_mode": "tdd",
"do_lte": %(jdo_lte)s,
"do_nr": %(jdo_nr)s,
"trx": "sdr",
"bbu": "ors",
"ru_type": "ors",
"ors": {"one-watt": true},
"earfcn": 36100,
......
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = fdd-lopcomm
rf-mode = fdd
trx = cpri
bbu = server
ru = lopcomm
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = fdd-ors
rf-mode = fdd
trx = sdr
bbu = ors
ru = ors
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = fdd
rf-mode = fdd
trx = sdr
bbu = server
ru = any
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = tdd-m2ru
rf-mode = tdd
trx = cpri
bbu = server
ru = m2ru
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = tdd-ors
rf-mode = tdd
trx = sdr
bbu = ors
ru = ors
......@@ -21,6 +21,4 @@ default-n-antenna-ul = 2
[rf-mode]
software-name = tdd
rf-mode = tdd
trx = sdr
bbu = server
ru = any
......@@ -25,6 +25,4 @@ default-n-antenna-ul = {{ default_n_antenna_ul }}
[rf-mode]
software-name = {{ software_name }}
rf-mode = {{ rf_mode }}
trx = {{ trx }}
bbu = {{ bbu }}
ru = {{ ru }}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment