Commit ae52d597 authored by Lu Xu's avatar Lu Xu 👀

ors-amarisoft improvement

See merge request !1348
parents 7175d973 71346259
Pipeline #26529 failed with stage
#!{{ python_path }}
import json
import logging
from logging.handlers import RotatingFileHandler
import time
from websocket import create_connection
class enbWebSocket:
def __init__(self):
log_file = "{{ log_file }}"
self.logger = logging.getLogger('logger')
self.logger.setLevel(logging.INFO)
handler = RotatingFileHandler(log_file, maxBytes=30000, backupCount=2)
formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
if {{ testing }}:
return
self.ws_url = "ws://127.0.1.2:9001"
self.ws = create_connection(self.ws_url)
def close(self):
if {{ testing }}:
return
self.ws.close()
def send(self, msg):
self.ws.send(json.dumps(msg))
def recv(self, message_type):
for i in range(1,20):
r = json.loads(self.ws.recv())
if r['message'] == message_type:
return r
def stats(self):
if {{ testing }}:
r = {
'message': 'rf',
'rf_info': "CPRI: x16 HW SW\n"
}
else:
self.send({
"message": "rf",
"rf_info": True
})
r = self.recv('rf')
self.logger.info('RF info', extra={'data': r})
if __name__ == '__main__':
ws = enbWebSocket()
try:
while True:
ws.stats()
time.sleep({{ stats_period }})
finally:
ws.close()
......@@ -16,43 +16,51 @@
[template]
filename = instance.cfg
md5sum = ed72fb7c36b0bc046df177e4d6f3bf12
md5sum = 4af831f81760b37250fa50ad3812142a
[amarisoft-stats.jinja2.py]
_update_hash_filename_ = amarisoft-stats.jinja2.py
md5sum = 6e0a052bd0ca08cc0c7b4880d3deffcc
[amarisoft-rf-info.jinja2.py]
_update_hash_filename_ = amarisoft-rf-info.jinja2.py
md5sum = c930c28365c685a6066f382c9b5d8893
[lopcomm-rrh-stats.jinja2.py]
_update_hash_filename_ = lopcomm-rrh-stats.jinja2.py
md5sum = 39e191080722ac13ebc56b1e6350eb8f
[template-lte-enb-epc]
_update_hash_filename_ = instance-enb-epc.jinja2.cfg
md5sum = 833667743c693b8d5f78a2527b275a9e
md5sum = bb4435f433e100fede09ff921d42c927
[template-lte-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg
md5sum = c4584b338d26f57d5c9384956be36677
md5sum = 31707b64ab531c60fc99287238130c5b
[template-lte-gnb-epc]
_update_hash_filename_ = instance-gnb-epc.jinja2.cfg
md5sum = 1f0b18cb6d70466d002b378bb931e7da
md5sum = 9a0e36188b23831bec3cb53b226baa66
[template-lte-epc]
_update_hash_filename_ = instance-epc.jinja2.cfg
md5sum = 99c05a34678adb5a70aa64ecf2ee4e35
md5sum = acc9176fd9abe1d04c60a08cdd520eee
[template-lte-gnb]
_update_hash_filename_ = instance-gnb.jinja2.cfg
md5sum = 954dd1409b0256874f9524a40b880e47
md5sum = 6695165ae0e5116073a802f10aba3d94
[template-lte-mme]
_update_hash_filename_ = instance-mme.jinja2.cfg
md5sum = 728f4d3ae248710c23e4b73eea4d628e
md5sum = 400accb9d5bc104b1b122a7b9bc3a261
[template-lte-ue-lte]
_update_hash_filename_ = instance-ue-lte.jinja2.cfg
md5sum = 8888be35fa6e6f3e69d9373002a0e06e
md5sum = 5c1f78588a0d7447807c10765552a6e7
[template-lte-ue-nr]
_update_hash_filename_ = instance-ue-nr.jinja2.cfg
md5sum = 94ca609dc564d7ed95db09e1fa029208
md5sum = 5b032c3c9dbbd5b8552b0ebd8dcc9acf
[ue_db.jinja2.cfg]
filename = config/ue_db.jinja2.cfg
......@@ -60,7 +68,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149
[enb.jinja2.cfg]
filename = config/enb.jinja2.cfg
md5sum = 1d0bbfa1cd22740db4f1034588c946eb
md5sum = 0b2a37e6902fd5ad3d6b37e11eebec69
[sib23.asn]
filename = config/sib23.asn
......@@ -68,7 +76,7 @@ md5sum = b377dac7f1fcf94fb9ce9ebed617f36a
[gnb.jinja2.cfg]
filename = config/gnb.jinja2.cfg
md5sum = 2e051b91c39542152bc679ec78cb5926
md5sum = cbbbdeda9ddaaf5957d91ef31afe6051
[ltelogs.jinja2.sh]
filename = ltelogs.jinja2.sh
......@@ -76,7 +84,7 @@ md5sum = 1ba2e065bdf14a6411e95e80db17dcfd
[mme.jinja2.cfg]
filename = config/mme.jinja2.cfg
md5sum = 21e24367dd8213a00f6c777df33d5c03
md5sum = f002f908d9af8b4089dad0c64742dde6
[ims.jinja2.cfg]
filename = config/ims.jinja2.cfg
......@@ -89,31 +97,3 @@ md5sum = 31c166f0a1b6d664f92b8f318b233d9a
[ue-nr.jinja2.cfg]
filename = config/ue-nr.jinja2.cfg
md5sum = b3078deab008d7e81ddd88ac02b8b698
[sdr-busy-promise]
_update_hash_filename_ = promise/check_sdr_busy.py
md5sum = b0c65aefa60a9d5b9f7e7b38383db48b
[cell-gain-saturated-promise]
_update_hash_filename_ = promise/check_cell_gain_saturated.py
md5sum = 764ca8913ea40964382848cf3233c1f7
[rx-saturated-promise]
_update_hash_filename_ = promise/check_rx_saturated.py
md5sum = 40cf5389fd91845f4426ef159fb20fc0
[baseband-latency-promise]
_update_hash_filename_ = promise/check_baseband_latency.py
md5sum = 4f115e2d9bde9f268c77264dc0deb2a3
[amarisoft-stats-log-promise]
_update_hash_filename_ = promise/check_amarisoft_stats_log.py
md5sum = 5bf57a9074ea5b054d999789cbbe2c87
[cpu-temperature-promise]
_update_hash_filename_ = promise/check_cpu_temperature.py
md5sum = 6812310b65c2d95815afc2b034a5f90f
[interface-up-promise]
_update_hash_filename_ = promise/check_interface_up.py
md5sum = 44ae5693f62b7a4dbc98f700f68d8600
Changelog
=========
Version 1.0.308 (2023-02-09)
-------------
* Add support for IPv6 in UEs if available
* Use latest amarisoft version on ORS if available
* Add gnb_id_bits parameter
* Use promises from slapos.toolbox repository
* Rotate and add timestamps in enb-output.log, gnb-output.log, mme-output.log etc...
* Add support for Lopcomm RRH
* Remove UE power emission limitation
\ No newline at end of file
......@@ -6,6 +6,20 @@
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,phy.level=info,file.rotate=1G,file.path=/dev/null",
log_filename: "{{ directory['log'] }}/enb.log",
{% if slapparameter_dict.get('rrh', '') == "Lopcomm ORAN" %}
rf_driver: {
name: "sdr",
args: "dev0=/dev/sdr0@0",
cpri_mapping: "hw",
cpri_mult: 16,
cpri_rx_delay: 0,
cpri_tx_delay: 0,
ifname: "{{ slap_configuration.get('tap-name', '') }}",
cpri_debug: 2,
},
tx_gain: 0,
rx_gain: 0,
{% else %}
rf_driver: {
name: "sdr",
args: "dev0=/dev/sdr0",
......@@ -13,6 +27,7 @@
},
tx_gain: {{ slapparameter_dict.get('tx_gain', slap_configuration['configuration.default_lte_tx_gain']) }},
rx_gain: {{ slapparameter_dict.get('rx_gain', slap_configuration['configuration.default_lte_rx_gain']) }},
{% endif %}
com_addr: "127.0.1.2:9001",
mme_list: [
......@@ -76,10 +91,13 @@
cell_default: {
n_antenna_dl: N_ANTENNA_DL,
n_antenna_ul: N_ANTENNA_UL,
{% if slapparameter_dict.get('rrh', '') == "Lopcomm ORAN" %}
// uldl_config: 2,
// sp_config: 7,
{% else %}
uldl_config: 2,
sp_config: 7,
{% endif %}
n_rb_dl: N_RB_DL,
cyclic_prefix: "normal",
......@@ -134,7 +152,11 @@
pucch_dedicated: {
n1_pucch_sr_count: 11,
cqi_pucch_n_rb: 1,
{% if slapparameter_dict.get('rrh', '') == "Lopcomm ORAN" %}
// tdd_ack_nack_feedback_mode: "multiplexing",
{% else %}
tdd_ack_nack_feedback_mode: "multiplexing",
{% endif %}
},
pusch_dedicated: {
......
......@@ -68,7 +68,7 @@
gtp_addr: "127.0.1.1",
{% endif %}
gnb_id_bits: 28,
gnb_id_bits: {{ slapparameter_dict.get('gnb_id_bits', 28) }},
gnb_id: {{ slapparameter_dict.get('gnb_id', '0x12345') }},
nr_support: true,
......
......@@ -50,7 +50,17 @@
pdn_list: [
{
{% if slap_configuration.get('tun-ipv6-network', '') %}
pdn_type: "ipv4v6",
first_ipv6_prefix: "{{ netaddr.IPAddress(slap_configuration.get('tun-ipv6-addr', '')) + 1 }}",
last_ipv6_prefix: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration.get('tun-ipv6-network', '')).last) - 1 }}",
dns_addr: ["8.8.8.8", "2001:4860:4860::8888"],
{% else %}
pdn_type: "ipv4",
dns_addr: "8.8.8.8",
{% endif %}
tun_ifname: "{{ slap_configuration.get('tun-name', '') }}",
access_point_name: ["default", "internet", "ims", "sos"],
{% if slap_configuration.get('tun-name', '') %}
......@@ -58,7 +68,6 @@
last_ip_addr: "{{ netaddr.IPAddress(netaddr.IPNetwork(slap_configuration.get('tun-ipv4-network', '')).last) - 1 }}",
{% endif %}
ip_addr_shift: 2,
dns_addr: "8.8.8.8",
p_cscf_addr: ["{{ slap_configuration.get('tun-ipv4-addr', '') }}"],
erabs: [
......
......@@ -14,7 +14,7 @@ parts =
directory
lte-enb-request
lte-mme-request
cpu-temperature-promise
check-cpu-temperature.py
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -87,17 +87,19 @@ return = monitor-base-url
lte-mme-request = ${lte-mme-request:connection-monitor-base-url}
lte-enb-request = ${lte-enb-request:connection-monitor-base-url}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
......
......@@ -23,6 +23,16 @@
"type": "boolean"
},
{%- endif %}
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -144,20 +154,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -5,14 +5,20 @@ parts =
lte-enb-config
lte-enb-service
amarisoft-stats-service
sdr-busy-promise
cell-gain-saturated-promise
rx-saturated-promise
baseband-latency-promise
amarisoft-stats-log-promise
amarisoft-rf-info-service
{% if slapparameter_dict.get('rrh', '') == "Lopcomm ORAN" %}
lopcomm-rrh-stats-service
check-lopcomm-vswr.py
check-lopcomm-lof.py
check-lopcomm-rssi.py
check-cpri-lock.py
{% endif %}
{% if not slapparameter_dict.get("sub-instance", False) %}
cpu-temperature-promise
check-cpu-temperature.py
{% endif %}
check-sdr-busy.py
check-baseband-latency.py
check-amarisoft-stats-log.py
monitor-base
publish-connection-information
......@@ -82,16 +88,19 @@ offline = false
[lte-enb-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
enb-log = ${directory:log}/enb-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
sudo /opt/amarisoft/rm-tmp-lte;
sudo /opt/amarisoft/init-sdr;
sudo /opt/amarisoft/init-enb;
{{ enb }}/lteenb ${directory:etc}/enb.cfg >> ${directory:log}/enb-output.log 2>> ${directory:log}/enb-output.log
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting eNB software..." && echo) >> ${:enb-log};
tail -c 1M ${:enb-log} > ${:enb-log}.tmp;
mv ${:enb-log}.tmp ${:enb-log};
{{ enb }}/lteenb ${directory:etc}/enb.cfg >> ${:enb-log} 2>> ${:enb-log};
{% endif %}
### eNodeB (enb)
[lte-enb-service]
recipe = slapos.cookbook:wrapper
init = ${ltelogs:output} ${directory:log}/enb.log; sleep 2
......@@ -122,7 +131,37 @@ mode = 0775
url = {{ amarisoft_stats_template }}
output = ${directory:bin}/amarisoft-stats.py
### eNodeB (enb)
[amarisoft-rf-info-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-rf-info.json.log
context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py
[lopcomm-rrh-stats-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/lopcomm-rrh-stats.log
json-log-output = ${directory:var}/log/lopcomm-rrh-stats.json.log
context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
key json_log_file :json-log-output
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ lopcomm_rrh_stats_template }}
output = ${directory:bin}/lopcomm-rrh-stats.py
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
......@@ -131,6 +170,22 @@ mode = 0775
hash-files =
${amarisoft-stats-template:output}
[amarisoft-rf-info-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-rf-info-template:output}
wrapper-path = ${directory:service}/amarisoft-rf-info
mode = 0775
hash-files =
${amarisoft-rf-info-template:output}
[lopcomm-rrh-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${lopcomm-rrh-stats-template:output}
wrapper-path = ${directory:service}/lopcomm-rrh-stats
mode = 0775
hash-files =
${lopcomm-rrh-stats-template:output}
[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
......@@ -164,68 +219,74 @@ monitor-title = {{ slapparameter_dict['name'] | string }}
password = {{ slapparameter_dict['monitor-password'] | string }}
{% endif %}
[sdr-busy-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[check-sdr-busy.py]
<= macro.promise
promise = check_sdr_busy
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
[cell-gain-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cell_gain_saturated_promise }}
output = ${directory:plugins}/check-cell-gain-saturated.py
[check-baseband-latency.py]
<= macro.promise
promise = check_baseband_latency
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-txrx-delay = {{ slapparameter_dict.get("min_txrx_delay", 5) }}
[rx-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ rx_saturated_promise }}
output = ${directory:plugins}/check-rx-saturated.py
[check-amarisoft-stats-log.py]
<= macro.promise
promise = check_amarisoft_stats_log
output = ${directory:plugins}/check-amarisoft-stats-log.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
[baseband-latency-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ baseband_latency_promise }}
output = ${directory:plugins}/check-baseband-latency.py
[check-lopcomm-vswr.py]
<= macro.promise
promise = check_lopcomm_vswr
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-txrx-delay = {{ slapparameter_dict.get("min_txrx_delay", 5) }}
config-avg-txrx-delay = {{ slapparameter_dict.get("avg_txrx_delay", 7) }}
[amarisoft-stats-log-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ amarisoft_stats_log_promise }}
output = ${directory:plugins}/check-amarisoft-stats-log.py
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
[check-lopcomm-rssi.py]
<= macro.promise
promise = check_lopcomm_rssi
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[check-lopcomm-lof.py]
<= macro.promise
promise = check_lopcomm_lof
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[check-cpri-lock.py]
<= macro.promise
promise = check_cpri_lock
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-amarisoft-rf-info-log = ${amarisoft-rf-info-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[check-rx-saturated.py]
<= macro.promise
promise = check_rx_saturated
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
......@@ -13,9 +13,7 @@ info = Your SIM card has been attached to service ${slap-configuration:instance-
parts =
directory
lte-mme-request
{% if not slapparameter_dict.get("sub-instance", False) %}
cpu-temperature-promise
{% endif %}
check-cpu-temperature.py
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -76,17 +74,19 @@ config-slave-list = {{ dumps(slave_instance_list) }}
[monitor-base-url-dict]
lte-mme-request = ${lte-mme-request:connection-monitor-base-url}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
......
......@@ -14,7 +14,7 @@ parts =
directory
lte-gnb-request
lte-mme-request
cpu-temperature-promise
check-cpu-temperature.py
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -87,17 +87,19 @@ return = monitor-base-url
lte-mme-request = ${lte-mme-request:connection-monitor-base-url}
lte-gnb-request = ${lte-gnb-request:connection-monitor-base-url}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
......
......@@ -87,6 +87,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -172,9 +178,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -5,13 +5,20 @@ parts =
lte-gnb-config
lte-enb-service
amarisoft-stats-service
sdr-busy-promise
cell-gain-saturated-promise
rx-saturated-promise
amarisoft-stats-log-promise
amarisoft-rf-info-service
{% if slapparameter_dict.get('rrh', '') == "Lopcomm ORAN" %}
lopcomm-rrh-stats-service
check-lopcomm-vswr.py
check-lopcomm-lof.py
check-lopcomm-rssi.py
check-cpri-lock.py
{% endif %}
{% if not slapparameter_dict.get("sub-instance", False) %}
cpu-temperature-promise
check-cpu-temperature.py
{% endif %}
check-sdr-busy.py
check-baseband-latency.py
check-amarisoft-stats-log.py
monitor-base
publish-connection-information
......@@ -82,13 +89,18 @@ offline = false
[lte-enb-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
gnb-log = ${directory:log}/gnb-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
sudo /opt/amarisoft/rm-tmp-lte;
sudo /opt/amarisoft/init-sdr;
sudo /opt/amarisoft/init-enb;
{{ enb }}/lteenb ${directory:etc}/gnb.cfg >> ${directory:log}/gnb-output.log 2>> ${directory:log}/gnb-output.log
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting gNB software..." && echo) >> ${:gnb-log};
tail -c 1M ${:gnb-log} > ${:gnb-log}.tmp;
mv ${:gnb-log}.tmp ${:gnb-log};
{{ enb }}/lteenb ${directory:etc}/gnb.cfg >> ${:gnb-log} 2>> ${:gnb-log};
{% endif %}
### eNodeB (enb)
......@@ -115,14 +127,28 @@ context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
raw stats_period {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_stats_template }}
output = ${directory:bin}/amarisoft-stats.py
### eNodeB (enb)
[amarisoft-rf-info-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-rf-info.json.log
context =
section directory directory
key slapparameter_dict slap-configuration:configuration
key log_file :log-output
raw stats_period {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
raw testing {{ slapparameter_dict.get("testing", False) }}
raw python_path {{ buildout_directory}}/bin/pythonwitheggs
mode = 0775
url = {{ amarisoft_rf_info_template }}
output = ${directory:bin}/amarisoft-rf-info.py
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
......@@ -131,6 +157,14 @@ mode = 0775
hash-files =
${amarisoft-stats-template:output}
[amarisoft-rf-info-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-rf-info-template:output}
wrapper-path = ${directory:service}/amarisoft-rf-info
mode = 0775
hash-files =
${amarisoft-rf-info-template:output}
[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
......@@ -163,68 +197,74 @@ monitor-title = {{ slapparameter_dict['name'] | string }}
password = {{ slapparameter_dict['monitor-password'] | string }}
{% endif %}
[sdr-busy-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[check-sdr-busy.py]
<= macro.promise
promise = check_sdr_busy
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
[cell-gain-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cell_gain_saturated_promise }}
output = ${directory:plugins}/check-cell-gain-saturated.py
[check-baseband-latency.py]
<= macro.promise
promise = check_baseband_latency
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-stats-period = {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
config-min-txrx-delay = {{ slapparameter_dict.get("min_txrx_delay", 5) }}
[rx-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ rx_saturated_promise }}
output = ${directory:plugins}/check-rx-saturated.py
[check-amarisoft-stats-log.py]
<= macro.promise
promise = check_amarisoft_stats_log
output = ${directory:plugins}/check-amarisoft-stats-log.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
[check-lopcomm-vswr.py]
<= macro.promise
promise = check_lopcomm_vswr
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
[check-lopcomm-rssi.py]
<= macro.promise
promise = check_lopcomm_rssi
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
[baseband-latency-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ baseband_latency_promise }}
output = ${directory:plugins}/check-baseband-latency.py
[check-lopcomm-lof.py]
<= macro.promise
promise = check_lopcomm_lof
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-netconf-log = ${lopcomm-rrh-stats-template:json-log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-txrx-delay = {{ slapparameter_dict.get("min_txrx_delay", 5) }}
config-avg-txrx-delay = {{ slapparameter_dict.get("avg_txrx_delay", 7) }}
[amarisoft-stats-log-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ amarisoft_stats_log_promise }}
output = ${directory:plugins}/check-amarisoft-stats-log.py
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
[check-cpri-lock.py]
<= macro.promise
promise = check_cpri_lock
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-amarisoft-rf-info-log = ${amarisoft-rf-info-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[check-rx-saturated.py]
<= macro.promise
promise = check_rx_saturated
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-amarisoft-stats-log = ${amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("gnb_stats_fetch_period", 60) }}
config-max-rx-sample-db = {{ slapparameter_dict.get("max_rx_sample_db", 0) }}
......@@ -4,9 +4,9 @@ parts =
ltelogs
lte-mme-config
lte-mme-service
tun-up-promise
monitor-base
publish-connection-information
check-interface-up.py
{% if slapparameter_dict.get("iperf3", None) %}
iperf-service
port-listening-promise
......@@ -86,11 +86,15 @@ environment = AMARISOFT_PATH=/opt/amarisoft/.amarisoft
[lte-mme-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
mme-log = ${directory:log}/mme-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
rm -f ${directory:var}/lte_ue.db;
{{ mme }}/ltemme ${directory:etc}/mme.cfg >> ${directory:log}/mme-output.log 2>> ${directory:log}/mme-output.log
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting MME software..." && echo) >> ${:mme-log};
tail -c 1M ${:mme-log} > ${:mme-log}.tmp;
mv ${:mme-log}.tmp ${:mme-log};
{{ mme }}/ltemme ${directory:etc}/mme.cfg >> ${:mme-log} 2>> ${:mme-log};
{% endif %}
### MME
......@@ -177,15 +181,16 @@ recipe = slapos.cookbook:publish.serialised
epc-ipv6 = ${slap-configuration:ipv6-random}
epc-ipv4 = {{ epc_ipv4 }}
# Add custom promise to check if /dev/sdr0 is busy
[tun-up-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ interface_up_promise }}
output = ${directory:plugins}/check-tun-up.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-interface-up.py]
<= macro.promise
promise = check_interface_up
config-testing = {{ slapparameter_dict.get("testing", False) }}
{% if not slapparameter_dict.get("testing", False) %}
config-ifname = ${slap-configuration:tun-name}
{% else %}
config-ifname =
{% endif %}
config-testing = {{ slapparameter_dict.get("testing", False) }}
......@@ -21,6 +21,16 @@
"description": "Activate iperf3 UDP server",
"type": "boolean"
},
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -142,20 +152,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -3,6 +3,16 @@
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters",
"properties": {
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -124,20 +134,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -85,6 +85,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -170,9 +176,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -67,6 +67,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -152,9 +158,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -21,6 +21,16 @@
"description": "Activate iperf3 UDP server",
"type": "boolean"
},
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -142,20 +152,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -3,6 +3,16 @@
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters",
"properties": {
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -124,20 +134,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -85,6 +85,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -170,9 +176,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -67,6 +67,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -152,9 +158,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -21,6 +21,16 @@
"description": "Activate iperf3 UDP server",
"type": "boolean"
},
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -142,20 +152,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -3,6 +3,16 @@
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters",
"properties": {
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -124,20 +134,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -85,6 +85,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -170,9 +176,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -67,6 +67,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -152,9 +158,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -21,6 +21,16 @@
"description": "Activate iperf3 UDP server",
"type": "boolean"
},
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -142,20 +152,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -3,6 +3,16 @@
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters",
"properties": {
"rrh": {
"title": "RRH",
"description": "RRH",
"type": "string",
"default": "SDR",
"enum": [
"SDR",
"Lopcomm ORAN"
]
},
"tx_gain": {
"title": "Tx gain",
"description": "Tx gain (in dB)",
......@@ -124,20 +134,20 @@
"type": "number",
"default": 7
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"promise_cpu_max_spot_temp": {
"title": "Maximum CPU spot temperature",
"description": "Maximum CPU spot temperature above which CPU temperature promise will fail",
"type": "number",
"default": 90
},
"promise_cpu_avg_temperature_threshold": {
"title": "Average CPU temperature promise threshold",
"description": "If average temperature over specified duration reaches this threshold, promise will fail",
"promise_cpu_max_avg_temp": {
"title": "Maximum average CPU temperature",
"description": "If average temperature over specified period reaches this threshold, promise will fail",
"type": "number",
"default": 80
},
"promise_cpu_avg_temperature_threshold_duration": {
"title": "Average CPU temperature promise threshold duration",
"promise_cpu_avg_temp_period": {
"title": "Period of Average CPU temperature checks",
"description": "Duration during which average temperature should not exceed specified threshold",
"type": "number",
"default": 600
......
......@@ -85,6 +85,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -170,9 +176,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -67,6 +67,12 @@
"type": "string",
"default": "0x12345"
},
"gnb_id_bits": {
"title": "gNB ID bits",
"description": "Number of bits for the gNodeB global identifier. (range 22 to 32)",
"type": "number",
"default": 28
},
"pci": {
"title": "Physical Cell ID",
"description": "Physical Cell ID",
......@@ -152,9 +158,9 @@
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"enb_stats_fetch_period": {
"title": "eNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get eNB statistics",
"gnb_stats_fetch_period": {
"title": "gNB statistics fetch period (seconds)",
"description": "Describes how often a call to Amarisoft remote API is made to get gNB statistics",
"type": "number",
"default": 60
},
......
......@@ -4,8 +4,8 @@ parts =
ltelogs
lte-ue-lte-config
lte-ue-service
sdr-busy-promise
cpu-temperature-promise
check-sdr-busy.py
check-cpu-temperature.py
monitor-base
publish-connection-information
......@@ -66,11 +66,15 @@ offline = false
[lte-ue-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
ue-log = ${directory:log}/ue-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
sudo /opt/amarisoft/rm-tmp-lte | true;
{{ ue }}/lteue ${directory:etc}/ue.cfg >> ${directory:log}/ue-output.log 2>> ${directory:log}/ue-output.log
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting UE software..." && echo) >> ${:ue-log};
tail -c 1M ${:ue-log} > ${:ue-log}.tmp;
mv ${:ue-log}.tmp ${:ue-log};
{{ ue }}/lteue ${directory:etc}/ue.cfg >> ${:ue-log} 2>> ${:ue-log};
{% endif %}
### User Equipment (UE)
......@@ -121,23 +125,22 @@ monitor-title = {{ slapparameter_dict['name'] | string }}
password = {{ slapparameter_dict['monitor-password'] | string }}
{% endif %}
# Add custom promise to check if /dev/sdr0 is busy
[sdr-busy-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[check-sdr-busy.py]
<= macro.promise
promise = check_sdr_busy
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-sdr = {{ sdr }}
......@@ -4,8 +4,8 @@ parts =
ltelogs
lte-ue-nr-config
lte-ue-service
sdr-busy-promise
cpu-temperature-promise
check-sdr-busy.py
check-cpu-temperature.py
monitor-base
publish-connection-information
......@@ -68,11 +68,15 @@ offline = false
[lte-ue-sh-wrapper]
recipe = slapos.recipe.template
output = ${directory:bin}/${:_buildout_section_name_}
ue-log = ${directory:log}/ue-output.log
inline =
#!/bin/sh
{% if not slapparameter_dict.get("testing", False) %}
sudo /opt/amarisoft/rm-tmp-lte | true;
{{ ue }}/lteue ${directory:etc}/ue.cfg >> ${directory:log}/ue-output.log 2>> ${directory:log}/ue-output.log
(echo && echo && date "+[%Y/%m/%d %T.%N %Z] Starting UE software..." && echo) >> ${:ue-log};
tail -c 1M ${:ue-log} > ${:ue-log}.tmp;
mv ${:ue-log}.tmp ${:ue-log};
{{ ue }}/lteue ${directory:etc}/ue.cfg >> ${:ue-log} 2>> ${:ue-log};
{% endif %}
### User Equipment (UE)
......@@ -124,23 +128,22 @@ monitor-title = {{ slapparameter_dict['name'] | string }}
password = {{ slapparameter_dict['monitor-password'] | string }}
{% endif %}
# Add custom promise to check if /dev/sdr0 is busy
[sdr-busy-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
[macro.promise]
<= monitor-promise-base
name = ${:_buildout_section_name_}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
[check-cpu-temperature.py]
<= macro.promise
promise = check_cpu_temperature
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold", 80) }}
config-max-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temperature_threshold_duration", 600) }}
config-frequency = {{ slapparameter_dict.get("promise_cpu_temperature_frequency", 5) }}
config-max-spot-temp = {{ slapparameter_dict.get("promise_cpu_max_spot_temp", 90) }}
config-max-avg-temp = {{ slapparameter_dict.get("promise_cpu_max_avg_temp", 80) }}
config-avg-temp-duration = {{ slapparameter_dict.get("promise_cpu_avg_temp_duration", 600) }}
config-avg-flag-file = ${directory:var}/promise_cpu_avg_flag_file
[check-sdr-busy.py]
<= macro.promise
promise = check_sdr_busy
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
......@@ -31,8 +31,11 @@ context =
[amarisoft]
recipe = slapos.recipe.build
init =
import os
lte_version = "2021-09-18"
import os, re
try:
lte_version = sorted(filter(lambda x: re.match(r"v[0-9]{4}-[0-9]{2}-[0-9]{2}", x), os.listdir('/opt/amarisoft')))[-1][1:]
except FileNotFoundError:
lte_version = 'LTEVERSION'
path = "/opt/amarisoft/v" + lte_version
options['lte-version'] = lte_version
options['path'] = path
......@@ -75,7 +78,6 @@ extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
key slave_instance_list slap-configuration:slave-instance-list
raw cpu_temperature_promise ${cpu-temperature-promise:target}
[dynamic-template-lte-gnb-epc]
< = jinja2-template-base
......@@ -85,7 +87,6 @@ extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
key slave_instance_list slap-configuration:slave-instance-list
raw cpu_temperature_promise ${cpu-temperature-promise:target}
[dynamic-template-lte-epc]
< = jinja2-template-base
......@@ -95,7 +96,6 @@ extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
key slave_instance_list slap-configuration:slave-instance-list
raw cpu_temperature_promise ${cpu-temperature-promise:target}
[dynamic-template-lte-enb]
< = jinja2-template-base
......@@ -110,12 +110,8 @@ extra-context =
raw sib23 ${sib23.asn:target}
raw ltelogs_template ${ltelogs.jinja2.sh:target}
raw amarisoft_stats_template ${amarisoft-stats.jinja2.py:target}
raw sdr_busy_promise ${sdr-busy-promise:target}
raw cell_gain_saturated_promise ${cell-gain-saturated-promise:target}
raw rx_saturated_promise ${rx-saturated-promise:target}
raw baseband_latency_promise ${baseband-latency-promise:target}
raw amarisoft_stats_log_promise ${amarisoft-stats-log-promise:target}
raw cpu_temperature_promise ${cpu-temperature-promise:target}
raw amarisoft_rf_info_template ${amarisoft-rf-info.jinja2.py:target}
raw lopcomm_rrh_stats_template ${lopcomm-rrh-stats.jinja2.py:target}
raw openssl_location ${openssl:location}
raw default_dl_earfcn ${default-params:default-dl-earfcn}
raw default_lte_dl_freq ${default-params:default-lte-dl-freq}
......@@ -141,12 +137,7 @@ extra-context =
raw gnb_template ${gnb.jinja2.cfg:target}
raw ltelogs_template ${ltelogs.jinja2.sh:target}
raw amarisoft_stats_template ${amarisoft-stats.jinja2.py:target}
raw sdr_busy_promise ${sdr-busy-promise:target}
raw cell_gain_saturated_promise ${cell-gain-saturated-promise:target}
raw rx_saturated_promise ${rx-saturated-promise:target}
raw baseband_latency_promise ${baseband-latency-promise:target}
raw amarisoft_stats_log_promise ${amarisoft-stats-log-promise:target}
raw cpu_temperature_promise ${cpu-temperature-promise:target}
raw amarisoft_rf_info_template ${amarisoft-rf-info.jinja2.py:target}
raw openssl_location ${openssl:location}
raw default_dl_nr_arfcn ${default-params:default-dl-nr-arfcn}
raw default_nr_band ${default-params:default-nr-band}
......@@ -169,7 +160,6 @@ filename = instance-lte-mme.cfg
extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
raw interface_up_promise ${interface-up-promise:target}
key mme amarisoft:mme
raw mme_template ${mme.jinja2.cfg:target}
raw ims_template ${ims.jinja2.cfg:target}
......@@ -186,13 +176,10 @@ filename = instance-lte-ue-lte.cfg
extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
raw interface_up_promise ${interface-up-promise:target}
key ue amarisoft:ue
key sdr amarisoft:sdr
raw ue_lte_template ${ue-lte.jinja2.cfg:target}
raw ltelogs_template ${ltelogs.jinja2.sh:target}
raw sdr_busy_promise ${sdr-busy-promise:target}
raw cpu_temperature_promise ${cpu-temperature-promise:target}
raw openssl_location ${openssl:location}
raw default_dl_earfcn ${default-params:default-dl-earfcn}
raw default_lte_dl_freq ${default-params:default-lte-dl-freq}
......@@ -211,13 +198,10 @@ filename = instance-lte-ue-nr.cfg
extensions = jinja2.ext.do
extra-context =
raw monitor_template ${monitor2-template:output}
raw interface_up_promise ${interface-up-promise:target}
key ue amarisoft:ue
key sdr amarisoft:sdr
raw ue_nr_template ${ue-nr.jinja2.cfg:target}
raw cpu_temperature_promise ${cpu-temperature-promise:target}
raw ltelogs_template ${ltelogs.jinja2.sh:target}
raw sdr_busy_promise ${sdr-busy-promise:target}
raw openssl_location ${openssl:location}
raw default_dl_nr_arfcn ${default-params:default-dl-nr-arfcn}
raw default_nr_band ${default-params:default-nr-band}
......
#!{{ python_path }}
import json
import logging
import time
import xmltodict
from logging.handlers import RotatingFileHandler
from ncclient import manager
from ncclient.xml_ import *
from ncclient.devices.default import DefaultDeviceHandler
class LopcommNetconfClient:
def __init__(self):
log_file = "{{ log_file }}"
json_log_file = "{{ json_log_file }}"
self.logger = logging.getLogger('logger')
self.json_logger = logging.getLogger('json_logger')
self.logger.setLevel(logging.DEBUG)
self.json_logger.setLevel(logging.DEBUG)
json_handler = RotatingFileHandler(json_log_file, maxBytes=100000, backupCount=5)
json_formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
json_handler.setFormatter(json_formatter)
self.json_logger.addHandler(json_handler)
handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=5)
self.logger.addHandler(handler)
formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
handler.setFormatter(formatter)
if {{ testing }}:
return
def connect(self, host, port, user, password):
if {{ testing }}:
return
self.address = (host, port)
self.logger.info('Connecting to %s, user %s...' % (self.address, user))
self.conn = manager.connect(host=host,
port=port,
username=user,
password=password,
timeout=1800,
device_params={
'name': 'huawei'
},
hostkey_verify=False)
self.logger.info('Connection to %s successful' % (self.address,))
def subscribe(self):
# Filter not compatible between ncclient and netconf server
#result = self.conn.create_subscription(filter=('xpath', '/o-ran-fm:*'))
sub = self.conn.create_subscription()
self.logger.info('Subscription to %s successful' % (self.address,))
def get_notification(self):
result = None
while result == None:
self.logger.debug('Waiting for notification from %s...' % (self.address,))
result = self.conn.take_notification(block=True)
if result:
self.logger.debug('Got new notification from %s...' % (self.address,))
result_in_xml = result._raw
data_dict = xmltodict.parse(result_in_xml)
self.json_logger.info('', extra={'data': data_dict})
def close(self):
# Close not compatible between ncclient and netconf server
#self.conn.close()
pass
if __name__ == '__main__':
nc = LopcommNetconfClient()
while True:
try:
nc.connect("192.168.0.210", 830, "oranuser", "oranpassword")
nc.subscribe()
while True:
nc.get_notification()
except Exception as e:
nc.logger.debug('Got exception, waiting 10 seconds before reconnecting...')
nc.logger.debug(e)
time.sleep(10)
finally:
nc.close()
import errno
import json
import os
from datetime import datetime
from dateutil import parser
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
# Get latest timestamp from JSON log
def get_latest_timestamp(log):
log_number = 0
while True:
try:
f = open("{}.{}".format(log, log_number) if log_number else log, "rb")
except OSError:
return 0
try:
f.seek(0, os.SEEK_END)
try:
while f.seek(-2, os.SEEK_CUR) and f.read(1) != b'\n':
pass
except OSError:
break
l = json.loads(f.readline().decode().replace("'", '"'))
return parser.parse(l['time'])
finally:
f.close()
log_number += 1
return 0
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
def sense(self):
amarisoft_stats_log = self.getConfig('amarisoft-stats-log')
stats_period = int(self.getConfig('stats-period'))
latest_timestamp = get_latest_timestamp(amarisoft_stats_log)
delta = (datetime.now() - latest_timestamp).total_seconds()
if delta > stats_period * 2:
self.logger.error("Latest entry from amarisoft statistics log too "\
"old (%s seconds old)" % (delta,))
else:
self.logger.info("Latest entry from amarisoft statistics is "\
"%s seconds old" % (delta,))
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=1, failure_amount=1)
import errno
import json
import logging
import os
from dateutil import parser
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
# Get all data in the last "interval" seconds from JSON log
def get_data_interval(log, interval):
log_number = 0
latest_timestamp = 0
data_list = []
while True:
try:
f = open("{}.{}".format(log, log_number) if log_number else log, "rb")
except OSError:
return data_list
try:
f.seek(0, os.SEEK_END)
while True:
try:
while f.seek(-2, os.SEEK_CUR) and f.read(1) != b'\n':
pass
except OSError:
break
pos = f.tell()
l = json.loads(f.readline().decode().replace("'", '"'))
timestamp = parser.parse(l['time'])
data_list.append(l['data'])
if not latest_timestamp:
latest_timestamp = timestamp
if (latest_timestamp - timestamp).total_seconds() > interval:
return data_list
f.seek(pos, os.SEEK_SET)
finally:
f.close()
log_number += 1
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
self.__name = config.get('name', None)
self.__log_folder = config.get('log-folder', None)
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
self.__title = os.path.splitext(self.__name)[0]
self.__log_file = os.path.join(self.__log_folder, '%s.json.log' % self.__title)
self.json_logger = logging.getLogger('json_logger')
self.json_logger.setLevel(logging.INFO)
handler = logging.FileHandler(self.__log_file)
formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
handler.setFormatter(formatter)
self.json_logger.addHandler(handler)
def sense(self):
testing = self.getConfig('testing') == "True"
if testing:
self.logger.info("skipping promise")
return
amarisoft_stats_log = self.getConfig('amarisoft-stats-log')
stats_period = int(self.getConfig('stats-period'))
min_txrx_delay = float(self.getConfig('min-txrx-delay', 5))
avg_txrx_delay = float(self.getConfig('avg-txrx-delay', 7))
data_list = get_data_interval(amarisoft_stats_log, stats_period * 2)
min_txrx_delay_list = []
avg_txrx_delay_list = []
min_unavailable = False
avg_unavailable = False
for data in data_list:
rf_list = data['rf']
if not min_txrx_delay_list:
min_txrx_delay_list = [99 for x in rf_list]
if not avg_txrx_delay_list:
avg_txrx_delay_list = [99 for x in rf_list]
for i, rxtx_delay_avg in enumerate(rf_list):
min_txrx_delay_list[i] = min(min_txrx_delay_list[i], float(rf_list['rxtx_delay_min']))
min_delay = min_txrx_delay_list[i]
if min_txrx_delay_list[i] >= min_txrx_delay:
min_unavailable = True
for i, rxtx_delay_avg in enumerate(rf_list):
avg_txrx_delay_list[i] = min(avg_txrx_delay_list[i], float(rf_list['rxtx_delay_avg']))
avg_delay = avg_txrx_delay_list[i]
if avg_txrx_delay_list[i] >= avg_txrx_delay:
avg_unavailable = True
self.json_logger.info("The minimum available time for radio front end processing (ms)",
extra={'data': min_txrx_delay_list})
if not min_txrx_delay_list or not avg_txrx_delay:
self.logger.error("No TX/RX diff data available")
elif min_unavailable:
self.logger.error("The minimum available time %s (ms) for radio front end processing is higher than a threshold %s (ms) depending on the radio front end." % (min_delay, min_txrx_delay))
elif avg_unavailable:
self.logger.error("The average available time %s (ms) for radio front end processing is higher than a threshold %s (ms) depending on the radio front end." % (avg_delay, avg_txrx_delay))
else:
self.logger.info("The minimum %s (ms) and average %s (ms) available time for radio front end processing OK" % (min_delay, avg_delay))
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=1, failure_amount=1)
import errno
import json
import logging
import os
from dateutil import parser
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
# Get latest data and data from "interval" seconds ago from JSON log
def get_data_interval(log, interval):
log_number = 0
latest_timestamp = 0
latest_data = {}
while True:
try:
f = open("{}.{}".format(log, log_number) if log_number else log, "rb")
except OSError:
return latest_data, {}
try:
f.seek(0, os.SEEK_END)
while True:
try:
while f.seek(-2, os.SEEK_CUR) and f.read(1) != b'\n':
pass
except OSError:
break
pos = f.tell()
l = json.loads(f.readline().decode().replace("'", '"'))
timestamp = parser.parse(l['time'])
if not latest_timestamp:
latest_timestamp = timestamp
latest_data = l['data']
if (latest_timestamp - timestamp).total_seconds() > interval:
return latest_data, l['data']
f.seek(pos, os.SEEK_SET)
finally:
f.close()
log_number += 1
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
self.__name = config.get('name', None)
self.__log_folder = config.get('log-folder', None)
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
self.__title = os.path.splitext(self.__name)[0]
self.__log_file = os.path.join(self.__log_folder, '%s.json.log' % self.__title)
self.json_logger = logging.getLogger('json_logger')
self.json_logger.setLevel(logging.INFO)
handler = logging.FileHandler(self.__log_file)
formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
handler.setFormatter(formatter)
self.json_logger.addHandler(handler)
def sense(self):
testing = self.getConfig('testing') == "True"
amarisoft_stats_log = self.getConfig('amarisoft-stats-log')
stats_period = int(self.getConfig('stats-period'))
latest_data, previous_data = get_data_interval(amarisoft_stats_log, stats_period * 2)
def get_saturation_events(data):
if data:
return sum([x['sat'] for x in data['samples']['rx']])
return 0
saturation_events = get_saturation_events(latest_data) - \
get_saturation_events(previous_data)
self.json_logger.info("Saturation events",
extra={'data': {'recent_saturation_events': saturation_events}})
if saturation_events:
self.logger.error("Reception saturated, please lower cell_gain")
else:
self.logger.info("No saturation events on reception")
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=1, failure_amount=1)
import socket
import errno
import logging
import json
import os
import psutil
from dateutil import parser
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
# Get all data in the last "interval" seconds from JSON log
def get_data_interval(log, interval):
log_number = 0
latest_timestamp = 0
data_list = []
while True:
try:
f = open("{}.{}".format(log, log_number) if log_number else log, "rb")
except OSError:
return data_list
try:
f.seek(0, os.SEEK_END)
while True:
try:
while f.seek(-2, os.SEEK_CUR) and f.read(1) != b'\n':
pass
except OSError:
break
pos = f.tell()
l = json.loads(f.readline().decode().replace("'", '"'))
timestamp = parser.parse(l['time'])
data_list.append(l['data'])
if not latest_timestamp:
latest_timestamp = timestamp
if (latest_timestamp - timestamp).total_seconds() > interval:
return data_list
f.seek(pos, os.SEEK_SET)
finally:
f.close()
log_number += 1
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
self.__name = config.get('name', None)
self.__log_folder = config.get('log-folder', None)
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=2)
self.__title = os.path.splitext(self.__name)[0]
self.__log_file = os.path.join(self.__log_folder, '%s.json.log' % self.__title)
self.json_logger = logging.getLogger('json_logger')
self.json_logger.setLevel(logging.INFO)
handler = logging.FileHandler(self.__log_file)
formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
handler.setFormatter(formatter)
self.json_logger.addHandler(handler)
def sense(self):
max_temp = float(self.getConfig('max-temp', 90))
max_avg_temp = float(self.getConfig('max-avg-temp', 80))
max_avg_temp_duration = int(self.getConfig('max-avg-temp-duration', 300))
testing = self.getConfig('testing') == "True"
if testing:
from random import randint
cpu_temp = randint(40, 75)
else:
data = psutil.sensors_temperatures()
cpu_temp = data['coretemp'][0][1]
l = get_data_interval(self.__log_file, max_avg_temp_duration) or [{'cpu_temperature': cpu_temp}]
avg_temp = sum(map(lambda x: x['cpu_temperature'], l)) / len(l)
data = json.dumps({'cpu_temperature': cpu_temp, 'avg_cpu_temperature': avg_temp})
self.json_logger.info("Temperature data", extra={'data': data})
promise_success = True
if cpu_temp > max_temp:
self.logger.error("Temperature reached critical threshold: %s degrees celsius (threshold is %s degrees celsius)" % (cpu_temp, max_temp))
promise_success = False
if avg_temp > max_avg_temp:
self.logger.error("Average temperature over the last %s seconds reached threshold: %s degrees celsius (threshold is %s degrees celsius)" % (max_avg_temp_duration, avg_temp, max_avg_temp))
promise_success = False
if promise_success:
self.logger.info("Temperature OK")
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=3, failure_amount=2)
import socket
import errno
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=2)
def sense(self):
ifname = self.getConfig('ifname')
testing = self.getConfig('testing') == "True"
if testing:
self.logger.info("skipping promise")
return
f = open('/sys/class/net/%s/operstate' % ifname, 'r')
if f.read() == 'up\n':
self.logger.info("%s is up", ifname)
else:
self.logger.error("%s is down", ifname)
f.close()
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=3, failure_amount=2)
import errno
import json
import logging
import os
from dateutil import parser
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
# Get all data in the last "interval" seconds from JSON log
def get_data_interval(log, interval):
log_number = 0
latest_timestamp = 0
data_list = []
while True:
try:
f = open("{}.{}".format(log, log_number) if log_number else log, "rb")
except OSError:
return data_list
try:
f.seek(0, os.SEEK_END)
while True:
try:
while f.seek(-2, os.SEEK_CUR) and f.read(1) != b'\n':
pass
except OSError:
break
pos = f.tell()
l = json.loads(f.readline().decode().replace("'", '"'))
timestamp = parser.parse(l['time'])
data_list.append(l['data'])
if not latest_timestamp:
latest_timestamp = timestamp
if (latest_timestamp - timestamp).total_seconds() > interval:
return data_list
f.seek(pos, os.SEEK_SET)
finally:
f.close()
log_number += 1
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
self.__name = config.get('name', None)
self.__log_folder = config.get('log-folder', None)
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
self.__title = os.path.splitext(self.__name)[0]
self.__log_file = os.path.join(self.__log_folder, '%s.json.log' % self.__title)
self.json_logger = logging.getLogger('json_logger')
self.json_logger.setLevel(logging.INFO)
handler = logging.FileHandler(self.__log_file)
formatter = logging.Formatter('{"time": "%(asctime)s", "log_level": "%(levelname)s", "message": "%(message)s", "data": %(data)s}')
handler.setFormatter(formatter)
self.json_logger.addHandler(handler)
def sense(self):
testing = self.getConfig('testing') == "True"
amarisoft_stats_log = self.getConfig('amarisoft-stats-log')
stats_period = int(self.getConfig('stats-period'))
max_rx_sample_db = float(self.getConfig('max-rx-sample-db'))
data_list = get_data_interval(amarisoft_stats_log, stats_period * 2)
max_rx = []
saturated = False
for data in data_list:
rx_list = data['samples']['rx']
if not max_rx:
max_rx = [-99.9 for x in rx_list]
for i, rx in enumerate(rx_list):
max_rx[i] = max(max_rx[i], float(rx['max']))
if max_rx[i] >= max_rx_sample_db:
saturated = True
self.json_logger.info("RX maximum sample values (dB)",
extra={'data': max_rx})
if not max_rx:
self.logger.error("No RX samples data available")
elif saturated:
self.logger.error("RX antenna saturated, please lower rx_gain")
else:
self.logger.info("No saturation detected on RX antenna")
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=1, failure_amount=1)
import os
import errno
import subprocess
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
@implementer(interface.IPromise)
class RunPromise(GenericPromise):
def __init__(self, config):
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
def sense(self):
testing = self.getConfig('testing') == "True"
sdr = self.getConfig('sdr')
if testing:
self.logger.info("skipping promise")
return
try:
out = subprocess.check_output([
sdr + '/sdr_util', '-c', '0', 'version'], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
if e.returncode == 1 and \
("DMA channel is already opened" in e.output.decode() or \
"Device or resource busy" in e.output.decode()):
self.logger.info("eNB is using /dev/sdr0")
return
self.logger.error("eNB is not using /dev/sdr0")
def test(self):
"""
Called after sense() if the instance is still converging.
Returns success or failure based on sense results.
In this case, fail if the previous sensor result is negative.
"""
return self._test(result_count=1, failure_amount=1)
def anomaly(self):
"""
Called after sense() if the instance has finished converging.
Returns success or failure based on sense results.
Failure signals the instance has diverged.
In this case, fail if two out of the last three results are negative.
"""
return self._anomaly(result_count=1, failure_amount=1)
#!/usr/bin/env python3
from jinja2 import Template
import argparse
import os
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--delete', action='store_true')
args = parser.parse_args()
if args.delete:
directory = os.path.dirname(os.path.realpath(__file__))
test_directory = os.path.join(directory, 'test')
for f in os.listdir(directory):
if 'tdd' in f:
os.remove(os.path.join(directory, f))
for f in os.listdir(test_directory):
if 'testTDD' in f:
os.remove(os.path.join(test_directory, f))
exit()
global_context = {
'generated_file_message': "This file was generated using a jinja2 template and the render-templates script, don't modify directly."
}
......
......@@ -7,6 +7,8 @@ extends =
../../component/nghttp2/buildout.cfg
../../component/iperf3/buildout.cfg
../../component/python3/buildout.cfg
../../component/python-pynacl/buildout.cfg
../../component/bcrypt/buildout.cfg
parts +=
template
......@@ -27,7 +29,6 @@ parts +=
# apache-php
# logrotate
[template]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/${:filename}
......@@ -40,6 +41,12 @@ url = ${:_profile_base_location_}/${:_update_hash_filename_}
[amarisoft-stats.jinja2.py]
<= download-base
[amarisoft-rf-info.jinja2.py]
<= download-base
[lopcomm-rrh-stats.jinja2.py]
<= download-base
[template-lte-enb-epc]
<= download-base
......@@ -64,21 +71,6 @@ url = ${:_profile_base_location_}/${:_update_hash_filename_}
[template-lte-ue-nr]
<= download-base
[sdr-busy-promise]
<= download-base
[cell-gain-saturated-promise]
<= download-base
[rx-saturated-promise]
<= download-base
[baseband-latency-promise]
<= download-base
[amarisoft-stats-log-promise]
<= download-base
[cpu-temperature-promise]
<= download-base
[interface-up-promise]
<= download-base
[copy-to-instance]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_buildout_section_name_}
......@@ -119,7 +111,13 @@ filename = ue-nr.jinja2.cfg
recipe = zc.recipe.egg
eggs =
websocket-client
${python-pynacl:egg}
${bcrypt:egg}
xmltodict
ncclient
interpreter = pythonwitheggs
[versions]
websocket-client = 1.4.2
ncclient = 0.6.13
xmltodict = 0.13.0
......@@ -66,6 +66,7 @@ param_dict = {
'n_antenna_ul': 2,
'inactivity_timer': 17,
'gnb_id': "0x17",
'gnb_id_bits': 30,
'ssb_pos_bitmap': "10",
'amf_list': {
'10.0.0.1': {'amf_addr': '10.0.0.1'},
......@@ -133,6 +134,7 @@ def test_gnb_conf1(self):
self.assertEqual(conf['nr_cell_list'][0]['ssb_pos_bitmap'], gnb_param_dict1['ssb_pos_bitmap'])
self.assertEqual(conf['nr_cell_default']['n_id_cell'], gnb_param_dict1['pci'])
self.assertEqual(conf['gnb_id'], int(gnb_param_dict1['gnb_id'], 16))
self.assertEqual(conf['gnb_id_bits'], gnb_param_dict1['gnb_id_bits'])
for p in conf['nr_cell_default']['plmn_list']:
for n in "plmn ranac reserved tac".split():
self.assertEqual(p[n], gnb_param_dict1['plmn_list'][p['plmn']][n])
......
......@@ -66,6 +66,7 @@ param_dict = {
'n_antenna_ul': 2,
'inactivity_timer': 17,
'gnb_id': "0x17",
'gnb_id_bits': 30,
'ssb_pos_bitmap': "10",
'amf_list': {
'10.0.0.1': {'amf_addr': '10.0.0.1'},
......@@ -133,6 +134,7 @@ def test_gnb_conf1(self):
self.assertEqual(conf['nr_cell_list'][0]['ssb_pos_bitmap'], gnb_param_dict1['ssb_pos_bitmap'])
self.assertEqual(conf['nr_cell_default']['n_id_cell'], gnb_param_dict1['pci'])
self.assertEqual(conf['gnb_id'], int(gnb_param_dict1['gnb_id'], 16))
self.assertEqual(conf['gnb_id_bits'], gnb_param_dict1['gnb_id_bits'])
for p in conf['nr_cell_default']['plmn_list']:
for n in "plmn ranac reserved tac".split():
self.assertEqual(p[n], gnb_param_dict1['plmn_list'][p['plmn']][n])
......
......@@ -66,6 +66,7 @@ param_dict = {
'n_antenna_ul': 2,
'inactivity_timer': 17,
'gnb_id': "0x17",
'gnb_id_bits': 30,
'ssb_pos_bitmap': "10",
'amf_list': {
'10.0.0.1': {'amf_addr': '10.0.0.1'},
......@@ -133,6 +134,7 @@ def test_gnb_conf1(self):
self.assertEqual(conf['nr_cell_list'][0]['ssb_pos_bitmap'], gnb_param_dict1['ssb_pos_bitmap'])
self.assertEqual(conf['nr_cell_default']['n_id_cell'], gnb_param_dict1['pci'])
self.assertEqual(conf['gnb_id'], int(gnb_param_dict1['gnb_id'], 16))
self.assertEqual(conf['gnb_id_bits'], gnb_param_dict1['gnb_id_bits'])
for p in conf['nr_cell_default']['plmn_list']:
for n in "plmn ranac reserved tac".split():
self.assertEqual(p[n], gnb_param_dict1['plmn_list'][p['plmn']][n])
......
......@@ -66,6 +66,7 @@ param_dict = {
'n_antenna_ul': 2,
'inactivity_timer': 17,
'gnb_id': "0x17",
'gnb_id_bits': 30,
'ssb_pos_bitmap': "10",
'amf_list': {
'10.0.0.1': {'amf_addr': '10.0.0.1'},
......@@ -133,6 +134,7 @@ def test_gnb_conf1(self):
self.assertEqual(conf['nr_cell_list'][0]['ssb_pos_bitmap'], gnb_param_dict1['ssb_pos_bitmap'])
self.assertEqual(conf['nr_cell_default']['n_id_cell'], gnb_param_dict1['pci'])
self.assertEqual(conf['gnb_id'], int(gnb_param_dict1['gnb_id'], 16))
self.assertEqual(conf['gnb_id_bits'], gnb_param_dict1['gnb_id_bits'])
for p in conf['nr_cell_default']['plmn_list']:
for n in "plmn ranac reserved tac".split():
self.assertEqual(p[n], gnb_param_dict1['plmn_list'][p['plmn']][n])
......
......@@ -66,6 +66,7 @@ param_dict = {
'n_antenna_ul': 2,
'inactivity_timer': 17,
'gnb_id': "0x17",
'gnb_id_bits': 30,
'ssb_pos_bitmap': "10",
'amf_list': {
'10.0.0.1': {'amf_addr': '10.0.0.1'},
......@@ -133,6 +134,7 @@ def test_gnb_conf1(self):
self.assertEqual(conf['nr_cell_list'][0]['ssb_pos_bitmap'], gnb_param_dict1['ssb_pos_bitmap'])
self.assertEqual(conf['nr_cell_default']['n_id_cell'], gnb_param_dict1['pci'])
self.assertEqual(conf['gnb_id'], int(gnb_param_dict1['gnb_id'], 16))
self.assertEqual(conf['gnb_id_bits'], gnb_param_dict1['gnb_id_bits'])
for p in conf['nr_cell_default']['plmn_list']:
for n in "plmn ranac reserved tac".split():
self.assertEqual(p[n], gnb_param_dict1['plmn_list'][p['plmn']][n])
......
......@@ -302,7 +302,7 @@ slapos.rebootstrap = 4.5
slapos.recipe.build = 0.56
slapos.recipe.cmmi = 0.19
slapos.recipe.template = 5.1
slapos.toolbox = 0.129
slapos.toolbox = 0.130
smmap = 5.0.0
sniffio = 1.3.0
sortedcontainers = 2.4.0
......
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