2
0
mirror of https://github.com/munin-monitoring/contrib.git synced 2018-11-08 00:59:34 +01:00
contrib-munin/tools/munin-node-from-hell/muninnode-from-hell

317 lines
10 KiB
Plaintext
Raw Normal View History

#!/usr/bin/python
#
# Artificial munin node that behaves in all the ways you would like
# ordinary nodes _not_ to behave.
#
# Intended use is for designing and debugging munin-server poller to handle
# such problems.
#
# See the file MIT-LICENSE for licensing information.
#
# Copyright (C) 2011 Karstensen IT
# Written by Lasse Karstensen <lasse.karstensen@gmail.com>, Dec 2011.
import os, sys, time, random
import socket
import threading
import SocketServer
import ConfigParser
VERSION = "muninnode-from-hell v0.1"
modules = {}
class MuninPlugin:
def sleep_fetch(self, conf):
period = None
if conf.get("mode") == "sleepy" and conf.get("sleepyness"):
period = float(conf.get("sleepyness"))
if conf.get("mode") == "exp" and conf.get("lambd"):
period = random.expovariate(1 / float(conf.get("lambd")))
if period:
#print "will sleep %.3f seconds" % period
time.sleep(period)
def sleep_config(self, conf):
return self.sleep_fetch(conf)
class load(MuninPlugin):
def fetch(self, conf):
self.sleep_fetch(conf)
load = open("/proc/loadavg", "r").read()
load, rest = load.split(" ", 1)
load = float(load)
return "load.value %.2f" % load
def config(self, conf):
self.sleep_config(conf)
return """graph_title Load average
graph_args --base 1000 -l 0
graph_vlabel load
graph_scale no
graph_category system
load.label load
graph_info The load average of the machine describes how many processes are in the run-queue (scheduled to run "immediately").
load.info 5 minute load average """
modules["load"] = load()
class locks(MuninPlugin):
def fetch(self, conf):
self.sleep_fetch(conf)
fp = open("/proc/locks", "r")
fdata = fp.readlines()
return "locks.value %i" % len(fdata)
def config(self, conf):
self.sleep_config(conf)
return """graph_title Filesystem locks
graph_vlabel number of locks
graph_scale no
graph_info This graph shows file system lock info
graph_category system
locks.label number of locks
locks.info Number of active locks"""
modules["locks"] = locks()
class tarpit(MuninPlugin):
"Nasty plugin that never responds"
def fetch(self, conf):
time.sleep(1000)
def config(self, conf):
time.sleep(1000)
modules["tarpit"] = tarpit()
class always_warning(MuninPlugin):
def fetch(self, conf):
return "generic.value 10"
def config(self, conf):
return """graph_title Always in warning
graph_vlabel Level
graph_scale no
graph_info A simple graph that is always in warning or critical
graph_category active_notification
generic.label Level
generic.info Level usually above warning level
generic.warn 5
generic.crit 10"""
modules["always_warning"] = always_warning()
class always_critical(always_warning):
def fetch(self, conf):
return "generic.value 20"
modules["always_critical"] = always_critical()
class graph_area(MuninPlugin):
"A plugin that uses STACK and AREA. From proc_pri. Use: testing the grapher"
def fetch(self, conf):
return """high.value 3
low.value 2
locked.value 1"""
def config(self, conf):
return """graph_title AREA and STACK
graph_order low high locked
graph_category graphtes t
graph_info This graph shows nuber of processes at each priority
graph_args --base 1000 -l 0
graph_vlabel Number of processes
high.label high priority
high.draw STACK
high.info The number of high-priority processes (tasks)
low.label low priority
low.draw AREA
low.info The number of low-priority processes (tasks)
locked.label locked in memory
locked.draw STACK
locked.info The number of processes that have pages locked into memory (for real-time and custom IO)
"""
modules["graph_area"] = graph_area()
class ArgumentTCPserver(SocketServer.ThreadingTCPServer):
def __init__(self, server_address, RequestHandlerClass, args):
SocketServer.ThreadingTCPServer.__init__(self,server_address, RequestHandlerClass)
self.args = args
class MuninHandler(SocketServer.StreamRequestHandler):
"""
Munin server implementation.
This is based on munin_node.py by Chris Holcombe / http://sourceforge.net/projects/pythonmuninnode/
Possible commands:
list, nodes, config, fetch, version or quit
"""
def handle(self):
print "%s: Connection from %s:%s. server args is %s" \
% (self.server.args["name"], self.client_address[0], self.client_address[1], self.server.args)
# slow path
hostname = self.server.args["name"]
full_hostname = hostname
moduleprofile = self.server.args["pluginprofile"]
modulenames = set(moduleprofile)
self.wfile.write("# munin node at %s\n" % hostname)
while True:
line = self.rfile.readline().strip()
try:
cmd, args = line.split(" ", 1)
except ValueError:
cmd = line
args = ""
if not cmd or cmd == "quit":
break
if cmd == "list":
# List all plugins that are available
self.wfile.write(" ".join(self.server.args["plugins"].keys()) + "\n")
elif cmd == "nodes":
# We just support this host
self.wfile.write("%s\n.\n" % full_hostname)
elif cmd == "config":
# display the config information of the plugin
if not self.server.args["plugins"].has_key(args):
self.wfile.write("# Unknown service\n.\n" )
else:
config = self.server.args["plugins"][args].config(self.server.args)
if config is None:
self.wfile.write("# Unknown service\n.\n")
else:
self.wfile.write(config + "\n.\n")
elif cmd == "fetch":
# display the data information as returned by the plugin
if not self.server.args["plugins"].has_key(args):
self.wfile.write("# Unknown service\n.\n")
else:
data = self.server.args["plugins"][args].fetch(self.server.args)
if data is None:
self.wfile.write("# Unknown service\n.\n")
else:
self.wfile.write(data + "\n.\n")
elif cmd == "version":
# display the server version
self.wfile.write("munin node on %s version: %s\n" %
(full_hostname, VERSION))
else:
self.wfile.write("# Unknown command. Try list, nodes, " \
"config, fetch, version or quit\n")
def start_servers(instances):
# TODO: Listen to IPv6
HOST = "0.0.0.0"
servers = {}
for iconf in instances:
print "Setting up instance %s at port %s" \
% (iconf["name"], iconf["expanded_port"])
server = ArgumentTCPserver((HOST, iconf["expanded_port"]), MuninHandler, iconf)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
servers[iconf["name"]] = server
return servers
def usage():
print "Usage: %s [--run] [--muninconf] <configfile>" % sys.argv[0]
def main():
if len(sys.argv) <= 2:
usage()
sys.exit(1)
config = ConfigParser.RawConfigParser()
config.read(sys.argv[2])
instancekeys = [ key for key in config.sections() if key.startswith("instance:") ]
servers = {}
instances = []
for key in instancekeys:
instancename = key.split(":", 2)[1]
portrange = []
if config.has_option(key, "port"):
portrange = [ config.getint(key, "port") ]
if config.has_option(key, "portrange"):
rangestr = config.get(key, "portrange")
ranges = rangestr.split("-")
range_expanded = range(int(ranges[0]), int(ranges[1])+1, 1)
portrange += range_expanded
if len(portrange) == 0:
print "WARN: No port or portrange defined for instance %s" \
% instancename
pluginprofile = "pluginprofile:%s" % config.get(key, "pluginprofile")
if not config.has_section(pluginprofile):
print "WARN: Definition for pluginprofile %s not found, skipping" \
% config.get(key, "pluginprofile")
continue
plugins = {}
tentative_pluginlist = config.get(pluginprofile, "plugins").split(",")
assert(len(tentative_pluginlist) > 0)
for tentative_plugin in tentative_pluginlist:
tentative_plugin = tentative_plugin.strip()
if not modules.has_key(tentative_plugin):
print "WARN: Pluginprofile %s specifies unknown plugin %s" \
% (pluginprofile, tentative_plugin)
continue
# support more than one instanciation of the same plugin.
plugininstancename = tentative_plugin
i=2
while (plugins.has_key(plugininstancename)):
plugininstancename = tentative_plugin + str(i)
i += 1
plugins[plugininstancename] = modules[tentative_plugin]
for portinstance in portrange:
instanceconfig = dict()
for k,v in config.items(key):
instanceconfig[k] = v
instanceconfig["plugins"] = plugins
instanceconfig["name"] = "%s-%s" % (instancename, portinstance)
instanceconfig["expanded_port"] = portinstance
instances.append(instanceconfig)
# XXX: need to store what handlers we should have.
# output sample munin config for the poller
if "--muninconf" in sys.argv:
for i in instances:
print "[%s;%s]\n\taddress %s\n\tuse_node_name yes\n\tport %s\n" \
% ( "fromhell", i["name"], config.get("base","hostname"), i["port"])
if "--run" in sys.argv:
servers = start_servers(instances)
try:
while True:
time.sleep(0.5)
except KeyboardInterrupt:
print "Caught Ctrl-c, shutting down.."
for port, server in servers.items():
server.shutdown()
sys.exit(0)
if __name__ == "__main__":
main()