diff --git a/.gitignore b/.gitignore index 651d0e2..114fa0b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,11 @@ nbproject distfiles docs +documentation files local log plugins .metadata -examples/private.json \ No newline at end of file +examples/private.json diff --git a/bin/linspector b/bin/linspector index 38359bb..17cb128 100755 --- a/bin/linspector +++ b/bin/linspector @@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . """ -__version__ = "0.6/TCPCONNECT" +__version__ = "0.1.7" __default_config__ = "./examples/minimal.json" import argparse @@ -35,7 +35,7 @@ from linspector.backends.https import HttpsBackend from linspector.frontends.lish import LishFrontend -def parseArgs(): +def parse_args(): parser = argparse.ArgumentParser( description="Linspector is for monitoring the vital information of hosts, services and devices in a network.", epilog="linspector is not some program expecting computers to run! Visit http://linspector.org for more " @@ -62,14 +62,14 @@ def parseArgs(): return parser.parse_args() -def setupLogging(logfile="./log/linspector.log", logLevel=logging.ERROR, logfileLevel=logging.DEBUG): +def setup_logging(logfile="./log/linspector.log", logLevel=logging.ERROR, logfileLevel=logging.DEBUG): logfile = path.expanduser(logfile) if not path.exists(path.dirname(logfile)): os.makedirs(path.dirname(logfile)) - logging.basicConfig(level=logging.WARNING) + #logging.basicConfig(level=logging.WARNING) log = logging.getLogger(__name__) - #log.setLevel(logging.ERROR) + log.setLevel(logging.DEBUG) consoleHandler = logging.StreamHandler() consoleHandler.setLevel(logLevel) @@ -90,14 +90,14 @@ def setupLogging(logfile="./log/linspector.log", logLevel=logging.ERROR, logfile return log -def handleJob(jobInfo): +def handle_job(jobInfo): jobInfo.handle_call() def main(): - args = parseArgs() + args = parse_args() #TODO: catch all log messages (apscheduler etc.); make logging more generic to support libraries logging - log = setupLogging(args.logfile, args.loglevel) + log = setup_logging(args.logfile, args.loglevel) log.info("parsed arguments") @@ -114,8 +114,8 @@ def main(): for service in hostgroup.get_services(): for host in hostgroup.get_hosts(): for period in service.get_periods(): - job = Job(service, host, hostgroup.get_members(), hostgroup.get_processors(), core) - schedulerJob = period.createJob(scheduler, job, handleJob) + job = Job(service, host, hostgroup.get_members(), hostgroup.get_processors(), core, hostgroup) + schedulerJob = period.createJob(scheduler, job, handle_job) if schedulerJob is not None: job.set_job(schedulerJob) job.set_logger(log) @@ -131,9 +131,9 @@ def main(): frontend = LishFrontend(jobs=jobs, scheduler=scheduler, linspectorConfig=linConf) log.debug("shutting down scheduler") - logging.shutdown() scheduler.shutdown(wait=True) + logging.shutdown() if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/linspector.json b/examples/linspector.json index 790cdcb..90980c1 100644 --- a/examples/linspector.json +++ b/examples/linspector.json @@ -170,6 +170,7 @@ } }, "layouts":{ + "_comment": "https://github.com/linspector/linspector/wiki/Configuration-Layouts", "production":{ "hostgroups":[ "group1" ], "enabled": true @@ -184,13 +185,16 @@ } }, "core":{ + "_comment": "https://github.com/linspector/linspector/wiki/Configuration-Core", "instance_name": "Master Monitoring (monitor.example.org)", "max_logfile_size": 1024000, "max_logfile_count": 4, "max_worker_threads": 8, + "threshold_handling": "reset", "members":[ "root" ], "backends": { "jsonrpc": { + "_comment": "https://github.com/linspector/linspector/wiki/Configuration-Core-Backends-JsonRPC", "host": "127.0.0.1", "port": "2323", "username": "linspector", diff --git a/linspector/core/daemon.py b/linspector/core/daemon.py index c80bb2d..799b48f 100644 --- a/linspector/core/daemon.py +++ b/linspector/core/daemon.py @@ -24,6 +24,27 @@ import time import atexit from signal import SIGTERM +""" +TODO: Think about, that daemonizing this software is not our goal but when we want to that, this needs a rewrite and +should maybe move over to the daemon frontend. daemon.py will remain the the base for this and should stay in linspector/core . +Since a daemon it normally not a frontend we should think about how to handle this. When daemonizing a real frontend +like "Lish" will make no sense but a frontend like "https" or "xmpp" could be useful anyway... +""" + + +class LinspectorDaemon(Daemon): + def run(self): + while True: + try: + a = 2 + #logger.writeLogToFile(_logfile, "Running!") + print "running!" + except Exception as err: + #logger.writeLogToFile(_logfile, str(err)) + print "failed" + sys.exit(1) + time.sleep(1) + class Daemon: """ diff --git a/linspector/core/job.py b/linspector/core/job.py index fc49733..d88bbd0 100644 --- a/linspector/core/job.py +++ b/linspector/core/job.py @@ -32,21 +32,22 @@ def generateId(): class Job: - def __init__(self, service, host, members, processors, core): + def __init__(self, service, host, members, processors, core, hostgroup): self.service = service self.host = host self.members = members self.processors = processors self.core = core + self.hostgroup = hostgroup self.jobInfos = [] self.jobThreshold = 0 + self._enabled = True def __str__(self): return str(self.__dict__) - def __hex__(self): - return hex(crc32(str(self.service) + str(self.host) + str(self.members))) + return hex(crc32(str(self.hostgroup) + str(self.host) + str(self.service) + str(self.members))) def hex_string(self): ret = self.__hex__() @@ -62,7 +63,9 @@ class Job: self.log = log def pretty_string(self): - return self.hex_string() + ": (" + str(self.host) + str(self.service) + str(self.job) + ")" + ret = (self.hex_string() + ": (Hostgroup: " + str(self.hostgroup.get_name()) + + " Host: " + str(self.host) + " Service: " + str(self.service) + " " + str(self.job) + ")") + return ret def set_job(self, job): self.job = job @@ -70,7 +73,15 @@ class Job: def handle_threshold(self, jobInfo, serviceThreshold, executionSucessful): if executionSucessful: if self.jobThreshold > 0: - self.jobThreshold -= 1 + #TODO: maybe set threshold_handling for each service optionally; will override core setting! + if self.core["threshold_handling"] == "reset": + # Reset counter to 0 + self.log.debug("Threshold Reset") + self.jobThreshold = 0 + else: + # Decrement the counter (default) + self.log.debug("Threshold Decrement") + self.jobThreshold -= 1 else: self.jobThreshold += 1 @@ -87,23 +98,32 @@ class Job: def handle_call(self): self.log.debug("handle call") self.log.debug(self.service) - try: - jobInfo = JobInfo(self.__hex__(), self.host, self.service) - self.service._execute(jobInfo) - jobInfo.set_execution_end() + if self._enabled: + try: + jobInfo = JobInfo(self.hex_string(), self.host, self.service) + self.service._execute(jobInfo) + jobInfo.set_execution_end() - self.handle_threshold(jobInfo, self.service.get_threshold(), jobInfo.was_execution_successful()) + self.handle_threshold(jobInfo, self.service.get_threshold(), jobInfo.was_execution_successful()) - self.log.debug("Code: " + str(jobInfo.get_errorcode()) + ", Message: " + str(jobInfo.get_message())) + self.log.debug("Code: " + str(jobInfo.get_errorcode()) + ", Message: " + str(jobInfo.get_message())) - self.jobInfos.append(jobInfo) + self.jobInfos.append(jobInfo) - except Exception, e: - self.log.debug(e) + except Exception, e: + self.log.debug(e) + else: + self.log.debug("Job " + self.hex_string() + " disabled") + + def enable(self): + self._enabled = True + + def disable(self): + self._enabled = False class JobInfo(object): - def __init__(self,jobHex, host, service): + def __init__(self, jobHex, host, service): self.id = generateId() self.jobHex = jobHex self.host = host diff --git a/linspector/frontends/lish.py b/linspector/frontends/lish.py index ffc89af..ee425c0 100644 --- a/linspector/frontends/lish.py +++ b/linspector/frontends/lish.py @@ -1,7 +1,8 @@ """ -Lish is the Linspector Interactive Shell... +Lish is the Linspector Interactive Shell. -This will become a commandline interface to Linspector. Think of a network switch or router like those from Cisco. +This will become a commandline interface to Linspector. Think of a +network switch or router like those from Cisco. Copyright (c) 2011-2013 "Johannes Findeisen and Rafael Timmerberg" @@ -21,13 +22,12 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . """ - from linspector.frontends.frontend import Frontend import os from shlex import split as shsplit from cmd import Cmd -__version__ = "0.1" +__version__ = "0.1.1" class LishFrontend(Frontend): @@ -128,7 +128,6 @@ class HostgroupCommander(Exit, object): class LishCommander(Exit, ShellCommander, LogCommander): - def __init__(self, kwargs): super(LishCommander, self).__init__() @@ -167,31 +166,44 @@ class LishCommander(Exit, ShellCommander, LogCommander): except KeyboardInterrupt, key: pass + def complete_hostgroup(self, text, line, begidx, endidx): + if begidx == 10: + return [x for x in self._hostgroupArgs if x.startswith(text)] if len(text) > 0 else self._hostgroupArgs + + def help_hostgroup(self): + print "usage:\n\t" + \ + "hostgroup list\n\t\t\t" + \ + "prints a list of all hostgroups\n\t" + \ + "hostgroup select HOSTGROUPNAME\n\t\t\t" + \ + "select a hostgroup to make changes on it" + def do_python(self, text): exec text + def help_python(self): + print "executes python using 'exec'." + + def do_job(self, text): + if text == "disable": + pass + elif text == "enable": + pass + else: + print "invalid or missing parameter\n" + self.help_job() + + def help_job(self): + print "Job helper functions:\n\t" + \ + "disable :\t\tdisable a job\n\t" + \ + "enable :\t\tenable a job\n\t" + def do_jobs(self, text): if text == "list": for job in self._jobs: print job.pretty_string() + else: + print "invalid or missing parameter\n" + self.help_jobs() def help_jobs(self): - print "Job helper functions" - - def help_python(self): - print ''' - executes python using 'exec'. - ''' - - def help_hostgroup(self): - print ''' - usage: - hostgroup list - prints a list of all hostgroups - hostgroup select HOSTGROUPNAME - select a hostgroup to make changes on it - ''' - - def complete_hostgroup(self, text, line, begidx, endidx): - if begidx == 10: - return [x for x in self._hostgroupArgs if x.startswith(text)] if len(text) > 0 else self._hostgroupArgs \ No newline at end of file + print "Joblist helper functions:\n\t" + "list:\t\t\tlists all jobs" \ No newline at end of file diff --git a/linspector/services/tcpconnect.py b/linspector/services/tcpconnect.py index 2bfcf70..af0cd40 100644 --- a/linspector/services/tcpconnect.py +++ b/linspector/services/tcpconnect.py @@ -1,8 +1,8 @@ """ -The tcpconnect service. This is to check if a service on a specific port is reachable. +The tcpconnect service. This is to check if a TCP service on a specific +port is reachable. -This should just return 0 on success and NOT 0 on error. Just to make internals generic to just report this code and -not use a parser. +This should just return 0 on success and NOT 0 on error. Copyright (c) 2011-2013 "Johannes Findeisen and Rafael Timmerberg" @@ -44,21 +44,21 @@ class TcpconnectService(Service): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error, msg: jobInfo.set_errorcode(2) - jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not create socket to host: " + jobInfo.get_host() + - " on port: " + str(self.port) + " (" + str(msg) + ")") + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not create socket to host: " + + jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")") try: sock.connect((jobInfo.get_host(), self.port)) except socket.error, msg: jobInfo.set_errorcode(1) - jobInfo.set_message("[tcpconnect] Could not establish connection to host: " + jobInfo.get_host() + - " on port: " + str(self.port) + " (" + str(msg) + ")") + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not establish connection to host: " + + jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")") if jobInfo.get_errorcode() == -1: jobInfo.set_execution_successful(True) jobInfo.set_errorcode(0) - jobInfo.set_message("[tcpconnect] Connection successful established to host: " + jobInfo.get_host() + - " on port: " + str(self.port)) + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Connection successful established to host: " + + jobInfo.get_host() + " on port: " + str(self.port)) sock.close() diff --git a/linspector/services/udpconnect.py b/linspector/services/udpconnect.py new file mode 100644 index 0000000..0f35335 --- /dev/null +++ b/linspector/services/udpconnect.py @@ -0,0 +1,67 @@ +""" +The udpconnect service. This is to check if a UDP service on a specific +port is reachable. + +This should just return 0 on success and NOT 0 on error. + +Copyright (c) 2011-2013 "Johannes Findeisen and Rafael Timmerberg" + +This file is part of Linspector (http://linspector.org). + +Linspector is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + +import socket +from linspector.services.service import Service + + +class UdpconnectService(Service): + def __init__(self, **kwargs): + super(UdpconnectService, self).__init__(**kwargs) + + args = self.get_arguments() + if "port" in args: + self.port = args["port"] + else: + raise Exception("There is no port set") + + def needs_arguments(self): + return True + + def execute(self, jobInfo): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + except socket.error, msg: + jobInfo.set_errorcode(2) + jobInfo.set_message("[udpconnect: " + jobInfo.jobHex + "] Could not create socket to host: " + + jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")") + + try: + sock.connect((jobInfo.get_host(), self.port)) + except socket.error, msg: + jobInfo.set_errorcode(1) + jobInfo.set_message("[udpconnect: " + jobInfo.jobHex + "] Could not establish connection to host: " + + jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")") + + if jobInfo.get_errorcode() == -1: + jobInfo.set_execution_successful(True) + jobInfo.set_errorcode(0) + jobInfo.set_message("[udpconnect: " + jobInfo.jobHex + "] Connection successful established to host: " + + jobInfo.get_host() + " on port: " + str(self.port)) + + sock.close() + + +def create(kwargs): + return UdpconnectService(**kwargs) \ No newline at end of file