Basic scheduling works now but execution of services is not available. Lot of stuff done...

This commit is contained in:
Johannes Findeisen 2022-09-30 02:14:36 +02:00
commit 8808b657c4
54 changed files with 538 additions and 199 deletions

View file

@ -32,7 +32,7 @@ from linspector.core.environment import Environment
from linspector.core.linspector import Linspector from linspector.core.linspector import Linspector
from linspector.core.monitors import Monitors from linspector.core.monitors import Monitors
__version__ = '0.19.10' __version__ = '0.19.11.dev1'
__author__ = 'Johannes Findeisen <you@hanez.org>' __author__ = 'Johannes Findeisen <you@hanez.org>'
logger = logging.getLogger('linspector') logger = logging.getLogger('linspector')
@ -125,6 +125,7 @@ def main():
try: try:
linspector = Linspector(configuration, environment, monitors, plugins, scheduler) linspector = Linspector(configuration, environment, monitors, plugins, scheduler)
#linspector.print_debug()
except Exception as err: except Exception as err:
logger.critical('[linspector] core initialization error: {0}'.format(err)) logger.critical('[linspector] core initialization error: {0}'.format(err))
sys.exit(1) sys.exit(1)

View file

@ -24,7 +24,7 @@ members = superadmin@example.com,developers@example.com
; scheduler configuration ; scheduler configuration
start_scheduler = true start_scheduler = true
max_threads = 3500 max_threads = 3500
max_processes = 100 max_processes = 10
timezone = CET timezone = CET
; hostgroup parents; if the hostgroup "group1" is down, don't alert for the hosts in group2. see TODO.txt for more ; hostgroup parents; if the hostgroup "group1" is down, don't alert for the hosts in group2. see TODO.txt for more

View file

@ -19,6 +19,7 @@ tasks = redis
; internally to single hosts to become a monitor for each host internally. some more ideas are in ;) ; internally to single hosts to become a monitor for each host internally. some more ideas are in ;)
; hosts with added hostgroup defined in main configuration file ; hosts with added hostgroup defined in main configuration file
hosts = 192.168.0.1,192.168.23.24,@group1,@testgroup1 hosts = 192.168.0.1,192.168.23.24,@group1,@testgroup1
hostgroup = group1
user = USERNAME user = USERNAME
password = PASSWORD password = PASSWORD
info = Cable Provider info = Cable Provider

View file

@ -1,2 +1,3 @@
[monitor] [monitor]
service = misc.dummy service = misc.dummy
interval = 1

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 60

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 60

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 120

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 50

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 62

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 30

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 60

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 60

View file

@ -0,0 +1,3 @@
[monitor]
service = misc.dummy
interval = 240

View file

@ -7,9 +7,7 @@ import configparser
import glob import glob
import os import os
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
# TODO: check for all required configuration options and set defaults if needed. do this only for # TODO: check for all required configuration options and set defaults if needed. do this only for
@ -21,7 +19,8 @@ class Configuration:
self.__configuration_path = configuration_path self.__configuration_path = configuration_path
self.__environment = environment self.__environment = environment
logger.info('reading configuration file: ' + configuration_path + '/linspector.conf') log('info', __name__, 'reading configuration file: ' + configuration_path +
'/linspector.conf')
if os.path.isfile(configuration_path + '/linspector.conf'): if os.path.isfile(configuration_path + '/linspector.conf'):
try: try:
self.__configuration.read(configuration_path + '/linspector.conf', 'utf-8') self.__configuration.read(configuration_path + '/linspector.conf', 'utf-8')

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
class Environment: class Environment:
@ -13,6 +11,7 @@ class Environment:
Object for storing environment variables at runtime. These variables must not affect the Object for storing environment variables at runtime. These variables must not affect the
stability or runtime of Linspector. stability or runtime of Linspector.
""" """
def __init__(self): def __init__(self):
self.__env = {} self.__env = {}
@ -20,17 +19,20 @@ class Environment:
if key in self.__env: if key in self.__env:
return self.__env[key] return self.__env[key]
else: else:
logger.info('environment var "' + key + '" not found! could be that it is set later at ' log('warning', __name__, 'environment var "' + key + '" not found! could be that it is '
'runtime. if you encounter any errors ' 'set later at runtime. if you '
'executing monipyd, something is wrong in the ' 'encounter any errors executing '
'logic of the code. please consider reporting ' 'linspector, something is wrong '
'this as a bug! btw. INFO is not an ERROR! ' 'in the logic of the code. please '
'monipyd should work even with missing ' 'consider reporting this as a '
'environment variables.') 'bug! btw. WARNING is not an '
'ERROR! Linspector should work '
'even with missing environment '
'variables.')
return None return None
def set_env_var(self, key, value): def set_env_var(self, key, value):
if self.__env[key]: if self.__env[key]:
logger.warning('environment var "' + key + ' existed and was overwritten!') log('warning', __name__, 'environment var "' + key + ' existed and was overwritten!')
self.__env[key] = value self.__env[key] = value

View file

@ -0,0 +1,21 @@
"""
This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license)
"""
from logging import getLogger
logger = getLogger('linspector')
def log(level, name, msg):
if level == 'critical':
logger.critical('[' + name + '] ' + str(msg))
if level == 'error':
logger.error('[' + name + '] ' + str(msg))
elif level == 'warning':
logger.warning('[' + name + '] ' + str(msg))
elif level == 'info':
logger.info('[' + name + '] ' + str(msg))
elif level == 'debug':
logger.debug('[' + name + '] ' + str(msg))

View file

@ -3,19 +3,19 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
import datetime
import importlib import importlib
import random
from logging import getLogger
from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor # , ProcessPoolExecutor from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
logger = getLogger('linspector') from linspector.core.helpers import log
def job_function(job): def job_function(monitor):
job.handle_call() monitor.handle_call()
class Linspector: class Linspector:
@ -23,19 +23,20 @@ class Linspector:
def __init__(self, configuration, environment, monitors, plugins, scheduler): def __init__(self, configuration, environment, monitors, plugins, scheduler):
self.__configuration = configuration self.__configuration = configuration
self.__environment = environment self.__environment = environment
self.__jobs = []
self.__monitors = monitors self.__monitors = monitors
self.__plugin_list = None self.__plugin_list = None
self.__plugins = plugins self.__plugins = plugins
self.__scheduler = scheduler self.__scheduler = scheduler
# load plugins # load plugins
logger.info('loading plugins...') log('info', __name__, 'loading plugins...')
if configuration.get_option('linspector', 'plugins'): if configuration.get_option('linspector', 'plugins'):
plugin_list = configuration.get_option('linspector', 'plugins') plugin_list = configuration.get_option('linspector', 'plugins')
self.__plugin_list = plugin_list.split(',') self.__plugin_list = plugin_list.split(',')
for plugin_option in plugin_list.split(','): for plugin_option in plugin_list.split(','):
if plugin_option not in plugins: if plugin_option not in plugins:
logger.info('loading plugin: ' + plugin_option) log('info', __name__, 'loading plugin: ' + plugin_option)
plugin_package = 'linspector.plugins.' + plugin_option.lower() plugin_package = 'linspector.plugins.' + plugin_option.lower()
plugin_module = importlib.import_module(plugin_package) plugin_module = importlib.import_module(plugin_package)
plugin = plugin_module.get(configuration, environment, self) plugin = plugin_module.get(configuration, environment, self)
@ -45,10 +46,10 @@ class Linspector:
'memory': MemoryJobStore() 'memory': MemoryJobStore()
} }
executors = { executors = {
'default': ThreadPoolExecutor(int(configuration.get_option('linspector', #'default': ThreadPoolExecutor(int(configuration.get_option('linspector',
'max_threads'))), # 'max_threads'))),
#'default': ProcessPoolExecutor(int(configuration.get_option('linspector', 'default': ProcessPoolExecutor(int(configuration.get_option('linspector',
# 'max_processes'))) 'max_processes')))
} }
job_defaults = { job_defaults = {
'max_instances': 10000 'max_instances': 10000
@ -57,6 +58,24 @@ class Linspector:
executors=executors, executors=executors,
job_defaults=job_defaults) job_defaults=job_defaults)
start_date = datetime.datetime.now()
log('debug', __name__, monitors.get_monitors())
monitors = self.__monitors.get_monitors()
for monitor in monitors:
time_delta = round(random.uniform(1.00, 10.00), 2)
new_start_date = start_date + datetime.timedelta(seconds=time_delta)
monitor_job = monitors.get(monitor)
interval = monitor_job.get_interval()
scheduler_job = scheduler['linspector'].add_job(job_function, 'interval',
start_date=new_start_date,
seconds=interval, timezone="CET",
args=[monitors.get(monitor)])
monitor_job.set_job(scheduler_job)
self.__jobs.append(monitor_job)
log('info', __name__, 'scheduling job ' + monitor + ' with delta ' + str(time_delta) +
' @' + str(new_start_date) + ' running service ')
if configuration.get_option('linspector', 'start_scheduler') == 'true': if configuration.get_option('linspector', 'start_scheduler') == 'true':
self.__scheduler['linspector'].start() self.__scheduler['linspector'].start()
@ -64,7 +83,7 @@ class Linspector:
def print_debug(self): def print_debug(self):
# example on how to access the monitor objects in monitors # example on how to access the monitor objects in monitors
monitors = self.__monitors.get_monitors() monitors = self.__monitors.get_monitors()
print(__file__ + ' (59): ' + str(monitors)) print(__file__ + ' (78): ' + str(monitors))
for monitor in monitors: for monitor in monitors:
print(__file__ + ' (61): ' + monitors.get(monitor).get_identifier()) print(__file__ + ' (80): ' + monitors.get(monitor).get_identifier())
print(__file__ + ' (62): ' + monitors.get(monitor).get_service()) print(__file__ + ' (81): ' + monitors.get(monitor).get_service())

View file

@ -9,9 +9,7 @@ import signal
import sys import sys
import time import time
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
# TODO: there is a bug when stopping the daemon. the pid_file is not being deleted. NEEDS A FIX! # TODO: there is a bug when stopping the daemon. the pid_file is not being deleted. NEEDS A FIX!
@ -24,7 +22,7 @@ class Linspectord:
try: try:
self.__pid_file = configuration.get_option('linspector', 'pid_file') self.__pid_file = configuration.get_option('linspector', 'pid_file')
except Exception as err: except Exception as err:
logger.critical(str('daemonize error (no pid_file set): {0}'.format(err))) log('critical', __name__, str('daemonize error (no pid_file set): {0}'.format(err)))
def daemonize(self): def daemonize(self):
# daemonize the class using the UNIX double fork mechanism. # daemonize the class using the UNIX double fork mechanism.
@ -36,7 +34,7 @@ class Linspectord:
# exit first parent. # exit first parent.
sys.exit(0) sys.exit(0)
except OSError as err: except OSError as err:
logger.critical(str('fork #1 failed: {0}'.format(err))) log('critical', __name__, str('fork #1 failed: {0}'.format(err)))
sys.exit(1) sys.exit(1)
# decouple from parent environment. # decouple from parent environment.
@ -51,7 +49,7 @@ class Linspectord:
# Exit from second parent. # Exit from second parent.
sys.exit(0) sys.exit(0)
except OSError as err: except OSError as err:
logger.critical(str('fork #2 failed: {0}'.format(err))) log('critical', __name__, str('fork #2 failed: {0}'.format(err)))
sys.exit(1) sys.exit(1)
# redirect standard file descriptors. # redirect standard file descriptors.
@ -77,7 +75,7 @@ class Linspectord:
def start(self): def start(self):
# start the daemon. check for a pidfile to see if the daemon already runs before. # start the daemon. check for a pidfile to see if the daemon already runs before.
logger.info('starting daemon using pid_file: ' + self.__pid_file) log('info', __name__, 'starting daemon using pid_file: ' + self.__pid_file)
try: try:
with open(self.__pid_file, 'r') as pf: with open(self.__pid_file, 'r') as pf:
pid = int(pf.read().strip()) pid = int(pf.read().strip())
@ -86,7 +84,7 @@ class Linspectord:
if pid: if pid:
message = 'pid_file {0} already exist. daemon already running?' message = 'pid_file {0} already exist. daemon already running?'
logger.critical(str(message.format(self.__pid_file))) log('critical', __name__, str(message.format(self.__pid_file)))
sys.exit(1) sys.exit(1)
# start the daemon. # start the daemon.
@ -95,7 +93,7 @@ class Linspectord:
def stop(self): def stop(self):
# stop the daemon. # stop the daemon.
logger.info('stopping daemon using pid_file: ' + self.__pid_file) log('info', __name__, 'stopping daemon using pid_file: ' + self.__pid_file)
# get the pid from the pid file. # get the pid from the pid file.
try: try:
with open(self.__pid_file, 'r') as pf: with open(self.__pid_file, 'r') as pf:
@ -105,7 +103,7 @@ class Linspectord:
if not pid: if not pid:
message = 'pid_file {0} does not exist. daemon not running?' message = 'pid_file {0} does not exist. daemon not running?'
logger.error(str(message.format(self.__pid_file))) log('error', __name__, str(message.format(self.__pid_file)))
return # not an error in a restart return # not an error in a restart
# try killing the daemon process. # try killing the daemon process.
@ -119,12 +117,12 @@ class Linspectord:
if os.path.exists(self.__pid_file): if os.path.exists(self.__pid_file):
os.remove(self.__pid_file) os.remove(self.__pid_file)
else: else:
logger.critical(str(err.args)) log('critical', __name__, str(err.args))
sys.exit(1) sys.exit(1)
def restart(self): def restart(self):
# restart the daemon. # restart the daemon.
logger.info('restarting daemon using pid_file: ' + self.__pid_file) log('info', __name__, 'restarting daemon using pid_file: ' + self.__pid_file)
self.stop() self.stop()
self.start() self.start()

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
# The logger can be used by any monitor to write arbitrary data to any arbitrary place. # The logger can be used by any monitor to write arbitrary data to any arbitrary place.

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
# This class can maybe be used for a general data model for Linspector data processing. currently # This class can maybe be used for a general data model for Linspector data processing. currently

View file

@ -5,9 +5,11 @@ See LICENSE (MIT license)
""" """
import importlib import importlib
from logging import getLogger from datetime import datetime
from binascii import crc32
logger = getLogger('linspector') from linspector.core.helpers import log
from linspector.core.task import Task, TaskExecutor
class Monitor: class Monitor:
@ -17,14 +19,38 @@ class Monitor:
self.__configuration = configuration self.__configuration = configuration
self.__environment = environment self.__environment = environment
self.__identifier = identifier self.__identifier = identifier
self.__interval = int(monitor_configuration.get('monitor', 'interval'))
self.__monitor_configuration = monitor_configuration self.__monitor_configuration = monitor_configuration
self.__notification_list = [] self.__notification_list = []
self.__service = None self.__service = monitor_configuration.get('monitor', 'service')
self.__notifications = notifications self.__notifications = notifications
self.__services = services self.__services = services
self.__task_list = [] # put tasks for the dedicated job here. self.__task_list = [] # put tasks for the dedicated job here.
self.__tasks = tasks self.__tasks = tasks
self.service = self.__service
self.host = monitor_configuration.get('monitor', 'host')
#self.members = members
#self.core = core
self.hostgroup = monitor_configuration.get('monitor', 'hostgroup')
self.job_threshold = 0
self.enabled = True
self.scheduler_job = None
self.job_id = self.hex_string()
"""
NONE job was not executed
OK when everything is fine
WARNING when a job has errors but not the threshold overridden
RECOVER when a job recovers e.g. the threshold decrements (not implemented)
ERROR when a jobs threshold is overridden
UNKNOWN when a job throws an exception which is not handled by the job itself (not implemented)
"""
self.status = "NONE"
self.last_execution = None
self.monitor_information = MonitorInformation(self.job_id, self.hostgroup, self.host,
self.service)
if configuration.get_option('linspector', 'notifications') or \ if configuration.get_option('linspector', 'notifications') or \
monitor_configuration.get('monitor', 'notifications'): monitor_configuration.get('monitor', 'notifications'):
@ -47,7 +73,7 @@ class Monitor:
if notification_option not in notifications: if notification_option not in notifications:
notification_package = 'linspector.notifications.' + notification_option.lower() notification_package = 'linspector.notifications.' + notification_option.lower()
notification_module = importlib.import_module(notification_package) notification_module = importlib.import_module(notification_package)
notification = notification_module.get(configuration, environment) notification = notification_module.create(configuration, environment)
notifications[notification_option.lower()] = notification notifications[notification_option.lower()] = notification
if self.__monitor_configuration.get('monitor', 'service'): if self.__monitor_configuration.get('monitor', 'service'):
@ -57,7 +83,7 @@ class Monitor:
service_module = importlib.import_module(service_package) service_module = importlib.import_module(service_package)
self.__service = monitor_configuration.get('monitor', 'service').lower() self.__service = monitor_configuration.get('monitor', 'service').lower()
service = service_module.get(configuration, environment) service = service_module.create(configuration, environment)
services[monitor_configuration.get('monitor', 'service').lower()] = service services[monitor_configuration.get('monitor', 'service').lower()] = service
#service.execute(self) #service.execute(self)
@ -83,14 +109,201 @@ class Monitor:
if task_option not in tasks: if task_option not in tasks:
task_package = 'linspector.tasks.' + task_option.lower() task_package = 'linspector.tasks.' + task_option.lower()
task_module = importlib.import_module(task_package) task_module = importlib.import_module(task_package)
task = task_module.get(configuration, environment) task = task_module.create(configuration, environment)
tasks[task_option.lower()] = task tasks[task_option.lower()] = task
def get_identifier(self): def get_identifier(self):
return self.__identifier return self.__identifier
def get_interval(self):
return self.__interval
def get_service(self):
return self.__service
# currently only used for testing but maybe i will add get functions for all known variables. # currently only used for testing but maybe i will add get functions for all known variables.
# but not all variables can be known because all monitors are different. only the service # but not all variables can be known because all monitors are different. only the service
# implementation can know all variables which are being used inside the service. # implementation can know all variables which are being used inside the service.
def get_service(self): def get_service(self):
return self.__monitor_configuration.get('monitor', 'service') return self.__service
def __str__(self):
return str(self.__dict__)
def __hex__(self):
return hex(crc32(bytes(self.hostgroup + self.host + self.service, 'utf-8')))
def hex_string(self):
ret = self.__hex__()
if ret[0] == "-":
ret = ret[3:]
else:
ret = ret[2:]
while len(ret) < 8:
ret = "0" + ret
return ret
def get_job_id(self):
return self.job_id
def set_job(self, scheduler_job):
self.scheduler_job = scheduler_job
def set_enabled(self, enabled=True):
self.enabled = enabled
def handle_threshold(self, service_threshold, execution_successful):
if execution_successful:
if self.job_threshold > 0:
if "threshold_reset" in self.core and self.core["threshold_reset"]:
#logger.info("Job " + self.get_job_id() + ", Threshold Reset")
self.job_threshold = 0
else:
#logger.info("Job " + self.get_job_id() + ", Threshold Decrement")
self.job_threshold -= 1
self.status = "OK"
self.monitor_information.set_status(self.status)
self.monitor_information.inc_job_overall_wins()
else:
self.status = "WARNING"
self.monitor_information.set_status(self.status)
self.monitor_information.inc_job_overall_fails()
self.job_threshold += 1
if self.job_threshold >= service_threshold:
#logger.info("Job " + self.get_job_id() + ", Threshold reached!")
self.status = "ERROR"
self.monitor_information.set_status(self.status)
def handle_tasks(self, monitor_information):
for task in self.__tasks:
if self.status.lower() in task.get_task_type().lower():
log('debug', __name__, 'executing task of type: ' + self.status)
# tasks can but should not be executed here. putting them in a queue is the better
# solution to execute them in a serial process.
#TaskExecutor.instance().schedule_task(monitor_information, task)
def handle_call(self):
log('info', __name__, "handle call to identifier: " + self.__identifier)
#logger.debug("handle call")
#logger.debug(self.service)
if self.enabled:
self.last_execution = None
try:
self.last_execution = MonitorExecution(self.get_host())
self.service.execute(self.last_execution)
except Exception as err:
log.debug('debug', __name__, err)
self.last_execution.set_execution_end()
self.handle_threshold(self.service.get_threshold(),
self.last_execution.was_successful())
#logger.info("Job " + self.get_job_id() +
# ", Code: " + str(self.last_execution.get_error_code()) +
# ", Message: " + str(self.last_execution.get_message()))
self.monitor_information.set_response_message(
self.last_execution.get_response_message(self))
self.handle_tasks(self.monitor_information)
else:
log('info', __name__, "job " + self.get_job_id() + " disabled")
def get_host(self):
return self.host
def get_hostgroup(self):
return self.hostgroup
class MonitorExecution:
def __init__(self, host):
self.execution_start = datetime.now()
self.execution_end = -1
self.host = host
self.error_code = -1
self.message = None
self.kwargs = None
def get_host_name(self):
return self.host
def get_message(self):
return self.message
def get_kwargs(self):
return self.kwargs
def set_execution_end(self):
self.execution_end = datetime.now()
def get_error_code(self):
return self.error_code
def was_successful(self):
return self.get_error_code() == 0
def set_result(self, error_code=0, message="", kwargs=None):
self.error_code = error_code
self.message = message
self.kwargs = kwargs
def get_response_message(self, job):
msg = str(job.status) + " [" + job.service.get_config_name() + ": " + str(job.get_job_id()) + "] " + \
str(job.get_hostgroup()) + " " + str(job.get_host())
if self.get_message() is not None:
msg += " " + str(self.get_message())
if self.get_kwargs() is not None:
msg += " " + str(self.get_kwargs())
return msg
class MonitorInformation:
def __init__(self, job_id, hostgroup, host, service):
self.job_id = job_id
self.hostgroup = hostgroup
self.host = host
self.service = service
self.response_massage = None
self.period = None
self.next_run = None
self.runs = 0
self.enabled = None
self.threshold = 0
self.fails = 0
self.job_overall_fails = 0
self.job_overall_wins = 0
self.last_execution = None
self.last_run = None
self.last_fail = None
self.last_success = None
self.last_disabled = None
self.last_enabled = None
self.last_threshold_override = None
self.last_escalation = None
self.status = "NONE"
def inc_job_overall_fails(self):
self.job_overall_fails += 1
def inc_job_overall_wins(self):
self.job_overall_wins += 1
def get_job_id(self):
return self.job_id
def get_response_message(self):
return self.response_massage
def set_response_message(self, msg):
self.response_massage = msg
def get_status(self):
return self.status
def set_status(self, status):
self.status = status

View file

@ -8,12 +8,8 @@ import copy
import glob import glob
import os import os
from logging import getLogger
from linspector.core.monitor import Monitor from linspector.core.monitor import Monitor
from linspector.core.helpers import log
logger = getLogger('linspector')
# monitors may could / should be added (and maybe changed) at runtime to add new monitors without # monitors may could / should be added (and maybe changed) at runtime to add new monitors without
@ -27,12 +23,12 @@ class Monitors:
self.__monitors = {} self.__monitors = {}
monitor_groups = os.listdir(self.__configuration.get_configuration_path() + '/monitors/') monitor_groups = os.listdir(self.__configuration.get_configuration_path() + '/monitors/')
logger.debug('[' + __name__ + '] monitor groups: ' + str(monitor_groups)) log('debug', __name__, 'monitor groups: ' + str(monitor_groups))
monitor_configuration = configparser.ConfigParser() monitor_configuration = configparser.ConfigParser()
for monitor_group in monitor_groups: for monitor_group in monitor_groups:
monitors_file_list = glob.glob(self.__configuration.get_configuration_path() + monitors_file_list = glob.glob(self.__configuration.get_configuration_path() +
'/monitors/' + monitor_group + '/*.conf') '/monitors/' + monitor_group + '/*.conf')
logger.debug('[' + __name__ + '] monitor files: ' + str(monitors_file_list)) log('debug', __name__, 'monitor files: ' + str(monitors_file_list))
for monitor_file in monitors_file_list: for monitor_file in monitors_file_list:
monitor_configuration.read(monitor_file, 'utf-8') monitor_configuration.read(monitor_file, 'utf-8')
@ -41,9 +37,9 @@ class Monitors:
# create Monitor() object and copy monitor_configuration for each instance because # create Monitor() object and copy monitor_configuration for each instance because
# they else refer to the same object. # they else refer to the same object.
self.__monitors[identifier] = Monitor(configuration, environment, identifier, self.__monitors[identifier] = copy.deepcopy(Monitor(configuration, environment, identifier,
copy.deepcopy(monitor_configuration), copy.deepcopy(monitor_configuration),
notifications, services, tasks) notifications, services, tasks))
def get_monitors(self): def get_monitors(self):
return self.__monitors return self.__monitors

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
class Notification: class Notification:

View file

@ -3,10 +3,9 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger
from apscheduler.util import convert_to_datetime from apscheduler.util import convert_to_datetime
logger = getLogger('linspector') from linspector.core.helpers import log
class Period(object): class Period(object):

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
class Plugin: class Plugin:

View file

@ -3,9 +3,7 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
logger = getLogger('linspector')
class Service: class Service:

View file

@ -3,13 +3,100 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from queue import Queue
from threading import Event, Thread
logger = getLogger('linspector') from linspector.core.helpers import log
from linspector.utils.singleton import Singleton
KEY_TYPE = "type"
KEY_ARGS = "args"
KEY_CLASS = "class"
class Task: class Task:
def __init__(self, configuration, environment, **kwargs):
def __init__(self, configuration, environment):
self.__configuration = configuration self.__configuration = configuration
self._environment = environment self._environment = environment
self._args = {}
if KEY_ARGS in kwargs:
self.add_arguments(kwargs[KEY_ARGS])
elif self.needs_arguments():
raise Exception("Error: needs arguments but none provided!")
if KEY_CLASS in kwargs:
self.name = kwargs[KEY_CLASS]
else:
self.name = self.__class__
self._type = None
if KEY_TYPE in kwargs:
self._type = kwargs[KEY_TYPE]
def get_task_type(self):
return str(self._type)
def get_config_name(self):
return str(self.name)
def add_arguments(self, args):
for key, val in args.items():
self._args[key] = val
def get_arguments(self):
return self._args
#def set_member(self, member):
# self.member = member
def needs_arguments(self):
return False
def execute(self, job):
try:
self.execute(job)
except Exception as e:
#logger.debug("Task execute failed!!!")
raise e
@Singleton
class TaskExecutor:
def __init__(self):
self.queue = Queue()
self.taskInfos = []
task_thread = Thread(target=self._run_worker_thread)
self._instantEnd = False
self._running = True
task_thread.daemon = True
task_thread.start()
def _run_worker_thread(self):
while self.is_running() or not self.is_instant_end():
try:
msg, task = self.queue.get()
if task:
log('debug', __name__, "starting task execution...")
#task.execute(msg)
self.queue.task_done()
except Exception as err:
log('error', __name__, "error " + str(err))
def is_instant_end(self):
return self._instantEnd
def is_running(self):
return self._running
def stop(self):
self._running = False
def stop_immediately(self):
self._running = False
self._instantEnd = True
def schedule_task(self, msg, task):
self.queue.put((msg, task))

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.notification import Notification from linspector.core.notification import Notification
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return CallNotification(configuration, environment) return CallNotification(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.notification import Notification from linspector.core.notification import Notification
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return EmailNotification(configuration, environment) return EmailNotification(configuration, environment)

View file

@ -3,14 +3,13 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger import gammu
from linspector.core.helpers import log
from linspector.core.notification import Notification from linspector.core.notification import Notification
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return SMSNotification(configuration, environment) return SMSNotification(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.notification import Notification from linspector.core.notification import Notification
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return TwitterNotification(configuration, environment) return TwitterNotification(configuration, environment)

View file

@ -3,14 +3,13 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger import xmpp
from linspector.core.helpers import log
from linspector.core.notification import Notification from linspector.core.notification import Notification
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return XMPPNotification(configuration, environment) return XMPPNotification(configuration, environment)

View file

@ -6,12 +6,9 @@ See LICENSE (MIT license)
import cherrypy import cherrypy
import json import json
from logging import getLogger from linspector.core.helpers import log
from linspector.core.plugin import Plugin from linspector.core.plugin import Plugin
logger = getLogger('linspector')
def get(configuration, environment, linspector): def get(configuration, environment, linspector):
return HTTPServerPlugin(configuration, environment, linspector) return HTTPServerPlugin(configuration, environment, linspector)

View file

@ -3,12 +3,9 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.plugin import Plugin from linspector.core.plugin import Plugin
logger = getLogger('linspector')
def get(configuration, environment, linspector): def get(configuration, environment, linspector):
return LishPlugin(configuration, environment, linspector) return LishPlugin(configuration, environment, linspector)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return HTTPKeywordService(configuration, environment) return HTTPKeywordService(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return DummyService(configuration, environment) return DummyService(configuration, environment)
@ -22,4 +19,5 @@ class DummyService(Service):
self.__environment = environment self.__environment = environment
def execute(self): def execute(self):
log('debug', __name__, 'dummy object @' + str(self))
return return

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return RandomService(configuration, environment) return RandomService(configuration, environment)

View file

@ -4,14 +4,12 @@ Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT) See LICENSE (MIT)
""" """
from fritzconnection.lib.fritzstatus import FritzStatus from fritzconnection.lib.fritzstatus import FritzStatus
from logging import getLogger
from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return FritzboxUplinkService(configuration, environment) return FritzboxUplinkService(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return PingService(configuration, environment) return PingService(configuration, environment)

View file

@ -5,14 +5,11 @@ See LICENSE (MIT license)
""" """
import socket import socket
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return PortService(configuration, environment) return PortService(configuration, environment)

View file

@ -7,14 +7,11 @@ import calendar
import requests import requests
import time import time
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return SpeedtestService(configuration, environment) return SpeedtestService(configuration, environment)

View file

@ -7,15 +7,11 @@ import os
import paramiko import paramiko
import pprint import pprint
from linspector.core.helpers import log
from logging import getLogger
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return SSHService(configuration, environment) return SSHService(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return TCPConnectService(configuration, environment) return TCPConnectService(configuration, environment)

View file

@ -3,15 +3,13 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger
from pysnmp.entity.rfc3413.oneliner import cmdgen from pysnmp.entity.rfc3413.oneliner import cmdgen
from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return GetService(configuration, environment) return GetService(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return ShellService(configuration, environment) return ShellService(configuration, environment)

View file

@ -3,12 +3,9 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.service import Service from linspector.core.service import Service
logger = getLogger('linspector')
def get(configuration, environment): def get(configuration, environment):
return UptimeService(configuration, environment) return UptimeService(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.task import Task from linspector.core.task import Task
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return CSVTask(configuration, environment) return CSVTask(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.task import Task from linspector.core.task import Task
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return FileLoggerTask(configuration, environment) return FileLoggerTask(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.task import Task from linspector.core.task import Task
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return MariaDBTask(configuration, environment) return MariaDBTask(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.task import Task from linspector.core.task import Task
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return RedisTask(configuration, environment) return RedisTask(configuration, environment)

View file

@ -3,14 +3,11 @@ This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved. Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license) See LICENSE (MIT license)
""" """
from logging import getLogger from linspector.core.helpers import log
from linspector.core.task import Task from linspector.core.task import Task
logger = getLogger('linspector')
def create(configuration, environment):
def get(configuration, environment):
return SQLiteTask(configuration, environment) return SQLiteTask(configuration, environment)

View file

View file

@ -0,0 +1,48 @@
"""
This file is part of Linspector (https://linspector.org/)
Copyright (c) 2022 Johannes Findeisen <you@hanez.org>. All Rights Reserved.
See LICENSE (MIT license)
"""
#see http://stackoverflow.com/questions/42558/python-and-the-singleton-pattern
class Singleton:
"""
A non-thread-safe helper class to ease implementing singletons.
This should be used as a decorator -- not a metaclass -- to the
class that should be a singleton.
The decorated class can define one `__init__` function that
takes only the `self` argument. Other than that, there are
no restrictions that apply to the decorated class.
To get the singleton instance, use the `Instance` method. Trying
to use `__call__` will result in a `TypeError` being raised.
Limitations: The decorated class cannot be inherited from.
"""
def __init__(self, decorated):
self._decorated = decorated
self._instance = None
def instance(self):
"""
Returns the singleton instance. Upon its first call, it creates a
new instance of the decorated class and calls its `__init__` method.
On all subsequent calls, the already created instance is returned.
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated()
return self._instance
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)

View file

@ -1,5 +1,8 @@
APScheduler==3.9.1 # (required) APScheduler==3.9.1 # (required)
CherryPy==18.8.0 # (optional) CherryPy==18.8.0 # (optional)
fritzconnection==1.10.3 # (optional) fritzconnection==1.10.3 # (optional, used by fritzuplink service)
paramiko==2.11.0 # (optional) python-gammu==3.2.4 # (optional, used by the sms notification)
pysnmp==4.4.12 # (optional) paramiko==2.11.0 # (optional, used by ssh service)
pysnmp==4.4.12 # (optional, used by snmp services)
requests==2.28.1 # (optional, used by speedtest service)
xmpp2==0.4 # (optional, used by the xmpp notification)