Commit 299b8d57 authored by Jérome Perrin's avatar Jérome Perrin

Merge remote-tracking branch 'upstream/master' into zope4py2

parents 1902a608 4968d55c
......@@ -525,6 +525,46 @@
"default": true,
"type": "boolean"
},
"coverage": {
"type": "object",
"title": "Coverage",
"description": "Coverage configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"description": "Collect python coverage data during test run.",
"default": false,
"type": "boolean"
},
"include": {
"description": "File name patterns to include in coverage data, relative to software buildout's directory. Default to all repositories defined in software by ${erp5_repository_list:repository_id_list}.",
"type": "array",
"items": {
"type": "string"
},
"examples": [
[
"parts/erp5/*",
"parts/custom-repository/*",
"develop-eggs/custom-egg/*"
]
]
},
"branch": {
"description": "Enable branch coverage",
"type": "boolean",
"default": false
},
"upload-url": {
"description": "URL to upload coverage data. This is interpreted as a RFC 6570 URI Template, with the following parameters: test_name, test_result_id and test_result_revision. The request will be a PUT request with the coverage file content as body, suitable for WebDav servers. If the URL contains user and password, they will be used to attempt authentication using Digest and Basic authentication schemes.",
"type": "string",
"format": "uri",
"examples": [
"https://user:password@example.com/{test_result_id}/{test_name}.coverage.sqlite3"
]
}
}
},
"node-count": {
"description": "Number of tests this instance can execute in parallel. This must be at least equal to the number of nodes configured on testnode running the test",
"default": 3,
......
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"description": "Values returned by Fluent-bit instantiation.",
"additionalProperties": false,
"properties": {},
"type": "object"
}
......@@ -7,6 +7,7 @@
"title": "Default",
"description": "Fluent-bit",
"request": "instance-input-schema.json",
"response": "instance-output-schema.json",
"index": 0
}
}
......
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Values returned by drone swarm (default) instantiation",
"additionalProperties": false,
"properties": {},
"type": "object"
}
......@@ -8,6 +8,7 @@
"software-type": "default",
"description": "Default",
"request": "instance-input-schema.json",
"response": "instance-output-schema.json",
"index": 0
}
}
......
......@@ -125,7 +125,7 @@ inline =
exec "$basedir/bin/mysqld" --defaults-file='{{defaults_file}}' "$@"
[versions]
coverage = 4.5.1
coverage = 5.5
ecdsa = 0.13
mysqlclient = 1.3.12
pycrypto = 2.6.1
......
#!{{ 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 }}:
from random import randint
r = {
'message': 'stats',
'samples': {'rx': [
{'sat': 0, 'max': randint(-500,-100) / 10.0},
{'sat': 0, 'max': randint(-500,-100) / 10.0},
]}
}
else:
self.send({
"message": "stats",
"samples": True,
"rf": True
})
r = self.recv('stats')
self.logger.info('Samples stats', extra={'data': r})
if __name__ == '__main__':
ws = enbWebSocket()
try:
while True:
ws.stats()
time.sleep({{ stats_period }})
finally:
ws.close()
......@@ -16,27 +16,31 @@
[template]
filename = instance.cfg
md5sum = 6e5c63c35b13f8920ea13e24c27d7b50
md5sum = 8a6e6e065e3eeffc3d5d4d8bb68f4a43
[amarisoft-stats.jinja2.py]
_update_hash_filename_ = amarisoft-stats.jinja2.py
md5sum = 6e0a052bd0ca08cc0c7b4880d3deffcc
[template-lte-enb-epc]
_update_hash_filename_ = instance-enb-epc.jinja2.cfg
md5sum = 42959a0525d2e06e61310836656d5de6
md5sum = f15c81f0f4c2aa59bc794d855e7d01c0
[template-lte-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg
md5sum = 04dbc8396cb112e86d5d1564df033b82
md5sum = 68362f58fccc534757b6bb566d285fa6
[template-lte-gnb-epc]
_update_hash_filename_ = instance-gnb-epc.jinja2.cfg
md5sum = 2b589e23486661c52f379c4d9b35e7a9
md5sum = dd98a2d0cb4fadc869638166bde6d9c8
[template-lte-epc]
_update_hash_filename_ = instance-epc.jinja2.cfg
md5sum = 9ffc54bf10757403c4b1809d1102ea52
md5sum = 48755eb0b2eb87f80130d42026ea8f7b
[template-lte-gnb]
_update_hash_filename_ = instance-gnb.jinja2.cfg
md5sum = 01f4de736594a0810a66f847753fadf1
md5sum = 45ff1f5f4e98867449dcff7caae678f8
[template-lte-mme]
_update_hash_filename_ = instance-mme.jinja2.cfg
......@@ -44,11 +48,11 @@ md5sum = 728f4d3ae248710c23e4b73eea4d628e
[template-lte-ue-lte]
_update_hash_filename_ = instance-ue-lte.jinja2.cfg
md5sum = 4517baee6700afbf78de3b2d7dd4ba22
md5sum = 38ef57bc93f53338187bd9c39fe4f0c5
[template-lte-ue-nr]
_update_hash_filename_ = instance-ue-nr.jinja2.cfg
md5sum = 45e7ade19a579d6750d2733a0659dc1a
md5sum = eaaf8fb3806a941f188eecdb90d38bda
[ue_db.jinja2.cfg]
filename = config/ue_db.jinja2.cfg
......@@ -56,7 +60,7 @@ md5sum = dcaac06553a3222b14c0013a13f4a149
[enb.jinja2.cfg]
filename = config/enb.jinja2.cfg
md5sum = 9fd6abb1e69fe59af0dafbb336f24f8d
md5sum = 1c9909f691b5bb00d3fc30d41613e26a
[sib23.asn]
filename = config/sib23.asn
......@@ -64,7 +68,7 @@ md5sum = b377dac7f1fcf94fb9ce9ebed617f36a
[gnb.jinja2.cfg]
filename = config/gnb.jinja2.cfg
md5sum = 407c20d11c5417636b985fe4886bf135
md5sum = 16698f0b4ea3e6cf40e9f070af2439e5
[ltelogs.jinja2.sh]
filename = ltelogs.jinja2.sh
......@@ -88,8 +92,24 @@ md5sum = b3078deab008d7e81ddd88ac02b8b698
[sdr-busy-promise]
_update_hash_filename_ = promise/check_sdr_busy.py
md5sum = 2e25144d1d952ed5bebb194360b60afb
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
[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 = 8e8f836554d81e2979e39dfb50e05b88
[interface-up-promise]
_update_hash_filename_ = promise/check_interface_up.py
md5sum = 7797aeaf02e055226249f2c08209775a
md5sum = 44ae5693f62b7a4dbc98f700f68d8600
......@@ -14,6 +14,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']) }},
com_addr: "127.0.1.2:9001",
mme_list: [
{% if slapparameter_dict.get('mme_list', '') %}
......
......@@ -38,6 +38,7 @@
tx_gain: {{ slapparameter_dict.get('tx_gain', slap_configuration['configuration.default_nr_tx_gain']) }},
rx_gain: {{ slapparameter_dict.get('rx_gain', slap_configuration['configuration.default_nr_rx_gain']) }},
{% endif %}
com_addr: "127.0.1.2:9001",
amf_list: [
{% if slapparameter_dict.get('amf_list', '') %}
......
......@@ -14,6 +14,7 @@ parts =
directory
lte-enb-request
lte-mme-request
cpu-temperature-promise
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -59,6 +60,7 @@ cert-file = {{ slap_connection['cert-file'] }}
sla-computer_guid = {{ slap_connection['computer-id'] }}
config-monitor-password = ${monitor-htpasswd:passwd}
config-sub-instance = true
[lte-mme-request]
<= request-common-base
......@@ -85,6 +87,14 @@ 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
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
<= monitor-publish
......
......@@ -119,6 +119,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -4,7 +4,14 @@ parts =
ltelogs
lte-enb-config
lte-enb-service
amarisoft-stats-service
sdr-busy-promise
cell-gain-saturated-promise
rx-saturated-promise
amarisoft-stats-log-promise
{% if not slapparameter_dict.get("sub-instance", False) %}
cpu-temperature-promise
{% endif %}
monitor-base
publish-connection-information
......@@ -97,6 +104,30 @@ environment =
LD_LIBRARY_PATH={{ openssl_location }}/lib
AMARISOFT_PATH=/opt/amarisoft/.amarisoft
[amarisoft-stats-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-stats.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_stats_template }}
output = ${directory:bin}/amarisoft-stats.py
### eNodeB (enb)
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
wrapper-path = ${directory:service}/amarisoft-stats
mode = 0775
hash-files =
${amarisoft-stats-template:output}
[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
......@@ -130,7 +161,6 @@ 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
......@@ -138,3 +168,44 @@ file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
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
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) }}
[rx-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ rx_saturated_promise }}
output = ${directory:plugins}/check-rx-saturated.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) }}
[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}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-maxtemp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
......@@ -26,6 +26,12 @@
"title": "Use IPv4",
"description": "Set to true to use IPv4 for AMF / MME addresses",
"type": "boolean"
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -13,6 +13,9 @@ 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 %}
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -73,6 +76,14 @@ 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
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
<= monitor-publish
......
......@@ -14,6 +14,7 @@ parts =
directory
lte-gnb-request
lte-mme-request
cpu-temperature-promise
publish-connection-information
{% for part in part_list -%}
{{ ' %s' % part }}
......@@ -59,6 +60,7 @@ cert-file = {{ slap_connection['cert-file'] }}
sla-computer_guid = {{ slap_connection['computer-id'] }}
config-monitor-password = ${monitor-htpasswd:passwd}
config-sub-instance = true
[lte-mme-request]
<= request-common-base
......@@ -85,6 +87,14 @@ 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
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
[publish-connection-information]
recipe = slapos.cookbook:publish.serialised
<= monitor-publish
......
......@@ -171,6 +171,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -4,7 +4,14 @@ parts =
ltelogs
lte-gnb-config
lte-enb-service
amarisoft-stats-service
sdr-busy-promise
cell-gain-saturated-promise
rx-saturated-promise
amarisoft-stats-log-promise
{% if not slapparameter_dict.get("sub-instance", False) %}
cpu-temperature-promise
{% endif %}
monitor-base
publish-connection-information
......@@ -98,6 +105,30 @@ environment =
LD_LIBRARY_PATH={{ openssl_location }}/lib
AMARISOFT_PATH=/opt/amarisoft/.amarisoft
[amarisoft-stats-template]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
log-output = ${directory:var}/log/amarisoft-stats.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_stats_template }}
output = ${directory:bin}/amarisoft-stats.py
### eNodeB (enb)
[amarisoft-stats-service]
recipe = slapos.cookbook:wrapper
command-line = ${amarisoft-stats-template:output}
wrapper-path = ${directory:service}/amarisoft-stats
mode = 0775
hash-files =
${amarisoft-stats-template:output}
[config-base]
recipe = slapos.recipe.template:jinja2
extensions = jinja2.ext.do
......@@ -130,7 +161,6 @@ 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
......@@ -138,3 +168,44 @@ file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
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
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) }}
[rx-saturated-promise]
recipe = slapos.cookbook:promise.plugin
eggs =
slapos.core
python-dateutil
file = {{ rx_saturated_promise }}
output = ${directory:plugins}/check-rx-saturated.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) }}
[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}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-max-temp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
......@@ -117,6 +117,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -99,6 +99,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -169,6 +169,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -151,6 +151,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -117,6 +117,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -99,6 +99,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -169,6 +169,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -151,6 +151,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -117,6 +117,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -99,6 +99,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -169,6 +169,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -151,6 +151,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -117,6 +117,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -99,6 +99,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -169,6 +169,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -151,6 +151,24 @@
"title": "Use IPv4",
"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",
"type": "number",
"default": 60
},
"max_rx_sample_db": {
"title": "Maximum RX sample value (dB)",
"description": "Maximum RX sample threshold above which RX saturated promise will fail",
"type": "number",
"default": 0
},
"promise_cpu_temperature_threshold": {
"title": "CPU temperature promise threshold",
"description": "Temperature threshold above which CPU temperature promise will fail",
"type": "number",
"default": 80
}
}
}
......@@ -5,6 +5,7 @@ parts =
lte-ue-lte-config
lte-ue-service
sdr-busy-promise
cpu-temperature-promise
monitor-base
publish-connection-information
......@@ -128,3 +129,11 @@ file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-maxtemp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
......@@ -5,6 +5,7 @@ parts =
lte-ue-nr-config
lte-ue-service
sdr-busy-promise
cpu-temperature-promise
monitor-base
publish-connection-information
......@@ -131,3 +132,11 @@ file = {{ sdr_busy_promise }}
output = ${directory:plugins}/check-sdr-busy.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-sdr = {{ sdr }}
[cpu-temperature-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ cpu_temperature_promise }}
output = ${directory:plugins}/check-cpu-temperature.py
config-testing = {{ slapparameter_dict.get("testing", False) }}
config-maxtemp = {{ slapparameter_dict.get("promise_cpu_temperature_threshold", 80) }}
......@@ -75,6 +75,7 @@ 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
......@@ -84,6 +85,7 @@ 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
......@@ -93,6 +95,7 @@ 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
......@@ -106,7 +109,12 @@ extra-context =
raw enb_template ${enb.jinja2.cfg:target}
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 amarisoft_stats_log_promise ${amarisoft-stats-log-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}
......@@ -129,7 +137,12 @@ extra-context =
key sdr amarisoft:sdr
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 amarisoft_stats_log_promise ${amarisoft-stats-log-promise:target}
raw cpu_temperature_promise ${cpu-temperature-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}
......@@ -141,6 +154,7 @@ extra-context =
raw default_nr_inactivity_timer ${default-params:default-nr-inactivity-timer}
raw min_frequency ${default-params:min-frequency}
raw max_frequency ${default-params:max-frequency}
raw python_path ${python3:location}
[dynamic-template-lte-mme]
< = jinja2-template-base
......@@ -172,6 +186,7 @@ extra-context =
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}
......@@ -194,6 +209,7 @@ extra-context =
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}
......
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 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 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):
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 = int(self.getConfig('max-temp', 80))
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]
data = json.dumps({'cpu_temperature': cpu_temp})
if cpu_temp > max_temp:
self.logger.error("Temperature too high (%s > %s)" % (cpu_temp, max_temp))
self.json_logger.info("Temperature too high (%s > %s)" % (cpu_temp, max_temp), extra={'data': data})
else:
self.logger.info("Temperature OK")
self.json_logger.info("Temperature OK", extra={'data': data})
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)
......@@ -5,28 +5,17 @@ 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):
"""
Called when initialising the promise before testing.
Sets the configuration and the periodicity.
"""
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=2)
def sense(self):
"""
Called every time the promise is tested.
Signals a positive or negative result.
In this case, check whether the file exists.
"""
ifname = self.getConfig('ifname')
testing = self.getConfig('testing') == "True"
......
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)
......@@ -6,28 +6,17 @@ 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):
"""
Called when initialising the promise before testing.
Sets the configuration and the periodicity.
"""
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
def sense(self):
"""
Called every time the promise is tested.
Signals a positive or negative result.
In this case, check whether the file exists.
"""
testing = self.getConfig('testing') == "True"
sdr = self.getConfig('sdr')
......
......@@ -6,6 +6,7 @@ extends =
../../component/logrotate/buildout.cfg
../../component/nghttp2/buildout.cfg
../../component/iperf3/buildout.cfg
../../component/python3/buildout.cfg
parts +=
template
......@@ -21,6 +22,7 @@ parts +=
ue-nr.jinja2.cfg
sib23.asn
iperf3
eggs
# unimplemented parts - the http monitor and better log handling using logrotate
# apache-php
# logrotate
......@@ -35,6 +37,9 @@ output = ${buildout:directory}/template.cfg
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/${:_update_hash_filename_}
[amarisoft-stats.jinja2.py]
<= download-base
[template-lte-enb-epc]
<= download-base
......@@ -61,7 +66,14 @@ url = ${:_profile_base_location_}/${:_update_hash_filename_}
[sdr-busy-promise]
<= download-base
[cell-gain-saturated-promise]
<= download-base
[rx-saturated-promise]
<= download-base
[amarisoft-stats-log-promise]
<= download-base
[cpu-temperature-promise]
<= download-base
[interface-up-promise]
<= download-base
......@@ -100,3 +112,13 @@ filename = ue-lte.jinja2.cfg
[ue-nr.jinja2.cfg]
<= copy-config-to-instance
filename = ue-nr.jinja2.cfg
[eggs]
recipe = zc.recipe.egg
eggs =
websocket-client
python-dateutil
interpreter = pythonwitheggs
[versions]
websocket-client = 1.4.2
......@@ -74,6 +74,7 @@ parts +=
mroonga-mariadb
tesseract
zabbix-agent
.coveragerc
# Buildoutish
eggs-all-scripts
......@@ -354,14 +355,52 @@ entry-points =
runUnitTest=runUnitTest:main
scripts = runUnitTest
initialization =
import glob, os, sys
import glob, os, sys, json
buildout_directory = '''${buildout:directory}'''
parts_directory = '''${buildout:parts-directory}'''
repository_id_list = \
'''${erp5_repository_list:repository_id_list}'''.split()[::-1]
# read testrunner configuration from slapos instance parameters to
# configure coverage if enabled.
with open(os.environ['ERP5_TEST_RUNNER_CONFIGURATION']) as f:
test_runner_configuration = json.load(f)
test_runner_configuration.setdefault('coverage', {})
test_runner_configuration['coverage'].setdefault('enabled', False)
coverage_process = None
if test_runner_configuration['coverage']['enabled']:
test_runner_configuration['coverage'].setdefault(
'include', [os.path.join('parts', repo, '*') for repo in repository_id_list])
assets_directory = ''
test_name = sys.argv[-1].replace(':', '_')
if os.environ.get('SLAPOS_TEST_LOG_DIRECTORY'):
assets_directory = os.path.join(os.environ['SLAPOS_TEST_LOG_DIRECTORY'], test_name)
if not os.path.exists(assets_directory):
os.makedirs(assets_directory)
coverage_data_file = os.path.abspath(
os.path.join(assets_directory, 'coverage.sqlite3'))
curdir = os.path.abspath(os.curdir)
# change current directory when importing coverage so that it considers paths
# relative to the root of the software
os.chdir(buildout_directory)
import coverage
coverage_process = coverage.Coverage(
include=test_runner_configuration['coverage']['include'],
data_file=coverage_data_file,
branch=test_runner_configuration['coverage'].get('branch'),
)
coverage_process.set_option('run:relative_files', 'true')
coverage_process.set_option('run:plugins', ['erp5_coverage_plugin'])
coverage_process.start()
os.chdir(curdir)
import Products
Products.__path__[:0] = filter(None,
os.getenv('INSERT_PRODUCTS_PATH', '').split(os.pathsep))
os.environ['ZOPE_SCRIPTS'] = ''
parts_directory = '''${buildout:parts-directory}'''
repository_id_list = \
'''${erp5_repository_list:repository_id_list}'''.split()[::-1]
os.environ['erp5_tests_bt5_path'] = ','.join(
os.path.join(parts_directory, x, 'bt5') for x in repository_id_list)
extra_path_list = '''${:extra-paths}'''.split()
......@@ -374,6 +413,59 @@ initialization =
sys.path[:0] = sum((
glob.glob(os.path.join(x, 'Products', '*', 'tests'))
for x in os.getenv('INSERT_PRODUCTS_PATH', '').split(os.pathsep)), [])
import runUnitTest
try:
sys.exit(runUnitTest.main())
finally:
if coverage_process:
coverage_process.stop()
coverage_process.save()
# upload the coverage so that they can be combined from another machine
upload_url = test_runner_configuration['coverage'].get('upload-url')
if upload_url:
import requests
import time
import uritemplate
from six.moves.urllib.parse import urlparse
auth_list = (None, )
parsed_url = urlparse(upload_url)
if parsed_url.username:
# try Digest and Basic authentication and retry 5 times to tolerate transiant errors
auth_list = (
requests.auth.HTTPDigestAuth(parsed_url.username, parsed_url.password),
requests.auth.HTTPBasicAuth(parsed_url.username, parsed_url.password),
) * 5
url = uritemplate.URITemplate(upload_url).expand(
test_name=test_name,
# Environment variables are set in parts/erp5/product/ERP5Type/tests/runTestSuite.py
test_result_id=os.environ.get('ERP5_TEST_RESULT_ID', 'unknown_test_result_id'),
test_result_revision=os.environ.get('ERP5_TEST_RESULT_REVISION', 'unknown_test_result_revision'),
)
for auth in auth_list:
with open(coverage_data_file, 'rb') as f:
resp = requests.put(url, data=f, auth=auth)
if resp.ok:
# print just the hostname, not to include the auth part
print('Uploaded coverage data to {parsed_url.hostname}'.format(parsed_url=parsed_url))
break
print('Error {resp.status_code} uploading coverage data to {parsed_url.hostname} with {auth.__class__.__name__}'.format(
resp=resp, parsed_url=parsed_url, auth=auth))
time.sleep(1)
else:
sys.stderr.write('Error uploading coverage data to {parsed_url.hostname}\n'.format(parsed_url=parsed_url))
[.coveragerc]
recipe = slapos.recipe.template
output = ${buildout:directory}/${:_buildout_section_name_}
inline =
# coverage configuration file, useful when making html report
[run]
plugins =
erp5_coverage_plugin
relative_files = true
[test-suite-runner]
# XXX: Workaround for fact ERP5Type is not an distribution and does not
......@@ -481,7 +573,6 @@ eggs = ${neoppod:eggs}
SOAPpy
chardet
collective.recipe.template
coverage
erp5diff
interval
ipdb
......@@ -649,6 +740,12 @@ waitress-patches =
${:_profile_base_location_}/../../component/egg-patch/waitress/CVE-2022-24761-6.patch#85fc9c4105eabee3ff71c800b2ddf63b
waitress-patch-options = -p1
# neoppod installs bin/coverage so we inject erp5 plugin here so that coverage script can use it in report
[neoppod]
eggs +=
erp5_coverage_plugin
[eggs-all-scripts]
recipe = zc.recipe.egg
eggs =
......@@ -731,6 +828,7 @@ PyStemmer = 1.3.0
Pympler = 0.4.3
StructuredText = 2.11.1
WSGIUtils = 0.7
erp5-coverage-plugin = 0.0.1
erp5diff = 0.8.1.8
five.formlib = 1.0.4
google-api-python-client = 1.6.1
......
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