1
0
Fork 0
mirror of https://github.com/munin-monitoring/contrib.git synced 2025-07-26 02:48:28 +00:00

[monit_parser] change input format to XML; retrieve via URL (Closes: #558)

This commit is contained in:
Lars Kruse 2016-10-23 05:05:28 +02:00
parent f69a442779
commit ef6cedf7a9

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
""" """
=head1 NAME =head1 NAME
@ -8,23 +8,22 @@ monit_parser - Monit status parser plugin for munin.
=head1 APPLICABLE SYSTEMS =head1 APPLICABLE SYSTEMS
Any system running monit. Any system being able to query monit servers via http.
Monit needs to be configured with the httpd port enabled, e.g.: Monit needs to be configured with the httpd port enabled, e.g.:
set httpd port 2812 set httpd port 2812
The configured port is not important, since this plugin uses "monit status"
for retrieving data. Thus the monit configuration is parsed implicitly.
=head1 CONFIGURATION =head1 CONFIGURATION
This plugin requires read access for the monit configuration. By default the monit instance at localhost is queried:
Thus it needs to run as root:
[monit_parser] [monit_parser]
user root env.port 2812
env.host localhost
=head1 AUTHOR =head1 AUTHOR
@ -41,9 +40,14 @@ Thus it needs to run as root:
=cut =cut
""" """
import re import xml.dom.minidom
import subprocess import os
import sys import sys
import urllib.request
MONIT_XML_URL = ("http://{host}:{port}/_status?format=xml"
.format(host=os.getenv("host", "localhost"),
port=os.getenv("port", "2812")))
def sanitize(s): def sanitize(s):
@ -51,53 +55,31 @@ def sanitize(s):
return "".join([char for char in s if char in OK_CHARS]) return "".join([char for char in s if char in OK_CHARS])
def get_monit_status_lines(): def get_monit_status_xml():
# retrieve output
try: try:
output = subprocess.check_output(["monit", "status"]) conn = urllib.request.urlopen(MONIT_XML_URL)
error_message = None except urllib.error.URLError as exc:
except (OSError, subprocess.CalledProcessError) as exc: conn = None
error_message = "Error running monit status command: %s" % str(exc) if conn is None:
if error_message is not None: raise RuntimeError("Failed to open monit status URL: {}".format(MONIT_XML_URL))
raise OSError(error_message) else:
# python3: the result of command execution is bytes and needs to be converted return xml.dom.minidom.parse(conn)
if not isinstance(output, str):
output = output.decode("ascii", "ignore")
return output.splitlines()
def parse_processes(): def parse_processes():
cur_proc = None dom = get_monit_status_xml()
procs = {} procs = {}
for line in get_monit_status_lines(): for item in dom.getElementsByTagName("service"):
m = re.match("^Process '(.*)'.*$", line) if item.getAttribute("type") == "3":
if m: # daemon with memory usage and CPU
cur_proc = sanitize(m.group(1)) name = item.getElementsByTagName("name")[0].childNodes[0].data
try: memory_usage = item.getElementsByTagName("memory")[0].getElementsByTagName(
procs[cur_proc] "kilobytetotal")[0].childNodes[0].data
except KeyError: cpu_usage = item.getElementsByTagName("cpu")[0].getElementsByTagName(
procs[cur_proc] = {} "percenttotal")[0].childNodes[0].data
continue procs[name] = {}
m = re.match(" memory kilobytes total\s+([0-9.]+)(.*)$", line) procs[name]["total_memory"] = memory_usage
if m: procs[name]["total_cpu"] = cpu_usage
size, suffix = m.groups()
# we store memory consumption as megabytes
factor = {
"": 1024 ** -1,
"KB": 1024 ** -1,
"MB": 1024 ** 0,
"GB": 1024 ** 1,
"TB": 1024 ** 2,
}[suffix.strip().upper()]
try:
procs[cur_proc]["total_memory"] = float(size) * factor
except ValueError:
procs[cur_proc]["total_memory"] = "U"
continue
m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line)
if m:
procs[cur_proc]["total_cpu"] = m.group(1)
continue
return procs return procs
@ -105,22 +87,22 @@ action = sys.argv[1] if (len(sys.argv) > 1) else None
if action == 'autoconf': if action == 'autoconf':
try: try:
get_monit_status_lines() get_monit_status_xml()
print("yes") print("yes")
except OSError: except RuntimeError:
print("no (failed to request monit status)") print("no (failed to request monit status)")
elif action == 'config': elif action == 'config':
procs = parse_processes() procs = parse_processes()
print('graph_title Per process stats from Monit') print('graph_title Per process stats from Monit')
print('graph_vlabel usage of memory [MB] or cpu [%]') print('graph_vlabel usage of memory [kB] or cpu [%]')
print('graph_category monit') print('graph_category monit')
for process in procs: for process in procs:
for stat in procs[process]: for stat in procs[process]:
print("monit_%s_%s.label %s.%s" % (process, stat, process, stat)) print("monit_%s_%s.label %s.%s" % (sanitize(process), stat, process, stat))
if stat == 'total_memory': if stat == 'total_memory':
# the allocated memory may never be zero # the allocated memory may never be zero
print("monit_%s_%s.warning 1:" % (process, stat)) print("monit_%s_%s.warning 1:" % (sanitize(process), stat))
else: else:
for process, stats in parse_processes().items(): for process, stats in parse_processes().items():
for stat_key, stat_value in stats.items(): for stat_key, stat_value in stats.items():
print("monit_%s_%s.value %s" % (process, stat_key, stat_value)) print("monit_%s_%s.value %s" % (sanitize(process), stat_key, stat_value))