From 9b25f65ca655a567873c66c2b015884a3e013276 Mon Sep 17 00:00:00 2001 From: Tom Ritter Date: Mon, 25 Jan 2016 21:24:41 -0500 Subject: Initial commit of checker --- jobs/EmailChecker.py | 46 +++++++++++++++++++++++++ jobs/HTTPServerChecker.py | 36 ++++++++++++++++++++ jobs/JobBase.py | 53 +++++++++++++++++++++++++++++ jobs/JobSpawner.py | 5 +++ jobs/PeerChecker.py | 39 +++++++++++++++++++++ jobs/TCPServerChecker.py | 41 ++++++++++++++++++++++ jobs/__init__.py | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 306 insertions(+) create mode 100755 jobs/EmailChecker.py create mode 100755 jobs/HTTPServerChecker.py create mode 100755 jobs/JobBase.py create mode 100755 jobs/JobSpawner.py create mode 100755 jobs/PeerChecker.py create mode 100755 jobs/TCPServerChecker.py create mode 100755 jobs/__init__.py (limited to 'jobs') diff --git a/jobs/EmailChecker.py b/jobs/EmailChecker.py new file mode 100755 index 0000000..51992bf --- /dev/null +++ b/jobs/EmailChecker.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +import os +import base64 +import datetime + +import imaplib + +import JobBase + +class EmailChecker(JobBase.JobBase): + def executeEvery(self): + return JobBase.JobFrequency.HOUR + def execute(self): + USER = self.config.get('email', 'user') + PASS = self.config.get('email', 'pass') + + #Generate a random subject + subj = base64.b64encode(os.urandom(20)) + + if not self.sendEmail(subj, "", USER): + return False + + M = imaplib.IMAP4_SSL(self.config.get('email', 'imapserver')) + M.login(USER, PASS) + + #If we have set up a filter to auto-delete messages from ourself + if self.config.get('email', 'ideletesentmessagesautomatically'): + M.select("[Gmail]/Trash") + + criteria = '(FROM "'+USER+'" SINCE "'+datetime.date.today().strftime("%d-%b-%Y")+'")' + typ, data = M.search(None, criteria) + + foundSubject = False + for num in data[0].split(): + typ, data = M.fetch(num, '(BODY.PEEK[HEADER.FIELDS (Subject)])') + if subj in data[0][1]: + foundSubject = True + M.close() + M.logout() + if not foundSubject: + #This may not work, but try anyway + self.sendEmail("Email Fetch Failure", "Body") + return False + else: + return True \ No newline at end of file diff --git a/jobs/HTTPServerChecker.py b/jobs/HTTPServerChecker.py new file mode 100755 index 0000000..ec8eda1 --- /dev/null +++ b/jobs/HTTPServerChecker.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +import logging +import requests + +import JobBase +import JobSpawner + +class HTTPServerChecker(JobSpawner.JobSpawner): + servers = [ + #("http://example.com", JobBase.JobFrequency.MINUTE), + #("https://exampletwo.com", JobBase.JobFrequency.MINUTE) + ] + + class ServerChecker(JobBase.JobBase): + def __init__(self, url, frequency): + self.url = url + self.frequency = frequency + + def getName(self): + return str(self.__class__) + " for " + self.url + def executeEvery(self): + return self.frequency + def execute(self): + try: + requests.get(self.url) + return True + except: + msg = "Could not hit server " + self.url + logging.warn(msg) + return self.sendEmail(msg, "") + + def get_sub_jobs(self): + for s in self.servers: + yield self.ServerChecker(s[0], s[1]) + diff --git a/jobs/JobBase.py b/jobs/JobBase.py new file mode 100755 index 0000000..330b6a9 --- /dev/null +++ b/jobs/JobBase.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +import random +import logging + +import smtplib + +class JobFrequency: + MINUTE = "minute" + HOUR = "hour" + +class JobBase: + def __init__(self): + self.config = None + def getName(self): + return str(self.__class__) + def shouldExecute(self, cronmode): + frequency = self.executeEvery() + if cronmode == frequency: + return True + return False + def setConfig(self, config): + self.config = config + + def sendEmail(self, subject, body, to=""): + return sendEmail(self.config, subject, body, to) + + def executeEvery(self): + pass + def execute(self): + pass + +def sendEmail(config, subject, body, to=""): + FROM = config.get('email', 'user') + PASS = config.get('email', 'pass') + if not to: + to = config.get('alertcontact', 'default') + + # Prepare actual message + # Avoid gmail threading + subject = subject + " " + str(random.random()) + message = """\From: %s\nTo: %s\nSubject: %s\n\n%s""" \ + % (FROM, ", ".join(to), subject, body) + try: + server = smtplib.SMTP(config.get('email', 'smtpserver'), config.get('email', 'smtpport')) + server.ehlo() + server.starttls() + server.login(FROM, PASS) + server.sendmail(FROM, to, message) + server.close() + return True + except: + return False \ No newline at end of file diff --git a/jobs/JobSpawner.py b/jobs/JobSpawner.py new file mode 100755 index 0000000..3d09693 --- /dev/null +++ b/jobs/JobSpawner.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +class JobSpawner: + def get_sub_jobs(self): + pass diff --git a/jobs/PeerChecker.py b/jobs/PeerChecker.py new file mode 100755 index 0000000..c8cca38 --- /dev/null +++ b/jobs/PeerChecker.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +import os +import base64 +import datetime + +import imaplib + +import JobBase + +class PeerChecker(JobBase.JobBase): + def executeEvery(self): + return JobBase.JobFrequency.HOUR + def execute(self): + testSuccess = True + peers = self.config.items('peers') + for p in peers: + peer = p[1].split(',') + peerOK = False + + try: + response = requests.get(peer[0]) + if response.status_code != 200: + peerOK = False + subject = peer[0] + " returned a non-standard status code." + else: + if "True" in response.content: + peerOK = True + elif "False" in response.content: + peerOK = False + subject = peer[0] + " reports it cannot send email." + except: + peerOK = False + subject = peer[0] + " is not responding." + + if not peerOK: + if not self.sendEmail(subject, "", peer[1]): + testSuccess = False + return testSuccess \ No newline at end of file diff --git a/jobs/TCPServerChecker.py b/jobs/TCPServerChecker.py new file mode 100755 index 0000000..711047b --- /dev/null +++ b/jobs/TCPServerChecker.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +import os +import socket +import logging + +import JobBase +import JobSpawner + +class TCPServerChecker(JobSpawner.JobSpawner): + servers = [ + #("example.com", 53, "example.com:tcpdns", JobBase.JobFrequency.MINUTE), + ] + + class ServerChecker(JobBase.JobBase): + def __init__(self, ip, port, friendlyName, frequency): + self.ip = ip + self.port = port + self.friendlyName = friendlyName + "(" + self.ip + ":" + str(self.port) + ")" + self.frequency = frequency + + def getName(self): + return str(self.__class__) + " for " + self.friendlyName + def executeEvery(self): + return self.frequency + def execute(self): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((self.ip, self.port)) + s.close() + return True + except: + msg = "Could not hit server " + self.friendlyName + logging.warn(msg) + return self.sendEmail(msg, "") + + def get_sub_jobs(self): + for s in self.servers: + yield self.ServerChecker(s[0], s[1], s[2], s[3]) + + diff --git a/jobs/__init__.py b/jobs/__init__.py new file mode 100755 index 0000000..9955164 --- /dev/null +++ b/jobs/__init__.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +import os +import sys +import inspect +import logging +from imp import load_module, find_module +import importlib + +import jobs +import jobs.JobBase +import jobs.JobSpawner + +class JobFinder: + def __init__(self, config): + """ + Opens the jobs folder and looks at every .py module in that directory. + Finds available jobs by looking at any class defined in those modules + that implements the JobBase abstract class. + Returns a list of job classes. + """ + self._jobs = set([]) + self.config = config + + job_modules = self.get_job_modules_dynamic() + + for module in job_modules: + # Check every declaration in that module + for name in dir(module): + obj = getattr(module, name) + if name not in module.__name__: + # Jobs have to have the same class name as their module name + # This prevents Job B from being detected twice when there is a Job A that imports Job B + continue + + if inspect.isclass(obj): + # A class declaration was found in that module + # Checking if it's a subclass of JobBase + # Discarding JobBase as a subclass of JobBase + if obj != jobs.JobBase.JobBase and obj != jobs.JobSpawner.JobSpawner: + logging.info("Found " + str(obj)) + for base in obj.__bases__: + # H4ck because issubclass() doesn't seem to work as expected on Linux + # It has to do with JobBase being imported multiple times (within jobs) or something + if base.__name__ == 'JobBase': + # A job was found, keep it + self._jobs.add(obj()) + elif base.__name__ == 'JobSpawner': + spawner = obj() + for j in spawner.get_sub_jobs(): + self._jobs.add(j) + + + def get_job_modules_dynamic(self): + job_modules = [] + + job_dir = jobs.__path__[0] + full_job_dir = os.path.join(sys.path[0], job_dir) + if os.path.exists(full_job_dir): + for (root, dirs, files) in os.walk(full_job_dir): + del dirs[:] # Do not walk into subfolders of the job directory + # Checking every .py module in the job directory + jobs_loaded = [] + for source in (s for s in files if s.endswith((".py"))): + module_name = os.path.splitext(os.path.basename(source))[0] + if module_name in jobs_loaded: + continue + jobs_loaded.append(module_name) + full_name = os.path.splitext(source)[0].replace(os.path.sep,'.') + + try: # Try to import the job package + # The job package HAS to be imported as a submodule + # of module 'jobs' or it will break windows compatibility + (file, pathname, description) = \ + find_module(full_name, jobs.__path__) + module = load_module('jobs.' + full_name, file, + pathname, description) + except Exception as e: + logging.critical('Import Error on ' + module_name + ': ' + str(e)) + jobs.JobBase.sendEmail(self.config, 'Import Error on ' + module_name, str(e)) + continue + job_modules.append(module) + return job_modules + + def get_jobs(self): + return self._jobs \ No newline at end of file -- cgit v1.2.3