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
docs
documentation
files
local
log
plugins
.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/>.
"""
__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()
main()

View file

@ -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",

View file

@ -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:
"""

View file

@ -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

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"
@ -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/>.
"""
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 <ID>:\t\tdisable a job\n\t" + \
"enable <ID>:\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
print "Joblist helper functions:\n\t" + "list:\t\t\tlists all jobs"

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
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()

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)