Commit 784aa033 authored by Joanne Hugé's avatar Joanne Hugé

simpleran: add gps locked promise

parent 36fd598e
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
[template] [template]
filename = instance.cfg filename = instance.cfg
md5sum = 9013df1ac77d35a1fc8df37bb5615dcd md5sum = c1588b160ca830cbd42a295e043d4405
[template-ors] [template-ors]
filename = instance-ors.cfg filename = instance-ors.cfg
...@@ -60,7 +60,7 @@ md5sum = 52da9fe3a569199e35ad89ae1a44c30e ...@@ -60,7 +60,7 @@ md5sum = 52da9fe3a569199e35ad89ae1a44c30e
[template-enb] [template-enb]
_update_hash_filename_ = instance-enb.jinja2.cfg _update_hash_filename_ = instance-enb.jinja2.cfg
md5sum = 04b723fc2a3d5555243921823b0e087b md5sum = de757719d6cf4bd731394f50dbce8d99
[template-ors-enb] [template-ors-enb]
_update_hash_filename_ = instance-ors-enb.jinja2.cfg _update_hash_filename_ = instance-ors-enb.jinja2.cfg
...@@ -142,6 +142,10 @@ md5sum = f02fbfd31ba89cf243e2752adcae28d9 ...@@ -142,6 +142,10 @@ md5sum = f02fbfd31ba89cf243e2752adcae28d9
_update_hash_filename_ = promise/check_frequency_outofbounds.py _update_hash_filename_ = promise/check_frequency_outofbounds.py
md5sum = 7c83eab2df4f5a5d519e3eb16e4077a3 md5sum = 7c83eab2df4f5a5d519e3eb16e4077a3
[gps_lock_promise]
_update_hash_filename_ = promise/check_gps_lock.py
md5sum = c79fb837cc32bc0182ebf15078115b10
[nginx_conf.in] [nginx_conf.in]
_update_hash_filename_ = config/nginx_conf.in _update_hash_filename_ = config/nginx_conf.in
md5sum = e2496564695fb76b242c3e0f8d0ab6c3 md5sum = e2496564695fb76b242c3e0f8d0ab6c3
Changelog Changelog
========= =========
Version 1.0.384 (2024-12-16)
-------------
* Add promise to check if GPS is synchronized when enabled
Version 1.0.383 (2024-12-11) Version 1.0.383 (2024-12-11)
------------- -------------
......
...@@ -65,6 +65,9 @@ parts = ...@@ -65,6 +65,9 @@ parts =
xlog-fluentbit-service xlog-fluentbit-service
check-xlog-fluentbit-forward-host.py check-xlog-fluentbit-forward-host.py
check-xlog-fluentbit-health.py check-xlog-fluentbit-health.py
{%- endif %}
{%- if slapparameter_dict.get('gps_sync', False) %}
gps-lock-promise
{%- endif %} {%- endif %}
nginx-launcher nginx-launcher
nginx-graceful nginx-graceful
...@@ -385,3 +388,12 @@ config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output} ...@@ -385,3 +388,12 @@ config-amarisoft-stats-log = ${ru_amarisoft-stats-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }} config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
config-min-rxtx-delay = {{ slapparameter_dict.get("min_rxtx_delay", 0) }} config-min-rxtx-delay = {{ slapparameter_dict.get("min_rxtx_delay", 0) }}
{%- if slapparameter_dict.get('gps_sync', False) %}
[gps-lock-promise]
recipe = slapos.cookbook:promise.plugin
eggs = slapos.core
file = {{ gps_lock_promise }}
output = ${directory:plugins}/check-gps-lock.py
config-amarisoft-rf-info-log = ${ru_amarisoft-rf-info-template:log-output}
config-stats-period = {{ slapparameter_dict.get("enb_stats_fetch_period", 60) }}
{%- endif %}
...@@ -243,6 +243,7 @@ extra-context = ...@@ -243,6 +243,7 @@ extra-context =
raw fluent_bit_location ${fluent-bit:location} raw fluent_bit_location ${fluent-bit:location}
raw openssh_location ${openssh:location} raw openssh_location ${openssh:location}
raw openssh_output_keygen ${openssh-output:keygen} raw openssh_output_keygen ${openssh-output:keygen}
raw gps_lock_promise ${gps_lock_promise:target}
[dynamic-template-core-network] [dynamic-template-core-network]
< = jinja2-template-base < = jinja2-template-base
......
import itertools
import json
import logging
import os
import re
from dateutil import parser as dateparser
from datetime import datetime
from zope.interface import implementer
from slapos.grid.promise import interface
from slapos.grid.promise.generic import GenericPromise
def iter_reverse_lines(f):
"""
Read lines from the end of the file
"""
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:
return
pos = f.tell()
yield f.readline()
f.seek(pos, os.SEEK_SET)
def iter_logrotate_file_handle(path, mode='r'):
"""
Yield successive file handles for rotated logs
(XX.log, XX.log.1, XX.log.2, ...)
"""
for i in itertools.count():
path_i = path + str(i or '')
try:
with open(path_i, mode) as f:
yield f
except OSError:
break
def get_json_log_data_interval(json_log_file, interval):
"""
Get all data in the last "interval" seconds from JSON log
Reads rotated logs too (XX.log, XX.log.1, XX.log.2, ...)
"""
current_time = datetime.now()
data_list = []
for f in iter_logrotate_file_handle(json_log_file, 'rb'):
for line in iter_reverse_lines(f):
l = json.loads(line)
timestamp = dateparser.parse(l['time'])
if (current_time - timestamp).total_seconds() > interval:
return data_list
data_list.append(l['data'])
return data_list
class JSONPromise(GenericPromise):
def __init__(self, config):
self.__name = config.get('name', None)
self.__log_folder = config.get('log-folder', None)
super(JSONPromise, self).__init__(config)
json_log_name = os.path.splitext(self.__name)[0] + '.json.log'
self.__json_log_file = os.path.join(self.__log_folder, json_log_name)
self.json_logger = self.__make_json_logger(self.__json_log_file)
def __make_json_logger(self, json_log_file):
logger = logging.getLogger('json-logger')
logger.setLevel(logging.INFO)
handler = logging.FileHandler(json_log_file)
formatter = logging.Formatter(
'{"time": "%(asctime)s", "log_level": "%(levelname)s"'
', "message": "%(message)s", "data": %(data)s}'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
@implementer(interface.IPromise)
class RunPromise(JSONPromise):
def __init__(self, config):
super(RunPromise, self).__init__(config)
self.setPeriodicity(minute=1)
self.amarisoft_rf_info_log = self.getConfig('amarisoft-rf-info-log')
self.stats_period = int(self.getConfig('stats-period'))
def sense(self):
data_list = get_json_log_data_interval(self.amarisoft_rf_info_log, self.stats_period * 2)
if len(data_list) < 1:
self.logger.error("rf_info: stale data")
return
rf_info_text = data_list[0]['rf_info']
if 'Sync: gps (locked)' in rf_info_text:
self.logger.info("GPS locked")
else:
self.logger.error("GPS not locked")
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)
...@@ -222,6 +222,9 @@ update-command = ${:command} ...@@ -222,6 +222,9 @@ update-command = ${:command}
<= setcap <= setcap
exe = ${dnsmasq:location}/sbin/dnsmasq exe = ${dnsmasq:location}/sbin/dnsmasq
[gps_lock_promise]
<= download-base
[versions] [versions]
websocket-client = 1.4.2 websocket-client = 1.4.2
ncclient = 0.6.13 ncclient = 0.6.13
......
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