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:
|
||||
|
||||
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`
|
||||
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
|
||||
- Add support for logging and alert extensions
|
||||
- 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 os
|
||||
import importlib
|
||||
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 = 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')
|
||||
|
||||
# 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'):
|
||||
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}
|
||||
checkers = dict()
|
||||
for ext in [importlib.import_module(ext) for ext in uptime_extension_imports]:
|
||||
checkers = {}
|
||||
for ext in [importlib.import_module(ext) for ext in checker_extension_imports]:
|
||||
for name, obj in inspect.getmembers(ext):
|
||||
if inspect.isclass(obj):
|
||||
checkers[name] = obj
|
||||
|
||||
if DEBUG:
|
||||
print('uptime_extension_imports:', uptime_extension_imports)
|
||||
print('checkers:', checkers)
|
||||
# Import alerts #
|
||||
# same as above, just for alerts
|
||||
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