Add support for other extensions
This commit is contained in:
parent
ffa21bffc6
commit
ed220154d8
5 changed files with 161 additions and 14 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
extensions/*
|
extensions/
|
||||||
|
__pycache__/
|
||||||
|
logs/
|
|
@ -14,7 +14,7 @@ Not much, you?
|
||||||
|
|
||||||
Docs are in the `./docs/` folder, but here's a quick overview on how to use this:
|
Docs are in the `./docs/` folder, but here's a quick overview on how to use this:
|
||||||
|
|
||||||
1. Clone this repository - or just download `updog.py`
|
1. Clone this repository - `git clone --depth 1 https://git.askiiart.net/askiiart/updog`
|
||||||
2. Install your extensions in the appropriate folders: `./extensions/checkers`, `./extensions/alerts` (optional), and `./extensions/logging`
|
2. Install your extensions in the appropriate folders: `./extensions/checkers`, `./extensions/alerts` (optional), and `./extensions/logging`
|
||||||
3. Create your `services.json` file, example below.
|
3. Create your `services.json` file, example below.
|
||||||
|
|
||||||
|
@ -49,6 +49,10 @@ Docs are in the `./docs/` folder, but here's a quick overview on how to use this
|
||||||
- Call the extensions
|
- Call the extensions
|
||||||
- Add support for logging and alert extensions
|
- Add support for logging and alert extensions
|
||||||
- Add maintenance windows (optionally recurring)
|
- Add maintenance windows (optionally recurring)
|
||||||
|
- Add ability to set default extensions that can be overridden on a per-service basis
|
||||||
|
- And groups (lower priority)
|
||||||
|
- Add ability for checkers to have preferred alerts and logging extensions - will print a warning if the preferred extension(s) are not used
|
||||||
|
- Add "keywords" to be replaced in the config (for current directory, service name, maybe some others)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
21
services-example.json
Normal file
21
services-example.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"site": {
|
||||||
|
"name": "A Website",
|
||||||
|
"checker": "CheckerTemplate",
|
||||||
|
"checker-args": {
|
||||||
|
"url": "https://example.net",
|
||||||
|
"port": 443,
|
||||||
|
"lol": "CheckerTemplate ignores these options lol"
|
||||||
|
},
|
||||||
|
"rate": 60,
|
||||||
|
"alerts": "AlertsTemplate",
|
||||||
|
"alerts-args": {
|
||||||
|
"url": "https://example.com/webhook-url-or-whatever-goes-here",
|
||||||
|
"lol": "irrelevant, AlertsTemplate ignores these options lol"
|
||||||
|
},
|
||||||
|
"logging": "LoggingTemplate",
|
||||||
|
"logging-args": {
|
||||||
|
"file": "/home/askiiart/Documents/updog/logs/site-log"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
timer.py
Normal file
54
timer.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Threaded repeating timer thing
|
||||||
|
# From https://stackoverflow.com/a/40965385/16432246
|
||||||
|
# It shouldn't drift, but that's untested
|
||||||
|
# Keep in mind you can't catch Exceptions for this, it just crashes the thread.
|
||||||
|
# Some more scheduling stuff: https://www.redwood.com/article/python-job-scheduling/
|
||||||
|
class RepeatedTimer(object):
|
||||||
|
'''
|
||||||
|
Run stuff repeatedly every x seconds
|
||||||
|
Example usage (from SO and ported to Python 3):
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
def hello(name):
|
||||||
|
print "Hello %s!" % name
|
||||||
|
|
||||||
|
print()"starting...")
|
||||||
|
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
|
||||||
|
try:
|
||||||
|
sleep(5) # your long-running job goes here...
|
||||||
|
catch:
|
||||||
|
rt.stop() # better in a try/catch block to make sure the program ends!
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, interval, function, *args, **kwargs):
|
||||||
|
'''
|
||||||
|
Run a functions with arguments every
|
||||||
|
'''
|
||||||
|
self._timer = None
|
||||||
|
self.interval = interval
|
||||||
|
self.function = function
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
self.is_running = False
|
||||||
|
self.next_call = time.time()
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def _run(self):
|
||||||
|
self.is_running = False
|
||||||
|
self.start()
|
||||||
|
self.function(*self.args, **self.kwargs)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
if not self.is_running:
|
||||||
|
self.next_call += self.interval
|
||||||
|
self._timer = threading.Timer(
|
||||||
|
self.next_call - time.time(), self._run)
|
||||||
|
self._timer.start()
|
||||||
|
self.is_running = True
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._timer.cancel()
|
||||||
|
self.is_running = False
|
90
updog.py
90
updog.py
|
@ -1,31 +1,97 @@
|
||||||
|
from timer import RepeatedTimer
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import importlib
|
import importlib
|
||||||
import inspect
|
import inspect
|
||||||
|
import json
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
# relative imports suck - https://gideonbrimleaf.github.io/2021/01/26/relative-imports-python.html
|
|
||||||
path = os.path.realpath(__file__)
|
path = os.path.realpath(__file__)
|
||||||
path = path[:path.rfind('/')]
|
path = path[:path.rfind('/')]
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# Import extensions #
|
||||||
|
#####################
|
||||||
|
|
||||||
|
# Import checkers #
|
||||||
|
# relative imports suck - https://gideonbrimleaf.github.io/2021/01/26/relative-imports-python.html
|
||||||
sys.path.insert(1, f'{path}/extensions/checkers')
|
sys.path.insert(1, f'{path}/extensions/checkers')
|
||||||
|
|
||||||
# importlib used to import stuff programmatically, rather than using the hardcoded import keyword, basically the same but it seems it can't import *just* a class
|
# importlib used to import stuff programmatically, rather than using the hardcoded import keyword, basically the same but it seems it can't import *just* a class
|
||||||
uptime_extension_imports = []
|
checker_extension_imports = []
|
||||||
for ext_name in os.listdir(f'{path}/extensions/checkers'):
|
for ext_name in os.listdir(f'{path}/extensions/checkers'):
|
||||||
if ext_name[0] != '.':
|
if ext_name[0] != '.':
|
||||||
uptime_extension_imports.append(f'{ext_name}.{ext_name}')
|
checker_extension_imports.append(f'{ext_name}.{ext_name}')
|
||||||
|
|
||||||
# uptime_checkers contains all the classes for the checker extensions
|
# checkers contains all the classes for the checker extensions
|
||||||
# e.g. {'CheckerTemplate': checker_template.checker_template.CheckerTemplate}
|
# e.g. {'CheckerTemplate': checker_template.checker_template.CheckerTemplate}
|
||||||
checkers = dict()
|
checkers = {}
|
||||||
for ext in [importlib.import_module(ext) for ext in uptime_extension_imports]:
|
for ext in [importlib.import_module(ext) for ext in checker_extension_imports]:
|
||||||
for name, obj in inspect.getmembers(ext):
|
for name, obj in inspect.getmembers(ext):
|
||||||
if inspect.isclass(obj):
|
if inspect.isclass(obj):
|
||||||
checkers[name] = obj
|
checkers[name] = obj
|
||||||
|
|
||||||
if DEBUG:
|
# Import alerts #
|
||||||
print('uptime_extension_imports:', uptime_extension_imports)
|
# same as above, just for alerts
|
||||||
print('checkers:', checkers)
|
sys.path.insert(1, f'{path}/extensions/alerts')
|
||||||
|
|
||||||
print(checkers['CheckerTemplate'].get_status())
|
alerts_extension_imports = []
|
||||||
|
for ext_name in os.listdir(f'{path}/extensions/alerts'):
|
||||||
|
if ext_name[0] != '.':
|
||||||
|
alerts_extension_imports.append(f'{ext_name}.{ext_name}')
|
||||||
|
|
||||||
|
# alerts contains all the classes for the checker extensions
|
||||||
|
# e.g. {'AlertsTemplate': alerts_template.alerts_template.AlertsTemplate}
|
||||||
|
alerts = {}
|
||||||
|
for ext in [importlib.import_module(ext) for ext in alerts_extension_imports]:
|
||||||
|
for name, obj in inspect.getmembers(ext):
|
||||||
|
if inspect.isclass(obj):
|
||||||
|
alerts[name] = obj
|
||||||
|
|
||||||
|
# Import logging #
|
||||||
|
# same as above, just for logging
|
||||||
|
sys.path.insert(1, f'{path}/extensions/logging')
|
||||||
|
|
||||||
|
logging_extension_imports = []
|
||||||
|
for ext_name in os.listdir(f'{path}/extensions/logging'):
|
||||||
|
if ext_name[0] != '.':
|
||||||
|
logging_extension_imports.append(f'{ext_name}.{ext_name}')
|
||||||
|
|
||||||
|
# logging contains all the classes for the checker extensions
|
||||||
|
# e.g. {'LoggingTemplate': logging_template.logging_template.LoggingTemplate}
|
||||||
|
logging = {}
|
||||||
|
for ext in [importlib.import_module(ext) for ext in logging_extension_imports]:
|
||||||
|
for name, obj in inspect.getmembers(ext):
|
||||||
|
if inspect.isclass(obj):
|
||||||
|
logging[name] = obj
|
||||||
|
|
||||||
|
# get config from services.json
|
||||||
|
if 'services.json' in os.listdir():
|
||||||
|
config_filename = 'services.json'
|
||||||
|
elif 'services-example.json' in os.listdir():
|
||||||
|
config_filename = 'services-example.json'
|
||||||
|
|
||||||
|
with open(config_filename, 'rt') as config_file:
|
||||||
|
config = json.loads(''.join(config_file.readlines()))
|
||||||
|
|
||||||
|
|
||||||
|
def create_instances(config):
|
||||||
|
'''
|
||||||
|
Creates instances of all the extensions according to config
|
||||||
|
Parameters:
|
||||||
|
config: the dictionary containing the config
|
||||||
|
Returns:
|
||||||
|
instances (dict): A dictionary containing instances of the extensions
|
||||||
|
example: {'site': {'checker': instanceOfCheckerTemplate}}
|
||||||
|
'''
|
||||||
|
instances = {}
|
||||||
|
for service in config.keys():
|
||||||
|
instances[service] = {}
|
||||||
|
# just creates an instance of the checker with the arguments for it
|
||||||
|
instances[service]['checker'] = checkers[config[service]
|
||||||
|
['checker']](config[service]['checker-args'])
|
||||||
|
instances[service]['alerts'] = alerts[config[service]
|
||||||
|
['alerts']](config[service]['alerts-args'])
|
||||||
|
instances[service]['logging'] = logging[config[service]
|
||||||
|
['logging']](config[service]['logging-args'])
|
||||||
|
|
||||||
|
return instances
|
||||||
|
|
Loading…
Reference in a new issue