mirror of
https://github.com/munin-monitoring/contrib.git
synced 2025-07-25 02:18:08 +00:00
Add munin-node-from-hell, a quite unfriendly munin node
This commit is contained in:
parent
5cc0fa32fe
commit
d33f5b319f
7 changed files with 469 additions and 0 deletions
290
tools/munin-node-from-hell/muninnode-from-hell
Executable file
290
tools/munin-node-from-hell/muninnode-from-hell
Executable file
|
@ -0,0 +1,290 @@
|
|||
#!/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 alarm
|
||||
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_alarm(always_warning):
|
||||
def fetch(self, conf):
|
||||
return "generic.value 20"
|
||||
modules["always_alarm"] = always_alarm()
|
||||
|
||||
|
||||
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()
|
Loading…
Add table
Add a link
Reference in a new issue