Fixed multiprocessing support and maintanence update. (0.30.3)

This commit is contained in:
Johannes Findeisen 2026-07-02 23:02:23 +02:00
commit 2ce69ed4ec
10 changed files with 105 additions and 53 deletions

View file

@ -1,4 +1,4 @@
PYTHON_VERSION=3.11
PYTHON_VERSION=3.14
#PYTHON_VERSION = $(shell python -V | awk -F ' ' '{print $2}' | awk -F '.' '{print $1 "." $2}')
help:

View file

@ -23,7 +23,7 @@ from linspector.monitors import Monitors
__author__ = 'Johannes Findeisen'
__author_email__ = 'you@hanez.org'
__description__ = 'linspector is a infrastructure and system monitoring daemon and toolchain.'
__version__ = '0.30.2'
__version__ = '0.30.3'
sys.stderr = open('/dev/null', 'w')

View file

@ -69,6 +69,15 @@ class Configuration:
else:
return None
def __getattr__(self, name):
if name.startswith('get_') and name.endswith('_url'):
service_name = name[4:-4]
return lambda: self.get_option('services', service_name + '_url')
elif name.startswith('get_') and name.endswith('_interval'):
service_name = name[4:-9]
return lambda: int(self.get_option('services', service_name + '_interval'))
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
# this function should be used with care because it edits the main configuration. maybe it can
# be used for dynamic runtime configuration later but i need to think about it.
def set_option(self, section, option, value):

View file

@ -14,6 +14,17 @@ class Environment:
self._env = {}
self._log = log
def __getstate__(self):
state = self.__dict__.copy()
if '_log' in state:
del state['_log']
return state
def __setstate__(self, state):
self.__dict__.update(state)
from loguru import logger
self._log = logger
def get_env_var(self, key):
if key in self._env:
return self._env[key]

View file

@ -14,7 +14,8 @@ from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.schedulers.background import BackgroundScheduler
def job_execution(log, monitor):
def job_execution(monitor):
from loguru import logger as log
try:
log.debug('executing job_function for monitor identifier: ' + monitor.get_identifier() +
' with monitor object: ' + str(monitor))
@ -73,10 +74,10 @@ class Linspector:
print('Job execution mode: process')
self._log.info('Job execution mode: process')
executors = {
'default': ThreadPoolExecutor(
int(configuration.get_option('linspector', 'max_threads'))),
'processpool': ProcessPoolExecutor(
int(configuration.get_option('linspector', 'max_processes')))
'default': ProcessPoolExecutor(
int(configuration.get_option('linspector', 'max_processes'))),
'threadpool': ThreadPoolExecutor(
int(configuration.get_option('linspector', 'max_threads')))
}
job_defaults = {
'coalesce': True,
@ -130,7 +131,7 @@ class Linspector:
id=monitor + '/' +
monitor_job.get_service() + '@' +
monitors.get(monitor).get_host(),
args=[log, monitors.get(monitor)])
args=[monitors.get(monitor)])
monitor_job.set_job(scheduler_job)
self._jobs.append(monitor_job)

View file

@ -195,3 +195,14 @@ class Monitor:
def __str__(self):
return str(self.__dict__)
def __getstate__(self):
state = self.__dict__.copy()
if '_log' in state:
del state['_log']
return state
def __setstate__(self, state):
self.__dict__.update(state)
from loguru import logger
self._log = logger

View file

@ -11,6 +11,17 @@ class Service:
self._environment = environment
self._log = log
def __getstate__(self):
state = self.__dict__.copy()
if '_log' in state:
del state['_log']
return state
def __setstate__(self, state):
self.__dict__.update(state)
from loguru import logger
self._log = logger
@staticmethod
def get_str(identifier, host, service, status):
log_string = ('identifier=' + identifier +

View file

@ -28,48 +28,45 @@ class SpeedtestService(Service):
self._speedtest_time_elapsed = None
def execute(self, **kwargs):
while True:
tmp_time = time.localtime(calendar.timegm(time.gmtime()))
self._environment.set_env_var('_speedtest_last_run_date',
time.strftime('%Y-%m-%d %H:%M:%S',
tmp_time))
tmp_time = time.localtime(calendar.timegm(time.gmtime()))
self._environment.set_env_var('_speedtest_last_run_date',
time.strftime('%Y-%m-%d %H:%M:%S',
tmp_time))
self._environment.set_env_var('_speedtest_last_run_timestamp',
calendar.timegm(time.gmtime()))
self._environment.set_env_var('_speedtest_last_run_timestamp',
calendar.timegm(time.gmtime()))
start = time.perf_counter()
request = requests.get(self._configuration.get_speedtest_url(), stream=True)
size = int(request.headers.get('Content-Length'))
downloaded = 0.0
total_mbps = 0.0
maximum_speed = 0.0
total_chunks = 0.0
start = time.perf_counter()
request = requests.get(self._configuration.get_speedtest_url(), stream=True)
size = int(request.headers.get('Content-Length'))
downloaded = 0.0
total_mbps = 0.0
maximum_speed = 0.0
total_chunks = 0.0
if size is not None:
for chunk in request.iter_content(1024 * 1024):
downloaded += len(chunk)
# megabytes per second
mbps = downloaded / (time.perf_counter() - start) / (1024 * 1024)
if mbps > maximum_speed:
maximum_speed = mbps
total_chunks += 1
total_mbps += mbps
if size is not None:
for chunk in request.iter_content(1024 * 1024):
downloaded += len(chunk)
# megabytes per second
mbps = downloaded / (time.perf_counter() - start) / (1024 * 1024)
if mbps > maximum_speed:
maximum_speed = mbps
total_chunks += 1
total_mbps += mbps
self._speedtest_average_speed = total_mbps / total_chunks
self._environment.set_env_var('_speedtest_average_speed_megabyte_per_second',
str(round(self._speedtest_average_speed)))
self._speedtest_average_speed = total_mbps / total_chunks
self._environment.set_env_var('_speedtest_average_speed_megabyte_per_second',
str(round(self._speedtest_average_speed)))
self._speedtest_maximum_speed = maximum_speed
self._environment.set_env_var('_speedtest_maximum_speed_megabyte_per_second',
str(round(self._speedtest_maximum_speed)))
self._speedtest_maximum_speed = maximum_speed
self._environment.set_env_var('_speedtest_maximum_speed_megabyte_per_second',
str(round(self._speedtest_maximum_speed)))
self._speedtest_time_elapsed = time.perf_counter() - start
self._environment.set_env_var('_speedtest_time_elapsed',
str(self._speedtest_time_elapsed))
self._log.info('speedtest average: ' + str(self._speedtest_average_speed) +
', max: ' + str(self._speedtest_maximum_speed) +
', time: ' + str(self._speedtest_time_elapsed))
else:
self._log.warning('could not calculate download speed!')
time.sleep(self._configuration.get_speedtest_interval())
self._speedtest_time_elapsed = time.perf_counter() - start
self._environment.set_env_var('_speedtest_time_elapsed',
str(self._speedtest_time_elapsed))
self._log.info('speedtest average: ' + str(self._speedtest_average_speed) +
', max: ' + str(self._speedtest_maximum_speed) +
', time: ' + str(self._speedtest_time_elapsed))
else:
self._log.warning('could not calculate download speed!')

View file

@ -11,3 +11,14 @@ class Task:
self._configuration = configuration
self._environment = environment
self._log = log
def __getstate__(self):
state = self.__dict__.copy()
if '_log' in state:
del state['_log']
return state
def __setstate__(self, state):
self.__dict__.update(state)
from loguru import logger
self._log = logger

View file

@ -46,6 +46,7 @@ Pygments==2.20.0
PyNaCl==1.6.2
pysmi==2.0.0
pysnmp==7.1.27
pysqlite3==0.6.0
python-dateutil==2.9.0.post0
pytz==2026.2
pytz-deprecation-shim==0.1.0.post0