Last active
October 1, 2015 05:08
-
-
Save lottspot/987c630941226c80bee1 to your computer and use it in GitHub Desktop.
A nagios-like HTTP check which can import a python callable to handle a critical threshold
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
import Queue | |
import threading | |
import time | |
import sys, os | |
from urllib import urlopen | |
import logger | |
# check_http.py | |
# $1: SCHEME | |
# $2: HOST | |
# $3: TIMEOUT | |
# $4: RETRY | |
# $5: CHECK_STRING | |
MAX_FAILURES = 3 | |
LOG_FILES = ( | |
# Must be specified in LogLevel order | |
sys.stdout, | |
) | |
ACTION_MODULE = '' | |
ACTION_CALL = '' | |
ACTION_IS_DEFINED = False | |
take_action = lambda: None | |
if ACTION_MODULE is not None and ACTION_CALL is not None and len(ACTION_MODULE) > 0 and len(ACTION_CALL) > 0: | |
ACTION_IS_DEFINED = True | |
take_action = getattr(__import__(ACTION_MODULE, fromlist=['']), ACTION_CALL) | |
class CheckThread(threading.Thread): | |
def __init__(self, queue, scheme, host, check_string): | |
self.q = queue | |
self.scheme = scheme | |
self.host = host | |
self.check_string = check_string | |
threading.Thread.__init__(self) | |
def run(self): | |
try: | |
doc = urlopen('%s://%s' % (self.scheme, self.host)).read() | |
if self.check_string is not None: | |
if self.check_string not in doc: | |
self.q.put(0) | |
sys.exit(0) | |
self.q.put(1) | |
sys.exit(0) | |
except IOError: | |
self.q.put(-1) | |
sys.exit(1) | |
def do_http_check(scheme, host, timeout, check_string=None): | |
msgq = Queue.Queue(0) | |
thread = CheckThread(msgq, scheme, host, check_string) | |
thread.start() | |
try: | |
return msgq.get(timeout=timeout) | |
except Queue.Empty: | |
return -2 | |
if __name__ == '__main__': | |
pid = os.fork() | |
if pid: | |
# enter parent | |
sys.exit('%s forked with PID %s' % (sys.argv[0], pid)) | |
else: | |
# enter child | |
min_args = 5 | |
if len(sys.argv) < min_args: | |
sys.exit(1) | |
SCHEME = sys.argv[1] | |
HOST = sys.argv[2] | |
TIMEOUT = int(sys.argv[3]) | |
RETRY = int(sys.argv[4]) | |
CHECK_STRING = None | |
if len(sys.argv) > min_args: | |
# The value of CHECK_STRING is always the final argument | |
CHECK_STRING = sys.argv[min_args] | |
failures = 0 | |
logfile = logger.LogFile(*LOG_FILES) | |
log = logger.Logger(logfile) | |
level = logger.LogLevel | |
while True: | |
result = do_http_check(SCHEME, HOST, TIMEOUT, CHECK_STRING) | |
if result == -2: | |
log(level.WARN, 'WARNING - %s : %d second timeout' % (HOST, TIMEOUT)) | |
failures += 1 | |
elif result == -1: | |
log(level.WARN, 'WARNING - %s : Error receiving HTTP response' % HOST) | |
failures += 1 | |
elif result == 0: | |
log(level.WARN, 'WARNING - %s : \'%s\' not in response' % (HOST, CHECK_STRING)) | |
failures += 1 | |
else: | |
log(level.INFO, 'OK - %s' % HOST) | |
failures = 0 | |
if failures >= MAX_FAILURES: | |
action_str = '' | |
if ACTION_IS_DEFINED: | |
action_str = ' -- Calling %s.%s' % (ACTION_MODULE, ACTION_CALL) | |
log(level.ERROR, 'CRITICAL - %s : %d consecutive failures (RETRY=%d)%s' % (HOST, failures, RETRY, action_str)) | |
take_action() | |
failures = 0 | |
time.sleep(RETRY) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os, time, socket | |
class LogLevel: | |
MAX_LEVEL = 4 | |
NONE, INFO, WARN, ERROR = range(MAX_LEVEL) | |
class LogFile(object): | |
def __init__(self, *args): | |
self.level = LogLevel.NONE | |
self.log = [ None for i in range(LogLevel.MAX_LEVEL) ] | |
self.log[LogLevel.NONE] = open(os.devnull, 'w') | |
i = 0 | |
end_args = len(args) | |
while i < end_args: | |
arg = args[i] | |
log_level = i + 1 | |
if isinstance(arg, file): self.log[log_level] = arg | |
if isinstance(arg, str) and len(arg) > 0: self.log[log_level] = open(arg, 'a') | |
if hasattr(arg, 'sendall'): self.log[log_level] = arg | |
i += 1 | |
last_file = self.log[i] | |
while i < LogLevel.MAX_LEVEL: | |
self.log[i] = last_file | |
i += 1 | |
self.is_active = True | |
def set_level(self, level): | |
self.level = level | |
def get_level(self): | |
return self.level | |
def write(self, string): | |
log = self.log[self.level] | |
if hasattr(log, 'sendall'): | |
log.sendall(string) | |
addr = log.getpeername() | |
log.close() | |
log = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
log.connect(addr) | |
self.log[self.level] = log | |
log = None | |
if log is not None: | |
log.write(string) | |
log.flush() | |
return | |
def close(self): | |
for i in range(LogLevel.MAX_LEVEL): | |
log = self.log[i] | |
if hasattr(log, 'close'): log.close() | |
class Logger(object): | |
def __init__(self, LogFile, Lock=None): | |
self.file = LogFile | |
self.lock = Lock | |
def log(self, level, msg): | |
if hasattr(self.lock, 'acquire'): | |
self.lock.acquire() | |
t = time.localtime() | |
yr, mon, day, hr, min, sec = [ t[i] for i in range(6) ] | |
prefix = '[%04d-%02d-%02d %02d:%02d:%02d]' % (yr, mon, day, hr, min, sec) | |
self.barelog(level, msg, prefix) | |
if hasattr(self.lock, 'release'): | |
self.lock.release() | |
def barelog(self, level, msg, msg_prefix=''): | |
msg = '%s %s\n' % (msg_prefix, msg) | |
logfile = self.file | |
logfile.set_level(level) | |
logfile.write(msg) | |
def new_file_set(self, *args): | |
if hasattr(self.lock, 'acquire'): | |
self.lock.acquire() | |
logfile = self.file | |
logfile.close() | |
LogFile.__init__(logfile, *args) | |
if hasattr(self.lock, 'release'): | |
self.lock.release() | |
def __call__ (self, level, msg): | |
return self.log(level, msg) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment