Commit 387a69a7 authored by Kirill Smelkov's avatar Kirill Smelkov

software/ors-amarisoft: enb/generic: Switch configuration of RU, cell, peer...

software/ors-amarisoft: enb/generic: Switch configuration of RU, cell, peer and peercell to shared instances

Previously we had cell_list, ncell_list and peers parameters and we now remove
them and rework the software release to accept configuration for said objects
via shared instances. In other words now it is possible to add Radio Units,
cells, peers and peer cells on on top of main eNB service.

Schema for parameters of those shared instances is based on what we just recently
generalized and established in ru/ cell/ peer/ and peer/cell/ input schemas. We
only add cell_kind=enb|enb_peer addition field to be able to distinguish a cell
from peercell object, and we add a way for cell to reference RU one way or another.

RU-CELL relation is no longer 1-1: one RU can be generally serving multiple
cells. For example transmission bandwidth of SDR100 board is ~ 100MHz while
bandwidth of one LTE cell is max 20MHz. This way it is possible to put several
cells whose frequencies are nearby each other to be run on the same SDR
board(*). And in general one Radio Unit can be serving transmission/reception in
multiple frequency ranges, thus providing ability for multiple cells to be
served by RU. This way RU is split off from CELL object and each CELL needs to
say which RU object it wants to use.

There are 3 ways to configure CELL->RU links:

1. CELL references a RU object. This is the most general.
2. CELL embeds definition of RU object. This variant is provided for simplicity
   of management for users that do not want to split CELL/RU relations.
3. CELL2 references another cell object and says to use the same RU as CELL1.
   Again, this variant is provided for simplicity of management when users want to
   add a cell on top of same RU when they already started with "2".

Full backward compatibility is provided for ORS. For this instance-ors-enb.jinja2.cfg
is adjusted to inject synthetic RU/CELL/PEER/PEERCELL shared instances instead
of translating to slapparameter_dict. As the result for ORS users it works
without visible change as parameters with original ORS schema are handled as expected.

Rendered files for ORS also stay practically the same as before this patch.

The main change is in slaplte.jinja2 - in the loading routines. The other
changes are mostly straightforward adaptation because details of how ru_dict,
cell_dict, peer_dict and peercell_dict are loaded were already localized to
slaplte and the rest of the code independent from that.

Now, once we switched to shared instances, we will be finally able to add tests
for updated enb. Please see a soon follow-up patch for that.

And for existing test_ors.py we also temporary workaround breakage of tests,
because currently generic code does not handle well special characters in
partition references. For example it currently breaks on spaces with buildout
complaining that sections with spaces in their name are invalid. We will fix
that in another soon-followup patch as well, but apply a workaround for now.

(*) see https://tech-academy.amarisoft.com/SDR_MultiCell_OneSdr.html for details.

--------

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

```
$ ./pythonwitheggs slapos-render-config.py && xdiff config/{old,out}
```

```diff
diff --git a/config/old/ors/enb/enb.cfg b/config/out/ors/enb/enb.cfg
index 15f3b68e9..6046d366e 100644
--- a/config/old/ors/enb/enb.cfg
+++ b/config/out/ors/enb/enb.cfg
@@ -6,7 +6,7 @@

   // Radio Units
   rf_driver: {
-      // CELL-RU 2T2R  (sdr)
+      // RU 2T2R  (sdr)
       name: "sdr",
       args: "dev0=/dev/sdr0",
       rx_antenna:"tx_rx",
@@ -28,7 +28,7 @@
   // LTE cells
   cell_list: [

-    // CELL  (CELL-RU)
+    // CELL  (RU)
     {
       rf_port:      0,
       n_antenna_dl: 2,
@@ -46,7 +46,7 @@
         // Inter-ENB HO
         {
           rat:          "eutra",
-          cell_id:      0x12345,  // -> 1
+          cell_id:      0x12345,  // -> PEERCELL1
           n_id_cell:    35,
           dl_earfcn:    700,
           tac:          123,
diff --git a/config/old/ors/gnb/enb.cfg b/config/out/ors/gnb/enb.cfg
index ac564db6c..9473f3207 100644
--- a/config/old/ors/gnb/enb.cfg
+++ b/config/out/ors/gnb/enb.cfg
@@ -6,7 +6,7 @@

   // Radio Units
   rf_driver: {
-      // CELL-RU 2T2R  (sdr)
+      // RU 2T2R  (sdr)
       name: "sdr",
       args: "dev0=/dev/sdr0",
       rx_antenna:"tx_rx",
@@ -35,7 +35,7 @@
   // NR cells
   nr_cell_list: [

-      // CELL  (CELL-RU)
+      // CELL  (RU)
       {
         rf_port:      0,
         n_antenna_dl: 2,
@@ -58,7 +58,7 @@
         // Inter-ENB HO
         {
           rat:          "nr",
-          nr_cell_id:   0x77712, // -> 1
+          nr_cell_id:   0x77712, // -> PEERCELL2
           gnb_id_bits:  22,
           n_id_cell:    75,
           dl_nr_arfcn:  520000,
```
parent 22e03a71
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
"type": "string", "type": "string",
"options": { "hidden": true } "options": { "hidden": true }
}, },
"cell_kind": {
"type": "string",
"const": "enb",
"template": "enb",
"options": { "hidden": true }
},
"rf_mode": { "rf_mode": {
"title": "RF mode", "title": "RF mode",
...@@ -52,8 +58,53 @@ ...@@ -52,8 +58,53 @@
"default": 10000 "default": 10000
}, },
"ru": { "ru": {
"$ref": "../ru/input-schema.json", "$ref": "#/$defs/ru-of-cell",
"propertyOrder": 9999 "propertyOrder": 9999
} }
},
"$defs": {
"ru-of-cell": {
"title": "Radio Unit",
"oneOf": [
{
"title": "Shared Radio Unit",
"description": "Use radio unit defined in separate shared instance",
"type": "object",
"required": ["ru_type", "ru_ref"],
"properties": {
"ru_type": {
"const": "ru_ref",
"template": "ru_ref",
"options": { "hidden": true }
},
"ru_ref": {
"title": "RU Reference",
"description": "Reference of shared radio unit instance",
"type": "string"
}
}
},
{
"title": "Shared Radio Unit of a Cell",
"description": "Use the same radio unit as referenced cell instance does",
"type": "object",
"required": ["ru_type", "ruincell_ref"],
"properties": {
"ru_type": {
"const": "ruincell_ref",
"template": "ruincell_ref",
"options": { "hidden": true }
},
"ruincell_ref": {
"title": "Cell Reference",
"description": "Reference of cell instance whose radio unit to share",
"type": "string"
}
}
},
{ "$ref": "../ru/input-schema.json" }
]
}
} }
} }
{%- import 'slaplte.jinja2' as slaplte with context %} {%- import 'slaplte.jinja2' as slaplte with context %}
{%- set J = slaplte.J %} {%- set J = slaplte.J %}
{%- set jcell_ru_ref = slaplte.jcell_ru_ref %}
{%- set ierror = slaplte.ierror %} {%- set ierror = slaplte.ierror %}
{%- set bug = slaplte.bug %} {%- set bug = slaplte.bug %}
{#- for standalone testing via slapos-render-config.py {#- for standalone testing via slapos-render-config.py
NOTE: keep in sync with instance-enb.jinja2.cfg and ru/libinstance.jinja2.cfg #} NOTE: keep in sync with instance-enb.jinja2.cfg and ru/libinstance.jinja2.cfg #}
{%- if _standalone is defined %} {%- if _standalone is defined %}
{%- set ru_dict = {} %} {%- set iru_dict = {} %}
{%- set cell_dict = {} %} {%- set icell_dict = {} %}
{%- set peer_dict = {} %} {%- set ipeer_dict = {} %}
{%- set peercell_dict = {} %} {%- set ipeercell_dict = {} %}
{%- do slaplte.load_ru_and_cell(ru_dict, cell_dict) %} {%- do slaplte.load_iru_and_icell(iru_dict, icell_dict) %}
{%- do slaplte.load_peer(peer_dict) %} {%- do slaplte.load_ipeer(ipeer_dict) %}
{%- do slaplte.load_peercell(peercell_dict) %} {%- do slaplte.load_ipeercell(ipeercell_dict) %}
{%- do slaplte.check_loaded_everything() %}
{%- endif %} {%- endif %}
{#- do_lte/do_nr indicate whether we have LTE and/or NR cells {#- do_lte/do_nr indicate whether we have LTE and/or NR cells
cell_dict_lte/cell_dict_nr keep LTE/NR parts of cell_dict registry #} icell_dict_lte/icell_dict_nr keep LTE/NR parts of icell_dict registry #}
{%- set cell_dict_lte = dict(cell_dict|dictsort | selectattr('1.cell_type', '==', 'lte')) %} {%- set icell_dict_lte = dict(icell_dict|dictsort | selectattr('1._.cell_type', '==', 'lte')) %}
{%- set cell_dict_nr = dict(cell_dict|dictsort | selectattr('1.cell_type', '==', 'nr' )) %} {%- set icell_dict_nr = dict(icell_dict|dictsort | selectattr('1._.cell_type', '==', 'nr' )) %}
{%- set do_lte = len(cell_dict_lte) > 0 %} {%- set do_lte = len(icell_dict_lte) > 0 %}
{%- set do_nr = len(cell_dict_nr) > 0 %} {%- set do_nr = len(icell_dict_nr) > 0 %}
{#- handover_config emits handover configuration #} {#- handover_config emits handover configuration #}
...@@ -31,7 +33,8 @@ ...@@ -31,7 +33,8 @@
{#- TODO: add info about peers as shared instances - one instance per peer *ENB*. {#- TODO: add info about peers as shared instances - one instance per peer *ENB*.
then query SlapOS Master about cells configured on that peer ENB and then query SlapOS Master about cells configured on that peer ENB and
put them as peers here #} put them as peers here #}
{%- for peercell_ref, ncell in peercell_dict|dictsort %} {%- for peercell_ref, ipeercell in ipeercell_dict|dictsort %}
{%- set ncell = ipeercell['_'] %}
{ {
{%- if ncell.cell_type == 'lte' %} {%- if ncell.cell_type == 'lte' %}
rat: "eutra", rat: "eutra",
...@@ -107,7 +110,7 @@ ...@@ -107,7 +110,7 @@
log_filename: "{{ directory['log'] }}/enb.log", log_filename: "{{ directory['log'] }}/enb.log",
{# instantiate radio units #} {# instantiate radio units #}
{{ slaplte.ru_config(ru_dict, slapparameter_dict) }} {{ slaplte.ru_config(iru_dict, slapparameter_dict) }}
{%- if slapparameter_dict.get('websocket_password', '') %} {%- if slapparameter_dict.get('websocket_password', '') %}
com_addr: "[{{ gtp_addr_v6 }}]:{{ slapparameter_dict.com_ws_port }}", com_addr: "[{{ gtp_addr_v6 }}]:{{ slapparameter_dict.com_ws_port }}",
...@@ -177,13 +180,13 @@ ...@@ -177,13 +180,13 @@
then query SlapOS Master about cells configured on that peer ENB and then query SlapOS Master about cells configured on that peer ENB and
depending on whether LTE and/or NR cells are there add X2 and/or Xn peers #} depending on whether LTE and/or NR cells are there add X2 and/or Xn peers #}
{%- if do_lte %} {%- if do_lte %}
x2_peers: {{ peer_dict|dictsort | selectattr('1.peer_type', '==', 'lte') x2_peers: {{ ipeer_dict|dictsort | selectattr('1._.peer_type', '==', 'lte')
| map(attribute='1.x2_addr') | map(attribute='1._.x2_addr')
| list | tojson }}, | list | tojson }},
{%- endif %} {%- endif %}
{%- if do_nr %} {%- if do_nr %}
xn_peers: {{ peer_dict|dictsort | selectattr('1.peer_type', '==', 'nr') xn_peers: {{ ipeer_dict|dictsort | selectattr('1._.peer_type', '==', 'nr')
| map(attribute='1.xn_addr') | map(attribute='1._.xn_addr')
| list | tojson }}, | list | tojson }},
{%- endif %} {%- endif %}
...@@ -200,9 +203,11 @@ ...@@ -200,9 +203,11 @@
// LTE cells // LTE cells
cell_list: [ cell_list: [
{%- if do_lte %} {%- if do_lte %}
{%- for cell_ref, cell in cell_dict_lte|dictsort %} {%- for cell_ref, icell in icell_dict_lte|dictsort %}
{%- set ru_ref = cell.ru_ref %} {%- set cell = icell['_'] %}
{%- set ru = ru_dict[ru_ref] %} {%- set ru_ref = J(jcell_ru_ref(icell, icell_dict)) %}
{%- set iru = iru_dict[ru_ref] %}
{%- set ru = iru['_'] %}
// {{ cell_ref }} ({{ ru_ref }}) // {{ cell_ref }} ({{ ru_ref }})
{ {
...@@ -222,7 +227,8 @@ ...@@ -222,7 +227,8 @@
// Carrier Aggregation // Carrier Aggregation
scell_list: [ scell_list: [
{%- for cell2_ref, cell2 in cell_dict_lte|dictsort %} {%- for cell2_ref, icell2 in icell_dict_lte|dictsort %}
{%- set cell2 = icell2['_'] %}
{%- if cell2_ref != cell_ref %} {%- if cell2_ref != cell_ref %}
{ {
cell_id: {{ cell2.cell_id }}, // + {{ cell2_ref }} cell_id: {{ cell2.cell_id }}, // + {{ cell2_ref }}
...@@ -264,11 +270,11 @@ ...@@ -264,11 +270,11 @@
n1_pucch_sr_count: 11, n1_pucch_sr_count: 11,
cqi_pucch_n_rb: 1, cqi_pucch_n_rb: 1,
{#- for CA with 2 cells it is possible to use PUCCH 1b CS ack/nack #} {#- for CA with 2 cells it is possible to use PUCCH 1b CS ack/nack #}
{%- if len(cell_dict_lte) == 2 %} {%- if len(icell_dict_lte) == 2 %}
ack_nack_feedback_mode_ca: "cs", ack_nack_feedback_mode_ca: "cs",
n1_pucch_an_cs_count: 8, n1_pucch_an_cs_count: 8,
{#- starting from 3 cells it is always PUCCH 3 for ack/nack in CA #} {#- starting from 3 cells it is always PUCCH 3 for ack/nack in CA #}
{%- elif len(cell_dict_lte) >= 3 %} {%- elif len(icell_dict_lte) >= 3 %}
ack_nack_feedback_mode_ca: "pucch3", ack_nack_feedback_mode_ca: "pucch3",
n3_pucch_an_n_rb: 3, n3_pucch_an_n_rb: 3,
{%- endif %} {%- endif %}
...@@ -417,9 +423,11 @@ ...@@ -417,9 +423,11 @@
{% if do_nr %} {% if do_nr %}
// NR cells // NR cells
nr_cell_list: [ nr_cell_list: [
{%- for cell_ref, cell in cell_dict_nr|dictsort %} {%- for cell_ref, icell in icell_dict_nr|dictsort %}
{%- set ru_ref = cell.ru_ref %} {%- set cell = icell['_'] %}
{%- set ru = ru_dict[ru_ref] %} {%- set ru_ref = J(jcell_ru_ref(icell, icell_dict)) %}
{%- set iru = iru_dict[ru_ref] %}
{%- set ru = iru['_'] %}
// {{ cell_ref }} ({{ ru_ref }}) // {{ cell_ref }} ({{ ru_ref }})
{ {
...@@ -561,7 +569,7 @@ ...@@ -561,7 +569,7 @@
bitmap: "110011", bitmap: "110011",
cdm_type: "fd_cdm2", cdm_type: "fd_cdm2",
{%- else %} {%- else %}
{%- do error('n_antenna_dl=%d is not supported' % ru.n_antenna_dl) %} {%- do ierror(iru, 'n_antenna_dl=%d is not supported' % ru.n_antenna_dl) %}
{%- endif %} {%- endif %}
}, },
{%- if tdd_config != 3 %} {%- if tdd_config != 3 %}
......
...@@ -3,17 +3,6 @@ ...@@ -3,17 +3,6 @@
"$schema": "http://json-schema.org/draft-04/schema", "$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters", "title": "Input Parameters",
"properties": { "properties": {
"cell_list": {
"title": "Cell List",
"description": "Cell List",
"patternProperties": {
".*": {
"$ref": "cell/input-schema.json"
}
},
"type": "object",
"default": {}
},
"user-authorized-key": { "user-authorized-key": {
"title": "User Authorized Key", "title": "User Authorized Key",
"description": "SSH public key in order to connect to the SSH server of this instance.", "description": "SSH public key in order to connect to the SSH server of this instance.",
...@@ -161,28 +150,6 @@ ...@@ -161,28 +150,6 @@
"type": "object", "type": "object",
"default": {} "default": {}
}, },
"ncell_list": {
"title": "Neighbour Cell Info",
"description": "Neighbour Cell Info",
"patternProperties": {
".*": {
"$ref": "peer/cell/input-schema.json"
}
},
"type": "object",
"default": {}
},
"peers": {
"title": "Peers",
"description": "Peers",
"patternProperties": {
".*": {
"$ref": "peer/input-schema.json"
}
},
"type": "object",
"default": {}
},
"websocket_password": { "websocket_password": {
"title": "Websocket password", "title": "Websocket password",
"description": "Activates websocket for remote control and sets password", "description": "Activates websocket for remote control and sets password",
......
...@@ -8,8 +8,6 @@ ...@@ -8,8 +8,6 @@
'use_ipv4': False, 'use_ipv4': False,
'gnb_id_bits': 28, 'gnb_id_bits': 28,
'nssai': {'1': {'sst': 1}}, 'nssai': {'1': {'sst': 1}},
"ncell_list": {},
"peers": {},
} %} } %}
{%- set gtp_addr_lo = '127.0.1.1' %} {%- set gtp_addr_lo = '127.0.1.1' %}
{%- for k,v in enb_defaults|dictsort %} {%- for k,v in enb_defaults|dictsort %}
...@@ -37,10 +35,11 @@ offline = true ...@@ -37,10 +35,11 @@ offline = true
{%- import 'slaplte.jinja2' as slaplte with context %} {%- import 'slaplte.jinja2' as slaplte with context %}
{%- import 'ru_libinstance.jinja2.cfg' as rulib with context %} {%- import 'ru_libinstance.jinja2.cfg' as rulib with context %}
{%- set peer_dict = {} %} {%- set ipeer_dict = {} %}
{%- set peercell_dict = {} %} {%- set ipeercell_dict = {} %}
{%- do slaplte.load_peer(peer_dict) %} {%- do slaplte.load_ipeer(ipeer_dict) %}
{%- do slaplte.load_peercell(peercell_dict) %} {%- do slaplte.load_ipeercell(ipeercell_dict) %}
{%- do slaplte.check_loaded_everything() %}
{{ rulib.buildout() }} {{ rulib.buildout() }}
...@@ -209,10 +208,10 @@ url = {{ enb_template }} ...@@ -209,10 +208,10 @@ url = {{ enb_template }}
output = ${directory:etc}/enb.cfg output = ${directory:etc}/enb.cfg
extra-context = extra-context =
import json_module json import json_module json
json ru_dict {{ rulib.ru_dict | tojson }} json iru_dict {{ rulib.iru_dict | tojson }}
json cell_dict {{ rulib.cell_dict | tojson }} json icell_dict {{ rulib.icell_dict | tojson }}
json peer_dict {{ peer_dict | tojson }} json ipeer_dict {{ ipeer_dict | tojson }}
json peercell_dict {{ peercell_dict | tojson }} json ipeercell_dict {{ ipeercell_dict | tojson }}
import-list = import-list =
rawfile slaplte.jinja2 {{ slaplte_template }} rawfile slaplte.jinja2 {{ slaplte_template }}
...@@ -228,6 +227,11 @@ enb-ipv4 = {{ lan_ipv4 }} ...@@ -228,6 +227,11 @@ enb-ipv4 = {{ lan_ipv4 }}
amarisoft-version = {{ lte_version }} amarisoft-version = {{ lte_version }}
license-expiration = {{ lte_expiration }} license-expiration = {{ lte_expiration }}
monitor-gadget-url = ${:monitor-base-url}/gadget/software.cfg.html 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) }}
[monitor-instance-parameter] [monitor-instance-parameter]
{% if slapparameter_dict.get("name", None) %} {% if slapparameter_dict.get("name", None) %}
......
...@@ -47,15 +47,24 @@ ...@@ -47,15 +47,24 @@
{%- endfor %} {%- endfor %}
{#- make real cell_list to be rejected in ORS mode #} {#- make real ru/cell/peer/... shared instances to be rejected in ORS mode #}
{%- set cell_list = slapparameter_dict.setdefault('cell_list', {}) %} {%- set ishared_list = slap_configuration.setdefault('slave-instance-list', []) %}
{%- if len(cell_list) > 0 %} {%- for ishared in ishared_list %}
{%- do error('ORS mode does not support cell_list parameter') %} {%- set _ = json_module.loads(ishared['_']) %}
{%- if 'ru_type' in _ or 'cell_type' in _ %}
{%- do ishared.update({'_': {'REJECT': 1}|tojson}) %}
{%- endif %} {%- endif %}
{%- endfor %}
{#- inject ru+cell synthesized from ORS-specific parameters #} {#- inject ru+cell synthesized from ORS-specific parameters #}
{%- set ru = { {%- macro iref(name) %}
{{- '%s.%s' % (slap_configuration['instance-title'], name) -}}
{%- endmacro %}
{%- do ishared_list.append({
'slave_title': iref('RU'),
'slave_reference': False,
'_': {
'ru_type': 'sdr', 'ru_type': 'sdr',
'ru_link_type': 'sdr', 'ru_link_type': 'sdr',
'sdr_dev': 0, 'sdr_dev': 0,
...@@ -63,7 +72,8 @@ ...@@ -63,7 +72,8 @@
'n_antenna_ul': slapparameter_dict.n_antenna_ul, 'n_antenna_ul': slapparameter_dict.n_antenna_ul,
'tx_gain': ors_version['current-tx-gain'], 'tx_gain': ors_version['current-tx-gain'],
'rx_gain': ors_version['current-rx-gain'], 'rx_gain': ors_version['current-rx-gain'],
} } |tojson
})
%} %}
{%- if enb_mode == 'enb' %} {%- if enb_mode == 'enb' %}
...@@ -88,24 +98,29 @@ ...@@ -88,24 +98,29 @@
{%- endif %} {%- endif %}
{%- do cell.update({ {%- do cell.update({
'cell_kind': 'enb',
'rf_mode': slapparameter_dict.rf_mode, 'rf_mode': slapparameter_dict.rf_mode,
'pci': slapparameter_dict.pci, 'pci': slapparameter_dict.pci,
'cell_id': slapparameter_dict.cell_id, 'cell_id': slapparameter_dict.cell_id,
'tdd_ul_dl_config': slapparameter_dict.tdd_ul_dl_config, 'tdd_ul_dl_config': slapparameter_dict.tdd_ul_dl_config,
'inactivity_timer': slapparameter_dict.inactivity_timer, 'inactivity_timer': slapparameter_dict.inactivity_timer,
'ru': ru,
'ru': { 'ru_type': 'ru_ref',
'ru_ref': iref('RU') }
}) })
%} %}
{%- do cell_list.update({'CELL': cell}) %} {%- do ishared_list.append({
'slave_title': iref('CELL'),
'slave_reference': False,
'_': cell | tojson
})
%}
{#- translate ORS ncell_list to generic ncell_list #} {#- inject synthesized peer cells #}
{%- set ncell_list_ors = slapparameter_dict.ncell_list %} {%- for k, ncell in slapparameter_dict.ncell_list|dictsort %}
{%- set ncell_list = {} %} {%- set peercell = {'cell_kind': 'enb_peer'} %}
{%- do slapparameter_dict.update({'ncell_list': ncell_list}) %}
{%- for k, ncell in ncell_list_ors|dictsort %}
{%- set peercell = {} %}
{%- macro _(name, default) %} {%- macro _(name, default) %}
{%- if default is defined %} {%- if default is defined %}
{%- do peercell.update({name: default}) %} {%- do peercell.update({name: default}) %}
...@@ -132,27 +147,37 @@ ...@@ -132,27 +147,37 @@
{%- do _('tac', 1) %} {%- do _('tac', 1) %}
{%- do _('nr_band') %} {%- do _('nr_band') %}
{%- endif %} {%- endif %}
{%- do ishared_list.append({
'slave_title': '%s%s' % (iref('PEERCELL'), k),
'slave_reference': False,
'_': peercell | tojson
})
%}
{%- endfor %} {%- endfor %}
{#- translate ORS x2_peers/xn_peers to generic peers #} {#- inject synthesized peers #}
{%- set peers = {} %}
{%- do slapparameter_dict.update({'peers': peers}) %}
{%- if enb_mode == 'lte' %} {%- if enb_mode == 'lte' %}
{%- for k, peer in slapparameter_dict.x2_peers|dictsort %} {%- for k, peer in slapparameter_dict.x2_peers|dictsort %}
{%- do peers.update({'X2_PEER%s' % k: { {%- do ishared_list.append({
'slave_title': '%s%s' % (iref('X2_PEER'), k),
'slave_reference': False,
'_': {
'peer_type': 'nr', 'peer_type': 'nr',
'x2_addr': peer.x2_addr, 'x2_addr': peer.x2_addr,
} } | tojson
}) })
%} %}
{%- endfor %} {%- endfor %}
{%- elif enb_mode == 'nr' %} {%- elif enb_mode == 'nr' %}
{%- for k, peer in slapparameter_dict.xn_peers|dictsort %} {%- for k, peer in slapparameter_dict.xn_peers|dictsort %}
{%- do peers.update({'XN_PEER%s' % k: { {%- do ishared_list.append({
'slave_title': '%s%s' % (iref('XN_PEER'), k),
'slave_reference': False,
'_': {
'peer_type': 'nr', 'peer_type': 'nr',
'xn_addr': peer.xn_addr 'xn_addr': peer.xn_addr
} } | tojson
}) })
%} %}
{%- endfor %} {%- endfor %}
...@@ -215,14 +240,14 @@ current-nr-band = {{ ors_version['current-nr-band'] }} ...@@ -215,14 +240,14 @@ current-nr-band = {{ ors_version['current-nr-band'] }}
{%- endif %} {%- endif %}
# hide <cell>-* from published information # hide ru-list, cell-list, peer-list and peer-cell-list from published information
[publish-connection-information] [publish-connection-information]
depends += ${publish-connection-information-ors-cleanup:recipe} depends += ${publish-connection-information-ors-cleanup:recipe}
[publish-connection-information-ors-cleanup] [publish-connection-information-ors-cleanup]
recipe = slapos.recipe.build recipe = slapos.recipe.build
init = init =
publish = self.buildout['publish-connection-information'] publish = self.buildout['publish-connection-information']
cell_ref = "CELL" del publish['ru-list']
for k in publish.keys(): del publish['cell-list']
if k.startswith('%s-' % cell_ref): del publish['peer-list']
del publish[k] del publish['peer-cell-list']
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
"type": "object", "type": "object",
"required": [ "required": [
"cell_type", "cell_type",
"cell_kind",
"pci", "pci",
"tac" "tac"
], ],
...@@ -13,6 +14,12 @@ ...@@ -13,6 +14,12 @@
"cell_type": { "cell_type": {
"type": "string", "type": "string",
"options": { "hidden": true } "options": { "hidden": true }
},
"cell_kind": {
"type": "string",
"const": "enb_peer",
"template": "enb_peer",
"options": { "hidden": true }
} }
} }
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
"required": [ "required": [
"cell_type", "cell_type",
"cell_kind",
"pci", "pci",
"tac", "tac",
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
"required": [ "required": [
"cell_type", "cell_type",
"cell_kind",
"pci", "pci",
"tac", "tac",
......
dhcp-leasefile={{ directory['etc'] }}/dnsmasq.leases dhcp-leasefile={{ directory['etc'] }}/dnsmasq.leases
port=5354 port=5354
{%- for (ru_ref, ru) in ru_dict|dictsort | selectattr('1.cpri_link', 'defined') %} {%- for (ru_ref, iru) in iru_dict|dictsort | selectattr('1._.cpri_link', 'defined') %}
{%- set ru = iru['_'] %}
{%- set ru_tap = ru.cpri_link._tap %} {%- set ru_tap = ru.cpri_link._tap %}
{%- set vtap = json_module.loads(vtap_jdict[ru_tap]) %} {%- set vtap = json_module.loads(vtap_jdict[ru_tap]) %}
{%- set plen = netaddr.IPNetwork(vtap.network).prefixlen %} {%- set plen = netaddr.IPNetwork(vtap.network).prefixlen %}
......
...@@ -7,19 +7,20 @@ ...@@ -7,19 +7,20 @@
{%- import 'slaplte.jinja2' as slaplte with context %} {%- import 'slaplte.jinja2' as slaplte with context %}
NOTE: driver-specific logic is implemented in rudrv .buildout_ru() and .buildout() . NOTE: driver-specific logic is implemented in rudrv .buildout_iru() and .buildout() .
#} #}
{#- ru_dict and cell_dict keep RU and cell registries {#- iru_dict and icell_dict keep RU and cell registries
ru_dict: reference -> ru iru_dict: reference -> iru
cell_dict: reference -> cell icell_dict: reference -> icell
#} #}
{%- set ru_dict = {} %} {%- set iru_dict = {} %}
{%- set cell_dict = {} %} {%- set icell_dict = {} %}
{%- do slaplte.load_ru_and_cell(ru_dict, cell_dict) %} {%- do slaplte.load_iru_and_icell(iru_dict, icell_dict) %}
{%- macro buildout() %} {%- macro buildout() %}
{%- set root = slap_configuration['instance-title'] %}
{%- set testing = slapparameter_dict.get("testing", False) %} {%- set testing = slapparameter_dict.get("testing", False) %}
{#- part emits new buildout section and registers it into buildout.parts #} {#- part emits new buildout section and registers it into buildout.parts #}
...@@ -31,14 +32,20 @@ ...@@ -31,14 +32,20 @@
{#- promise emits new buildout section for a promise #} {#- promise emits new buildout section for a promise #}
{%- macro promise(name) %} {%- macro promise(name) %}
{#- show in monitor RU1-... instead of COMP-ENB/RU1- #}
{%- set pretty_name = name.removeprefix('%s.' % root) %}
{{ part('promise-'+name) }} {{ part('promise-'+name) }}
<= monitor-promise-base <= monitor-promise-base
name = {{ name }}.py name = {{ pretty_name }}.py
config-testing = {{ testing }} config-testing = {{ testing }}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
{%- endmacro %} {%- endmacro %}
{#- import RU drivers #} {#- import RU drivers #}
{%- set J = slaplte.J %}
{%- set jref_of_shared = slaplte.jref_of_shared %}
{%- set jcell_ru_ref = slaplte.jcell_ru_ref %}
{%- set ierror = slaplte.ierror %}
{%- import 'ru_sdr_libinstance.jinja2.cfg' as rudrv_sdr with context %} {%- import 'ru_sdr_libinstance.jinja2.cfg' as rudrv_sdr with context %}
{%- import 'ru_lopcomm_libinstance.jinja2.cfg' as rudrv_lopcomm with context %} {%- import 'ru_lopcomm_libinstance.jinja2.cfg' as rudrv_lopcomm with context %}
{%- import 'ru_sunwave_libinstance.jinja2.cfg' as rudrv_sunwave with context %} {%- import 'ru_sunwave_libinstance.jinja2.cfg' as rudrv_sunwave with context %}
...@@ -54,7 +61,7 @@ config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} ...@@ -54,7 +61,7 @@ config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
provide several TAP interfaces to instances. See discussion at provide several TAP interfaces to instances. See discussion at
https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356 https://lab.nexedi.com/nexedi/slapos/merge_requests/1471#note_194356
for details. #} for details. #}
{%- set ntap = len(list(ru_dict|dictsort | selectattr('1.cpri_link', 'defined'))) %} {%- set ntap = len(list(iru_dict|dictsort | selectattr('1._.cpri_link', 'defined'))) %}
{%- set vtap_list = [] %} {%- set vtap_list = [] %}
[vtap] [vtap]
recipe = plone.recipe.command recipe = plone.recipe.command
...@@ -148,7 +155,7 @@ context = ...@@ -148,7 +155,7 @@ context =
import netaddr netaddr import netaddr netaddr
section directory directory section directory directory
section vtap_jdict vtap_jdict section vtap_jdict vtap_jdict
json ru_dict {{ ru_dict | tojson }} json iru_dict {{ iru_dict | tojson }}
{{ part('dnsmasq-service') }} {{ part('dnsmasq-service') }}
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
...@@ -167,7 +174,16 @@ hash-files = ...@@ -167,7 +174,16 @@ hash-files =
{#- go through all RUs and for each RU emit generic promises and invoke {#- go through all RUs and for each RU emit generic promises and invoke
RU-specific buildout handler #} RU-specific buildout handler #}
{%- for ru_ref, ru in ru_dict|dictsort %} {%- 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 %}
# {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R ({{ ru.ru_type}}) # {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R ({{ ru.ru_type}})
{%- if ru.ru_link_type == 'sdr' %} {%- if ru.ru_link_type == 'sdr' %}
...@@ -206,13 +222,15 @@ config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }} ...@@ -206,13 +222,15 @@ config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
{{ rudrv.buildout() }} {{ rudrv.buildout() }}
{%- do rudrv_init.update({ru.ru_type: 1}) %} {%- do rudrv_init.update({ru.ru_type: 1}) %}
{%- endif %} {%- endif %}
{{ rudrv.buildout_ru(ru_ref, ru, ru.cell_ref, cell_dict[ru.cell_ref]) }} {{ rudrv.buildout_iru(iru, iru_icell_list) }}
{%- endfor %} {%- endfor %}
{#- handle configured cells #} {#- handle configured cells #}
{%- for cell_ref, cell in cell_dict|dictsort %} {%- for cell_ref, icell in icell_dict|dictsort %}
{%- set ru_ref = cell.ru_ref %} {%- set cell = icell['_'] %}
{%- set ru = ru_dict[ru_ref] %} {%- set ru_ref = J(jcell_ru_ref(icell, icell_dict)) %}
{%- set iru = iru_dict[ru_ref] %}
{%- set ru = iru['_'] %}
{#- generate CELL-drb.cfg and CELL-sib23.asn #} {#- generate CELL-drb.cfg and CELL-sib23.asn #}
{{ part('drb-config-%s' % cell_ref) }} {{ part('drb-config-%s' % cell_ref) }}
...@@ -235,7 +253,8 @@ extra-context = ...@@ -235,7 +253,8 @@ extra-context =
json ru_ref {{ ru_ref | tojson }} json ru_ref {{ ru_ref | tojson }}
json ru {{ ru | tojson }} json ru {{ ru | tojson }}
{#- publish information about the cell #} {#- publish information about the cell (skipping synthetic) #}
{%- if icell.slave_reference %}
[publish-connection-information] [publish-connection-information]
{%- if cell.cell_type == 'lte' %} {%- if cell.cell_type == 'lte' %}
{{cell_ref}}-dl_earfcn = {{ dumps(cell.dl_earfcn) }} {{cell_ref}}-dl_earfcn = {{ dumps(cell.dl_earfcn) }}
...@@ -246,6 +265,8 @@ extra-context = ...@@ -246,6 +265,8 @@ extra-context =
{%- do bug('unreachable') %} {%- do bug('unreachable') %}
{%- endif %} {%- endif %}
{%- endif %}
{%- endfor %} {%- endfor %}
{#- retrieve rf and stats[rf,samples] data from amarisoft service for promises {#- retrieve rf and stats[rf,samples] data from amarisoft service for promises
......
{#- Package ru/lopcomm/libinstance provides instance code for handling Lopcomm ORAN Radio Units. #} {#- Package ru/lopcomm/libinstance provides instance code for handling Lopcomm ORAN Radio Units. #}
{%- macro buildout_ru(ru_ref, ru, cell_ref, cell) %} {%- 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 #} {#- indicate whether RU is listening for netconf #}
......
{#- Package ru/sdr/libinstance provides instance code for handling SDR Radio Units. #} {#- Package ru/sdr/libinstance provides instance code for handling SDR Radio Units. #}
{%- macro buildout_ru(ru_ref, ru, cell_ref, cell) %} {%- macro buildout_iru(iru, icell_list) %}
{#- nothing SDR-specific #} {#- nothing SDR-specific #}
{%- endmacro %} {%- endmacro %}
......
{#- Package ru/sunwave/libinstance provides instance code for handling SunWave Radio Units. #} {#- Package ru/sunwave/libinstance provides instance code for handling SunWave Radio Units. #}
{%- macro buildout_ru(ru_ref, ru, cell_ref, cell) %} {%- macro buildout_iru(iru, icell_list) %}
{#- nothing SunWave-specific #} {#- nothing SunWave-specific #}
{%- endmacro %} {%- endmacro %}
......
{#- Package slaplte provides helpers for configuring Amarisoft LTE services in SlapOS. {#- Package slaplte provides helpers for configuring Amarisoft LTE services in SlapOS.
- load_ru_and_cell initializes RU and cell registries. - load_iru_and_icell initializes RU and cell registries.
- load_peercell initializes peer-cell registry. - load_ipeercell initializes peer-cell registry.
- load_peer initializes peer registry. - load_ipeer initializes peer registry.
- ru_config emits RF driver configuration for specified Radio Units. - ru_config emits RF driver configuration for specified Radio Units.
In the code iX denotes shared instance of type X, while X denotes
parameters passed to iX. For example iru denotes Radio Unit shared
instance, while ru denotes parameters of that Radio Unit instance.
The following utilities are also provided: The following utilities are also provided:
- J should be used around macro calls to retrieve returned objects. - J should be used around macro calls to retrieve returned objects.
- error reports instantiation error. - error reports instantiation error.
- ierror reports instantiation error caused by shared instance configuration.
-#} -#}
...@@ -104,42 +109,119 @@ ...@@ -104,42 +109,119 @@
{%- do assert(False, msg) %} {%- do assert(False, msg) %}
{%- endmacro %} {%- endmacro %}
{#- ierror reports instantiation error caused by shared instance configuration. #}
{%- macro ierror(ishared, msg) %}
{%- do error('%s: %s' % (J(jref_of_shared(ishared)), msg)) %}
{%- endmacro %}
{#- ---- loading ---- #} {#- ---- loading ---- #}
{#- load_ru_and_cell initializes RU and cell registries. {#- jref_of_shared returns original reference used to request shared instance.
slapproxy puts the reference into slave_reference and slave_title as <partition_id>_<reference>.
slapos master puts the reference into slave_title as-is and assigns to slave_reference SOFTINST-XXX.
cell_dict keeps configured cells: {} cell reference -> cell parameters -> we extract the reference from slave_title.
ru_dict keeps configured RU: {} RU reference -> RU parameters
#} #}
{%- macro load_ru_and_cell(ru_dict, cell_dict) %} {%- macro jref_of_shared(ishared) %}
{%- set cell_list = slapparameter_dict.get('cell_list', {}) %} {#- do print('jref_of_shared %r' % (ishared,)) #}
{%- do cell_dict.update(cell_list) %} {%- set ref = ishared['slave_title'] %}
{%- set partition_id = slap_configuration['slap-computer-partition-id'] %}
{%- if ref.startswith(partition_id) %}
{%- set ref = ref[len(partition_id):] %}
{%- endif %}
{%- set ref = ref.removeprefix('_') %}
{{- ref | tojson }}
{%- endmacro %}
{%- for i, (cell_ref, cell) in enumerate(cell_dict|dictsort) %} {#- qshared_instance_list queues not yet loaded shared instances.
{%- set ru_ref = '%s-RU' % cell_ref %} load_* routines process this queue and move loaded instances to i<type>_dict registries. #}
{%- set ru = cell.ru %} {%- set qshared_instance_list = slap_configuration.get('slave-instance-list', []) %}
{%- do ru.update({'cell_ref': cell_ref}) %}
{%- do cell.update({'ru_ref': ru_ref}) %}
{%- if ru.ru_link_type == 'cpri' %} {#- protect against duplicate slave_title -- see jref_of_shared for why we need this #}
{#- set 0 tx/rx gain to emit 0 in enb.cfg. {%- for i, ishared in enumerate(qshared_instance_list) %}
This will be changed later: .tx_gain and .rx_gain will be carrying real RU tx/rx gain #} {%- for k, kshared in enumerate(qshared_instance_list) %}
{%- do ru.update({'tx_gain': 0, {%- if i != k and ishared.slave_title == kshared.slave_title %}
'rx_gain': 0, {%- do ierror(ishared, 'duplicate title wrt %s' % kshared.slave_reference) %}
}) %}
{%- endif %} {%- endif %}
{%- endfor %}
{%- endfor %}
{#- check_loaded_everything verifies that all shared instances were handling during the load. #}
{%- macro check_loaded_everything() %}
{%- for ishared in qshared_instance_list %}
{%- do ierror(ishared, "shared instance of unsupported type") %}
{%- endfor %}
{%- endmacro %}
{#- json-decode _ in all shared instances #}
{%- for ishared in qshared_instance_list %}
{%- do ishared.update({'_': J(ishared['_'])}) %}
{%- endfor %}
{#- load_iru_and_icell initializes RU and cell registries.
icell_dict keeps cell shared instances: reference -> icell
iru_dict keeps RU shared instances + RU whose definition is embedded into a cell: reference -> iRU
in the kept instances _ is automatically json-decoded
#}
{%- macro load_iru_and_icell(iru_dict, icell_dict) %}
{%- set qother = [] %}
{%- for ishared in qshared_instance_list %}
{%- set ref = J(jref_of_shared(ishared)) %}
{%- set _ = ishared['_'] %}
{%- if 'ru_type' in _ %}
{%- set iru = ishared %}
{%- do _ru_set_defaults(_) %}
{%- do iru_dict.update({ref: iru}) %}
{%- elif 'cell_type' in _ and _.get('cell_kind') == 'enb' %}
{%- set icell = ishared %}
{%- do _cell_set_defaults(_, icell_dict) %}
{%- do icell_dict.update({ref: icell}) %}
{%- set ru = _['ru'] %}
{%- if ru.ru_type not in ('ru_ref', 'ruincell_ref') %}
{#- embedded ru definition -> expose it as synthethic `_<cell_ref>_ru` #}
{%- do _ru_set_defaults(ru) %} {%- do _ru_set_defaults(ru) %}
{%- do ru_dict.update({ru_ref: ru}) %} {%- do iru_dict.update({'_%s_ru' % ref: {
'_': ru,
'slave_title': '%s. RU' % icell.slave_title,
'slave_reference': False,
}}) %}
{%- endif %}
{%- else %}
{%- do qother.append(ishared) %}
{%- endif %}
{%- endfor %}
{%- do qshared_instance_list.clear() %}
{%- do qshared_instance_list.extend(qother) %}
{#- do print('\n>>> iru_dict:'), pprint(iru_dict) #}
{#- do print('\n>>> icell_dict:'), pprint(icell_dict) #}
{#- verify that there is no dangling cell -> cell refs in ruincell_ref #}
{%- for _, icell in icell_dict|dictsort %}
{%- set ru = icell['_']['ru'] %}
{%- if ru.ru_type == 'ruincell_ref' %}
{%- if ru.ruincell_ref not in icell_dict %}
{%- do ierror(icell, "referred cell %r does not exist" % ru.ruincell_ref) %}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- do _cell_set_defaults(cell, cell_dict) %} {#- verify that there is no dangling cell->ru references #}
{%- for _, icell in icell_dict|dictsort %}
{%- set ru_ref = J(jcell_ru_ref(icell, icell_dict)) %}
{%- if ru_ref not in iru_dict %}
{%- do ierror(icell, "referred RU %r does not exist" % ru_ref) %}
{%- endif %}
{%- endfor %} {%- endfor %}
{#- assign RUs rf_port and tx/rx channel indices #} {#- assign RUs rf_port and tx/rx channel indices #}
{%- set rf_chan = namespace(tx=0, rx=0) %} {%- set rf_chan = namespace(tx=0, rx=0) %}
{%- for rf_port, (ru_ref, ru) in enumerate(ru_dict|dictsort) %} {%- for rf_port, (ru_ref, iru) in enumerate(iru_dict|dictsort) %}
{%- set ru = iru['_'] %}
{%- do ru.update({'_rf_port': rf_port, {%- do ru.update({'_rf_port': rf_port,
'_rf_chan_tx': rf_chan.tx, '_rf_chan_tx': rf_chan.tx,
'_rf_chan_rx': rf_chan.rx}) %} '_rf_chan_rx': rf_chan.rx}) %}
...@@ -148,14 +230,14 @@ ...@@ -148,14 +230,14 @@
{%- endfor %} {%- endfor %}
{#- assign TAP interfaces to RUs #} {#- assign TAP interfaces to RUs #}
{%- set ru_vcpri = list(ru_dict|dictsort | selectattr('1.ru_link_type', '==', 'cpri')) %} {%- set iru_vcpri = list(iru_dict|dictsort | selectattr('1._.ru_link_type', '==', 'cpri')) %}
{%- for i, (ru_ref, ru) in enumerate(ru_vcpri) %} {%- for i, (ru_ref, iru) in enumerate(iru_vcpri) %}
{%- if len(ru_vcpri) > 1 %} {%- if len(iru_vcpri) > 1 %}
{%- set ru_tap = "%s-%d" % (tap, i+1) %} {%- set ru_tap = "%s-%d" % (tap, i+1) %}
{%- else %} {%- else %}
{%- set ru_tap = tap %} {%- set ru_tap = tap %}
{%- endif %} {%- endif %}
{%- do ru.cpri_link.update({'_tap': ru_tap}) %} {%- do iru._.cpri_link.update({'_tap': ru_tap}) %}
{%- endfor %} {%- endfor %}
{%- endmacro %} {%- endmacro %}
...@@ -174,47 +256,100 @@ ...@@ -174,47 +256,100 @@
{%- for k, v in defaults['ru/%s/cpri_link' % ru.ru_type].items() %} {%- for k, v in defaults['ru/%s/cpri_link' % ru.ru_type].items() %}
{%- do link.setdefault(k, v) %} {%- do link.setdefault(k, v) %}
{%- endfor %} {%- endfor %}
{#- set 0 tx/rx gain to emit 0 in enb.cfg.
This will be changed later: .tx_gain and .rx_gain will be carrying real RU tx/rx gain #}
{%- do ru.update({'tx_gain': 0,
'rx_gain': 0,
}) %}
{%- endif %} {%- endif %}
{%- endmacro %} {%- endmacro %}
{%- macro _cell_set_defaults(cell, cell_dict) %} {%- macro _cell_set_defaults(cell, icell_dict) %}
{%- for k, v in defaults['cell/%s' % cell.cell_type].items() %} {%- for k, v in defaults['cell/%s' % cell.cell_type].items() %}
{%- do cell.setdefault(k, v) %} {%- do cell.setdefault(k, v) %}
{%- endfor %} {%- endfor %}
{%- for k, v in defaults['cell/%s/%s' % (cell.cell_type, cell.rf_mode)].items() %} {%- for k, v in defaults['cell/%s/%s' % (cell.cell_type, cell.rf_mode)].items() %}
{%- do cell.setdefault(k, v) %} {%- do cell.setdefault(k, v) %}
{%- endfor %} {%- endfor %}
{%- set n = len(list(cell_dict|dictsort | selectattr('1.cell_type', '==', cell.cell_type))) %} {%- set n = len(list(icell_dict|dictsort | selectattr('1._.cell_type', '==', cell.cell_type))) %}
{%- do cell.setdefault('root_sequence_index', 1 + 203*(cell.cell_type == 'lte') + n) %} {%- do cell.setdefault('root_sequence_index', 1 + 203*(cell.cell_type == 'lte') + n) %}
{%- endmacro %} {%- endmacro %}
{#- load_peer initializes peer registry. {#- jcell_ru_ref returns RU reference linked from a cell.
if the cell embeds RU definition, its reference comes as `_<cell_ref>_ru`. #}
{%- macro jcell_ru_ref(icell, icell_dict) %}
{{- _jcell_ru_ref(icell, icell_dict, []) }}
{%- endmacro %}
{%- macro _jcell_ru_ref(icell, icell_dict, seen) %}
{%- set cell_ref = J(jref_of_shared(icell)) %}
{%- if cell_ref in seen %}
{%- for x in seen %}
{%- do ierror(x, "%s form a cycle via RU references" % seen) %}
{%- endfor %}
{{- None | tojson }}
{%- else %}
{%- do seen.append(cell_ref) %}
{%- set ru = icell['_']['ru'] %}
{%- if ru.ru_type == 'ru_ref' %}
{{- ru.ru_ref | tojson }}
{%- elif ru.ru_type == 'ruincell_ref' %}
{{- _jcell_ru_ref(icell_dict[ru.ruincell_ref], icell_dict, seen) }}
{%- else %}
{#- ru definition is embedded into cell #}
{{- ('_%s_ru' % J(jref_of_shared(icell))) | tojson }}
{%- endif %}
{%- endif %}
{%- endmacro %}
peer_dict keeps configured peers: {} peer reference -> peer {#- load_ipeer initializes peer registry.
ipeer_dict keeps peer shared instances: reference -> ipeer
#} #}
{%- macro load_peer(peer_dict) %} {%- macro load_ipeer(ipeer_dict) %}
{%- for ref, peer in slapparameter_dict.peers|dictsort %} {%- set qother = [] %}
{%- do peer_dict.update({ref: peer}) %} {%- for ishared in qshared_instance_list %}
{%- set ref = J(jref_of_shared(ishared)) %}
{%- set _ = ishared['_'] %}
{%- if 'peer_type' in _ %}
{%- set ipeer = ishared %}
{%- do assert(_.peer_type in ('lte', 'nr')) %}
{%- do ipeer_dict.update({ref: ipeer}) %}
{%- else %}
{%- do qother.append(ishared) %}
{%- endif %}
{%- endfor %} {%- endfor %}
{%- do qshared_instance_list.clear() %}
{%- do qshared_instance_list.extend(qother) %}
{%- endmacro %} {%- endmacro %}
{#- load_peercell initializes peer-cell registry. {#- load_ipeercell initializes peer-cell registry.
peercell_dict keeps configured peer cells: {} peer cell reference -> peercell ipeercell_dict keeps peer cell shared instances: reference -> ipeercell
#} #}
{%- macro load_peercell(peercell_dict) %} {%- macro load_ipeercell(ipeercell_dict) %}
{%- for ref, peercell in slapparameter_dict.ncell_list|dictsort %} {%- set qother = [] %}
{%- do peercell_dict.update({ref: peercell}) %} {%- for ishared in qshared_instance_list %}
{%- set ref = J(jref_of_shared(ishared)) %}
{%- set _ = ishared['_'] %}
{%- if 'cell_type' in _ and _.get('cell_kind') == 'enb_peer' %}
{%- set ipeercell = ishared %}
{%- do ipeercell_dict.update({ref: ipeercell}) %}
{%- else %}
{%- do qother.append(ishared) %}
{%- endif %}
{%- endfor %} {%- endfor %}
{%- do qshared_instance_list.clear() %}
{%- do qshared_instance_list.extend(qother) %}
{%- endmacro %} {%- endmacro %}
{#- ---- building configuration ---- #} {#- ---- building configuration ---- #}
{#- ru_config emits RF driver configuration for specified Radio Units. #} {#- ru_config emits RF driver configuration for specified Radio Units. #}
{%- macro ru_config(ru_dict, slapparameter_dict) %} {%- macro ru_config(iru_dict, slapparameter_dict) %}
// Radio Units // Radio Units
rf_driver: { rf_driver: {
{%- set dev_argv = [] %} {%- set dev_argv = [] %}
...@@ -222,7 +357,8 @@ ...@@ -222,7 +357,8 @@
{%- set ru_cpri_dict = {} %} {#- dev -> ru for ru with link_type = cpri #} {%- set ru_cpri_dict = {} %} {#- dev -> ru for ru with link_type = cpri #}
{%- set tx_gainv = [] %} {#- tx_gain by tx channel #} {%- set tx_gainv = [] %} {#- tx_gain by tx channel #}
{%- set rx_gainv = [] %} {#- rx_gain by rx channel #} {%- set rx_gainv = [] %} {#- rx_gain by rx channel #}
{%- for (ru_ref, ru) in ru_dict.items() | sort(attribute="1._rf_port") %} {%- for (ru_ref, iru) in iru_dict.items() | sort(attribute="1._._rf_port") %}
{%- set ru = iru['_'] %}
// {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R ({{ ru.ru_type }}) // {{ ru_ref }} {{ ru.n_antenna_dl }}T{{ ru.n_antenna_ul }}R ({{ ru.ru_type }})
{%- if ru.ru_link_type == 'sdr' %} {%- if ru.ru_link_type == 'sdr' %}
{%- do ru_sdr_dict.update({len(dev_argv): ru}) %} {%- do ru_sdr_dict.update({len(dev_argv): ru}) %}
...@@ -249,8 +385,8 @@ ...@@ -249,8 +385,8 @@
CPRI Radio Units: %r CPRI Radio Units: %r
See https://support.amarisoft.com/issues/26021 for details' % ( See https://support.amarisoft.com/issues/26021 for details' % (
ru_dict |dictsort |selectattr('1.ru_type', '==', 'sdr') |map(attribute='0') |list, iru_dict |dictsort |selectattr('1._.ru_type', '==', 'sdr') |map(attribute='0') |list,
ru_dict |dictsort |selectattr('1.ru_link_type', '==', 'cpri') |map(attribute='0') |list iru_dict |dictsort |selectattr('1._.ru_link_type', '==', 'cpri') |map(attribute='0') |list
)) %} )) %}
{%- endif %} {%- endif %}
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import zc.buildout.buildout # XXX workaround for https://lab.nexedi.com/nexedi/slapos.recipe.template/merge_requests/9 import zc.buildout.buildout # XXX workaround for https://lab.nexedi.com/nexedi/slapos.recipe.template/merge_requests/9
from slapos.recipe.template import jinja2_template from slapos.recipe.template import jinja2_template
import json, os, shutil import json, copy, os, pprint, shutil
# j2render renders config/<src> into config/out/<out> with provided json parameters. # j2render renders config/<src> into config/out/<out> with provided json parameters.
...@@ -17,6 +17,7 @@ def j2render(src, out, jcfg): ...@@ -17,6 +17,7 @@ def j2render(src, out, jcfg):
ctx = json.loads(jcfg) ctx = json.loads(jcfg)
assert '_standalone' not in ctx assert '_standalone' not in ctx
ctx['_standalone'] = True ctx['_standalone'] = True
ctx.setdefault('ors', False)
textctx = '' textctx = ''
for k, v in ctx.items(): for k, v in ctx.items():
textctx += 'json %s %s\n' % (k, json.dumps(v)) textctx += 'json %s %s\n' % (k, json.dumps(v))
...@@ -38,85 +39,41 @@ def j2render(src, out, jcfg): ...@@ -38,85 +39,41 @@ def j2render(src, out, jcfg):
return f.read() return f.read()
r._read = _read r._read = _read
# for template debugging
r.context.update({
'print': lambda *argv: print(*argv, file=sys.stderr),
'pprint': lambda obj: pprint.pprint(obj, sys.stderr),
})
os.makedirs(os.path.dirname(out), exist_ok=True) os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, 'w+') as f: with open(out, 'w+') as f:
f.write(r._render().decode()) f.write(r._render().decode())
def do_enb(): # Instance simulates configuration for an instance on SlapOS Master.
def do(src, out, slapparameter_dict): class Instance:
defaults = { def __init__(self, slap_software_type):
"com_ws_port": 9001, self.shared_instance_list = []
"com_addr": "127.0.1.2", self.slap_software_type = slap_software_type
"use_ipv4": False,
"gnb_id_bits": 28, # ishared appends new shared instance with specified configuration to .shared_instance_list .
"nssai": {'1': {'sst': 1}}, def ishared(self, slave_reference, cfg):
"ncell_list": {}, ishared = {
"peers": {}, # see comments in jref_of_shared about where and how slapproxy and
"gtp_addr": "127.0.1.1", # slapos master put partition_reference of a shared instance.
'slave_title': '_%s' % slave_reference,
'slave_reference': 'SOFTINST-%03d' % (len(self.shared_instance_list)+1),
'slap_software_type': self.slap_software_type,
'_': json.dumps(cfg)
} }
slapparameter_dict = slapparameter_dict.copy() self.shared_instance_list.append(ishared)
for k, v in defaults.items(): return ishared
slapparameter_dict.setdefault(k, v)
jslapparameter_dict = json.dumps(slapparameter_dict)
json_params_empty = """{
"slap_configuration": {
},
"directory": {
},
"slapparameter_dict": %(jslapparameter_dict)s
}"""
json_params = """{
"ors": {"one-watt": true},
"slap_configuration": {
"tap-name": "slaptap9"
},
"directory": {
"log": "log",
"etc": "etc",
"var": "var"
},
"slapparameter_dict": %(jslapparameter_dict)s
}"""
j2render(src, out, json_params % locals())
peer_lte1 = {
'peer_type': 'lte',
'x2_addr': '44.1.1.1',
}
peer_lte2 = {
'peer_type': 'lte',
'x2_addr': '44.1.1.2',
}
peer_nr1 = {
'peer_type': 'nr',
'xn_addr': '55.1.1.1',
}
peer_nr2 = {
'peer_type': 'nr',
'xn_addr': '55.1.1.2',
}
peercell_lte = { # ---- eNB ----
'cell_type': 'lte',
'e_cell_id': '0x12345',
'pci': 35,
'dl_earfcn': 700,
'tac': 123,
}
peercell_nr = {
'cell_type': 'nr',
'nr_cell_id': '0x77712',
'gnb_id_bits': 22,
'dl_nr_arfcn': 520000,
'nr_band': 38,
'ssb_nr_arfcn': 520090,
'pci': 75,
'tac': 321,
}
ru = { # ORS_eNB and ORS_gNB mimic what instance-ors-enb.jinja2.cfg does.
ORS_ru = {
'ru_type': 'sdr', 'ru_type': 'sdr',
'ru_link_type': 'sdr', 'ru_link_type': 'sdr',
'sdr_dev': 0, 'sdr_dev': 0,
...@@ -124,12 +81,15 @@ def do_enb(): ...@@ -124,12 +81,15 @@ def do_enb():
'n_antenna_ul': 2, 'n_antenna_ul': 2,
'tx_gain': 62, 'tx_gain': 62,
'rx_gain': 43, 'rx_gain': 43,
} }
ORS_json = """
do('enb.jinja2.cfg', 'ors/enb/enb.cfg', { "ors": {"one-watt": true},
'enb_id': "0x1A2D0", """
'cell_list': {'CELL': { def ORS_enb(ienb):
ienb.ishared('RU', ORS_ru)
ienb.ishared('CELL', {
'cell_type': 'lte', 'cell_type': 'lte',
'cell_kind': 'enb',
'rf_mode': 'tdd', 'rf_mode': 'tdd',
'dl_earfcn': 36100, 'dl_earfcn': 36100,
'bandwidth': 10, 'bandwidth': 10,
...@@ -139,18 +99,18 @@ def do_enb(): ...@@ -139,18 +99,18 @@ def do_enb():
'cell_id': '0x01', 'cell_id': '0x01',
"tdd_ul_dl_config": "[Configuration 6] 5ms 5UL 3DL (maximum uplink)", "tdd_ul_dl_config": "[Configuration 6] 5ms 5UL 3DL (maximum uplink)",
'inactivity_timer': 10000, 'inactivity_timer': 10000,
'ru': ru, 'ru': {
}}, 'ru_type': 'ru_ref',
"mme_list": {"1": {"mme_addr": "127.0.1.100"}}, 'ru_ref': 'RU',
'plmn_list': {"1": {'plmn': '00101'}}, },
"peers": {"1": peer_lte1, "2": peer_lte2},
"ncell_list": {'1': peercell_lte},
}) })
do('enb.jinja2.cfg', 'ors/gnb/enb.cfg', { return {'out': 'ors/enb', 'jextra': ORS_json, 'want_nr': False}
'gnb_id': "0x12345",
'gnb_id_bits': 28, def ORS_gnb(ienb):
'cell_list': {'CELL': { ienb.ishared('RU', ORS_ru)
ienb.ishared('CELL', {
'cell_type': 'nr', 'cell_type': 'nr',
'cell_kind': 'enb',
'rf_mode': 'tdd', 'rf_mode': 'tdd',
'dl_nr_arfcn': 380000, 'dl_nr_arfcn': 380000,
'nr_band': 39, 'nr_band': 39,
...@@ -161,14 +121,100 @@ def do_enb(): ...@@ -161,14 +121,100 @@ def do_enb():
'cell_id': '0x01', 'cell_id': '0x01',
"tdd_ul_dl_config": "5ms 8UL 1DL 2/10 (maximum uplink)", "tdd_ul_dl_config": "5ms 8UL 1DL 2/10 (maximum uplink)",
'inactivity_timer': 10000, 'inactivity_timer': 10000,
'ru': ru, 'ru': {
}}, 'ru_type': 'ru_ref',
"amf_list": {"1": {"amf_addr": "127.0.1.100"}}, 'ru_ref': 'RU',
"plmn_list_5g": {'1': {'plmn': '00101', 'tac': 100}}, },
"peers": {"1": peer_nr1, "2": peer_nr2}, })
"ncell_list": {'1': peercell_nr}, return {'out': 'ors/gnb', 'jextra': ORS_json, 'want_lte': False}
def do_enb():
for f in (ORS_enb,
ORS_gnb):
_do_enb_with(f)
def _do_enb_with(iru_icell_func):
ienb = Instance('enb')
opt = iru_icell_func(ienb) or {}
out = opt.get('out', 'enb/%s' % iru_icell_func.__name__)
want_lte = opt.get('want_lte', True)
want_nr = opt.get('want_nr', True)
# add 4 peer nodes
if want_lte:
ienb.ishared('PEER11', {
'peer_type': 'lte',
'x2_addr': '44.1.1.1',
})
ienb.ishared('PEER12', {
'peer_type': 'lte',
'x2_addr': '44.1.1.2',
})
if want_nr:
ienb.ishared('PEER21', {
'peer_type': 'nr',
'xn_addr': '55.1.1.1',
})
ienb.ishared('PEER22', {
'peer_type': 'nr',
'xn_addr': '55.1.1.2',
}) })
# add 2 peer cells
if want_lte:
ienb.ishared('PEERCELL1', {
'cell_type': 'lte',
'cell_kind': 'enb_peer',
'e_cell_id': '0x12345',
'pci': 35,
'dl_earfcn': 700,
'tac': 123,
})
if want_nr:
ienb.ishared('PEERCELL2', {
'cell_type': 'nr',
'cell_kind': 'enb_peer',
'nr_cell_id': '0x77712',
'gnb_id_bits': 22,
'dl_nr_arfcn': 520000,
'ssb_nr_arfcn': 520090,
'nr_band': 38,
'pci': 75,
'tac': 321,
})
jshared_instance_list = json.dumps(ienb.shared_instance_list)
jextra = opt.get('jextra', '')
json_params = """{
%(jextra)s
"sib23_file": "sib2_3.asn",
"slap_configuration": {
"tap-name": "slaptap9",
"slap-computer-partition-id": "slappart9",
"slave-instance-list": %(jshared_instance_list)s
},
"directory": {
"log": "log",
"etc": "etc",
"var": "var"
},
"slapparameter_dict": {
"enb_id": "0x1A2D0",
"gnb_id": "0x12345",
"gnb_id_bits": 28,
"com_ws_port": 9001,
"com_addr": "127.0.1.2",
"gtp_addr": "127.0.1.1",
"mme_list": {"1": {"mme_addr": "127.0.1.100"}},
"amf_list": {"1": {"amf_addr": "127.0.1.100"}},
"plmn_list": {"1": {"plmn": "00101"}},
"plmn_list_5g": {"1": {"plmn": "00101", "tac": 100}},
"nssai": {"1": {"sst": 1}}
}
}""" % locals()
j2render('enb.jinja2.cfg', '%s/enb.cfg' % out, json_params)
# TODO render drb.cfg + sib.asn for all cells # TODO render drb.cfg + sib.asn for all cells
......
...@@ -11,13 +11,49 @@ ...@@ -11,13 +11,49 @@
"response": "instance-enb-schema.json", "response": "instance-enb-schema.json",
"index": 1 "index": 1
}, },
"ru": {
"title": "→ eNB/gNB | Radio Unit",
"description": "Configuration of Radio Unit attached to eNB/gNB",
"software-type": "enb",
"shared": true,
"request": "ru/input-schema.json",
"response": "ru/schema.json",
"index": 2
},
"cell": {
"title": "→ eNB/gNB | Cell",
"description": "Configuration of Cell served by eNB/gNB",
"software-type": "enb",
"shared": true,
"request": "cell/input-schema.json",
"response": "cell/schema.json",
"index": 3
},
"peer": {
"title": "→ eNB/gNB | Peer",
"description": "Handover information about nearby eNB/gNB",
"software-type": "enb",
"shared": true,
"request": "peer/input-schema.json",
"response": "peer/schema.json",
"index": 4
},
"peer/cell": {
"title": "→ eNB/gNB | Peer Cell",
"description": "Handover information about Peer Cell served by nearby eNB/gNB",
"software-type": "enb",
"shared": true,
"request": "peer/cell/input-schema.json",
"response": "peer/cell/schema.json",
"index": 5
},
"core-network": { "core-network": {
"title": "Core Network", "title": "Core Network",
"software-type": "core-network", "software-type": "core-network",
"description": "Core Network Configuration", "description": "Core Network Configuration",
"request": "instance-core-network-input-schema.json", "request": "instance-core-network-input-schema.json",
"response": "instance-core-network-schema.json", "response": "instance-core-network-schema.json",
"index": 2 "index": 6
}, },
"core-network-slave": { "core-network-slave": {
"title": "→ Core Network | Sim Card", "title": "→ Core Network | Sim Card",
...@@ -26,7 +62,7 @@ ...@@ -26,7 +62,7 @@
"request": "sim/input-schema.json", "request": "sim/input-schema.json",
"response": "sim/schema.json", "response": "sim/schema.json",
"shared": true, "shared": true,
"index": 3 "index": 7
}, },
"ue": { "ue": {
"title": "UE", "title": "UE",
...@@ -34,7 +70,7 @@ ...@@ -34,7 +70,7 @@
"software-type": "ue", "software-type": "ue",
"request": "instance-ue-input-schema.json", "request": "instance-ue-input-schema.json",
"response": "instance-ue-schema.json", "response": "instance-ue-schema.json",
"index": 4 "index": 8
} }
} }
} }
...@@ -38,6 +38,9 @@ setUpModule, ORSTestCase = makeModuleSetUpAndTestCaseClass( ...@@ -38,6 +38,9 @@ setUpModule, ORSTestCase = makeModuleSetUpAndTestCaseClass(
os.path.abspath( os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', 'software-ors.cfg'))) os.path.join(os.path.dirname(__file__), '..', 'software-ors.cfg')))
# XXX temporary workaround for breakage when partition reference contains space.
ORSTestCase.default_partition_reference = ORSTestCase.default_partition_reference.replace(' ','-')
param_dict = { param_dict = {
'testing': True, 'testing': True,
'sim_algo': 'milenage', 'sim_algo': 'milenage',
......
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