Merge remote-tracking branch 'origin/master'

Conflicts:
	.gitignore
	bin/linspector
	examples/linspector.json
	linspector/core/job.py
	linspector/frontends/lish.py
	linspector/services/tcpconnect.py
This commit is contained in:
Rafael.Timmerberg 2013-10-17 21:08:30 +02:00
commit 9de9af9921
8 changed files with 186 additions and 61 deletions

3
.gitignore vendored
View file

@ -9,10 +9,11 @@ nbproject
distfiles distfiles
docs docs
documentation
files files
local local
log log
plugins plugins
.metadata .metadata
examples/private.json examples/private.json

View file

@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
__version__ = "0.6/TCPCONNECT" __version__ = "0.1.7"
__default_config__ = "./examples/minimal.json" __default_config__ = "./examples/minimal.json"
import argparse import argparse
@ -35,7 +35,7 @@ from linspector.backends.https import HttpsBackend
from linspector.frontends.lish import LishFrontend from linspector.frontends.lish import LishFrontend
def parseArgs(): def parse_args():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description="Linspector is for monitoring the vital information of hosts, services and devices in a network.", 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 " 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() 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) logfile = path.expanduser(logfile)
if not path.exists(path.dirname(logfile)): if not path.exists(path.dirname(logfile)):
os.makedirs(path.dirname(logfile)) os.makedirs(path.dirname(logfile))
logging.basicConfig(level=logging.WARNING) #logging.basicConfig(level=logging.WARNING)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
#log.setLevel(logging.ERROR) log.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler() consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logLevel) consoleHandler.setLevel(logLevel)
@ -90,14 +90,14 @@ def setupLogging(logfile="./log/linspector.log", logLevel=logging.ERROR, logfile
return log return log
def handleJob(jobInfo): def handle_job(jobInfo):
jobInfo.handle_call() jobInfo.handle_call()
def main(): def main():
args = parseArgs() args = parse_args()
#TODO: catch all log messages (apscheduler etc.); make logging more generic to support libraries logging #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") log.info("parsed arguments")
@ -114,8 +114,8 @@ def main():
for service in hostgroup.get_services(): for service in hostgroup.get_services():
for host in hostgroup.get_hosts(): for host in hostgroup.get_hosts():
for period in service.get_periods(): for period in service.get_periods():
job = Job(service, host, hostgroup.get_members(), hostgroup.get_processors(), core) job = Job(service, host, hostgroup.get_members(), hostgroup.get_processors(), core, hostgroup)
schedulerJob = period.createJob(scheduler, job, handleJob) schedulerJob = period.createJob(scheduler, job, handle_job)
if schedulerJob is not None: if schedulerJob is not None:
job.set_job(schedulerJob) job.set_job(schedulerJob)
job.set_logger(log) job.set_logger(log)
@ -131,9 +131,9 @@ def main():
frontend = LishFrontend(jobs=jobs, scheduler=scheduler, linspectorConfig=linConf) frontend = LishFrontend(jobs=jobs, scheduler=scheduler, linspectorConfig=linConf)
log.debug("shutting down scheduler") log.debug("shutting down scheduler")
logging.shutdown()
scheduler.shutdown(wait=True) scheduler.shutdown(wait=True)
logging.shutdown()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -170,6 +170,7 @@
} }
}, },
"layouts":{ "layouts":{
"_comment": "https://github.com/linspector/linspector/wiki/Configuration-Layouts",
"production":{ "production":{
"hostgroups":[ "group1" ], "hostgroups":[ "group1" ],
"enabled": true "enabled": true
@ -184,13 +185,16 @@
} }
}, },
"core":{ "core":{
"_comment": "https://github.com/linspector/linspector/wiki/Configuration-Core",
"instance_name": "Master Monitoring (monitor.example.org)", "instance_name": "Master Monitoring (monitor.example.org)",
"max_logfile_size": 1024000, "max_logfile_size": 1024000,
"max_logfile_count": 4, "max_logfile_count": 4,
"max_worker_threads": 8, "max_worker_threads": 8,
"threshold_handling": "reset",
"members":[ "root" ], "members":[ "root" ],
"backends": { "backends": {
"jsonrpc": { "jsonrpc": {
"_comment": "https://github.com/linspector/linspector/wiki/Configuration-Core-Backends-JsonRPC",
"host": "127.0.0.1", "host": "127.0.0.1",
"port": "2323", "port": "2323",
"username": "linspector", "username": "linspector",

View file

@ -24,6 +24,27 @@ import time
import atexit import atexit
from signal import SIGTERM 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: class Daemon:
""" """

View file

@ -32,21 +32,22 @@ def generateId():
class Job: class Job:
def __init__(self, service, host, members, processors, core): def __init__(self, service, host, members, processors, core, hostgroup):
self.service = service self.service = service
self.host = host self.host = host
self.members = members self.members = members
self.processors = processors self.processors = processors
self.core = core self.core = core
self.hostgroup = hostgroup
self.jobInfos = [] self.jobInfos = []
self.jobThreshold = 0 self.jobThreshold = 0
self._enabled = True
def __str__(self): def __str__(self):
return str(self.__dict__) return str(self.__dict__)
def __hex__(self): 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): def hex_string(self):
ret = self.__hex__() ret = self.__hex__()
@ -62,7 +63,9 @@ class Job:
self.log = log self.log = log
def pretty_string(self): 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): def set_job(self, job):
self.job = job self.job = job
@ -70,7 +73,15 @@ class Job:
def handle_threshold(self, jobInfo, serviceThreshold, executionSucessful): def handle_threshold(self, jobInfo, serviceThreshold, executionSucessful):
if executionSucessful: if executionSucessful:
if self.jobThreshold > 0: 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: else:
self.jobThreshold += 1 self.jobThreshold += 1
@ -87,23 +98,32 @@ class Job:
def handle_call(self): def handle_call(self):
self.log.debug("handle call") self.log.debug("handle call")
self.log.debug(self.service) self.log.debug(self.service)
try: if self._enabled:
jobInfo = JobInfo(self.__hex__(), self.host, self.service) try:
self.service._execute(jobInfo) jobInfo = JobInfo(self.hex_string(), self.host, self.service)
jobInfo.set_execution_end() 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: except Exception, e:
self.log.debug(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): class JobInfo(object):
def __init__(self,jobHex, host, service): def __init__(self, jobHex, host, service):
self.id = generateId() self.id = generateId()
self.jobHex = jobHex self.jobHex = jobHex
self.host = host self.host = host

View file

@ -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" 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 <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
from linspector.frontends.frontend import Frontend from linspector.frontends.frontend import Frontend
import os import os
from shlex import split as shsplit from shlex import split as shsplit
from cmd import Cmd from cmd import Cmd
__version__ = "0.1" __version__ = "0.1.1"
class LishFrontend(Frontend): class LishFrontend(Frontend):
@ -128,7 +128,6 @@ class HostgroupCommander(Exit, object):
class LishCommander(Exit, ShellCommander, LogCommander): class LishCommander(Exit, ShellCommander, LogCommander):
def __init__(self, kwargs): def __init__(self, kwargs):
super(LishCommander, self).__init__() super(LishCommander, self).__init__()
@ -167,31 +166,44 @@ class LishCommander(Exit, ShellCommander, LogCommander):
except KeyboardInterrupt, key: except KeyboardInterrupt, key:
pass 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): def do_python(self, text):
exec 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 <ID>:\t\tdisable a job\n\t" + \
"enable <ID>:\t\tenable a job\n\t"
def do_jobs(self, text): def do_jobs(self, text):
if text == "list": if text == "list":
for job in self._jobs: for job in self._jobs:
print job.pretty_string() print job.pretty_string()
else:
print "invalid or missing parameter\n"
self.help_jobs()
def help_jobs(self): def help_jobs(self):
print "Job helper functions" print "Joblist helper functions:\n\t" + "list:\t\t\tlists all jobs"
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

View file

@ -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 This should just return 0 on success and NOT 0 on error.
not use a parser.
Copyright (c) 2011-2013 "Johannes Findeisen and Rafael Timmerberg" 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) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg: except socket.error, msg:
jobInfo.set_errorcode(2) jobInfo.set_errorcode(2)
jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not create socket to host: " + jobInfo.get_host() + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not create socket to host: " +
" on port: " + str(self.port) + " (" + str(msg) + ")") jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")")
try: try:
sock.connect((jobInfo.get_host(), self.port)) sock.connect((jobInfo.get_host(), self.port))
except socket.error, msg: except socket.error, msg:
jobInfo.set_errorcode(1) jobInfo.set_errorcode(1)
jobInfo.set_message("[tcpconnect] Could not establish connection to host: " + jobInfo.get_host() + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Could not establish connection to host: " +
" on port: " + str(self.port) + " (" + str(msg) + ")") jobInfo.get_host() + " on port: " + str(self.port) + " (" + str(msg) + ")")
if jobInfo.get_errorcode() == -1: if jobInfo.get_errorcode() == -1:
jobInfo.set_execution_successful(True) jobInfo.set_execution_successful(True)
jobInfo.set_errorcode(0) jobInfo.set_errorcode(0)
jobInfo.set_message("[tcpconnect] Connection successful established to host: " + jobInfo.get_host() + jobInfo.set_message("[tcpconnect: " + jobInfo.jobHex + "] Connection successful established to host: " +
" on port: " + str(self.port)) jobInfo.get_host() + " on port: " + str(self.port))
sock.close() sock.close()

View file

@ -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 <http://www.gnu.org/licenses/>.
"""
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)