2007-07-21 09:42:54 +02:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2016-10-23 03:14:48 +02:00
|
|
|
"""
|
|
|
|
=head1 NAME
|
2007-07-21 09:42:54 +02:00
|
|
|
|
2016-10-23 03:14:48 +02:00
|
|
|
monit_parser - Monit status parser plugin for munin.
|
2007-07-21 09:42:54 +02:00
|
|
|
|
2016-10-23 03:14:48 +02:00
|
|
|
|
|
|
|
=head1 APPLICABLE SYSTEMS
|
|
|
|
|
|
|
|
Any system running monit.
|
|
|
|
|
|
|
|
Monit needs to be configured with the httpd port enabled, e.g.:
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
This plugin requires read access for the monit configuration.
|
|
|
|
Thus it needs to run as root:
|
|
|
|
|
|
|
|
[monit_parser]
|
|
|
|
user root
|
|
|
|
|
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
|
|
|
|
Todd Troxell <ttroxell@debian.org>
|
|
|
|
Lars Kruse <devel@sumpfralle.de>
|
|
|
|
|
|
|
|
|
|
|
|
=head1 MAGIC MARKERS
|
|
|
|
|
|
|
|
family=auto
|
|
|
|
capabilities=autoconf
|
|
|
|
|
|
|
|
=cut
|
|
|
|
"""
|
2007-07-21 09:42:54 +02:00
|
|
|
|
|
|
|
import re
|
2016-10-23 01:27:25 +02:00
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
2007-07-21 09:42:54 +02:00
|
|
|
|
|
|
|
def sanitize(s):
|
|
|
|
OK_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"
|
2016-10-23 03:20:05 +02:00
|
|
|
return "".join([char for char in s if char in OK_CHARS])
|
|
|
|
|
|
|
|
|
|
|
|
def get_monit_status_lines():
|
|
|
|
# retrieve output
|
|
|
|
try:
|
|
|
|
output = subprocess.check_output(["monit", "status"])
|
|
|
|
error_message = None
|
|
|
|
except (OSError, subprocess.CalledProcessError) as exc:
|
|
|
|
error_message = "Error running monit status command: %s" % str(exc)
|
|
|
|
if error_message is not None:
|
|
|
|
raise OSError(error_message)
|
|
|
|
# python3: the result of command execution is bytes and needs to be converted
|
|
|
|
if not isinstance(output, str):
|
|
|
|
output = output.decode("ascii", "ignore")
|
|
|
|
return output.splitlines()
|
|
|
|
|
|
|
|
|
|
|
|
def parse_processes():
|
|
|
|
cur_proc = None
|
|
|
|
procs = {}
|
|
|
|
for line in get_monit_status_lines():
|
|
|
|
m = re.match("^Process '(.*)'.*$", line)
|
|
|
|
if m:
|
|
|
|
cur_proc = sanitize(m.group(1))
|
|
|
|
try:
|
|
|
|
procs[cur_proc]
|
|
|
|
except KeyError:
|
|
|
|
procs[cur_proc] = {}
|
|
|
|
continue
|
2016-10-23 03:40:44 +02:00
|
|
|
m = re.match(" memory kilobytes total\s+([0-9.]+)(.*)$", line)
|
2016-10-23 03:20:05 +02:00
|
|
|
if m:
|
2016-10-23 03:40:44 +02:00
|
|
|
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"
|
2016-10-23 03:20:05 +02:00
|
|
|
continue
|
|
|
|
m = re.match(" cpu percent total\s+([.0-9]+)%.*$", line)
|
|
|
|
if m:
|
|
|
|
procs[cur_proc]["total_cpu"] = m.group(1)
|
|
|
|
continue
|
|
|
|
return procs
|
|
|
|
|
|
|
|
|
|
|
|
action = sys.argv[1] if (len(sys.argv) > 1) else None
|
|
|
|
|
|
|
|
if action == 'autoconf':
|
|
|
|
try:
|
|
|
|
get_monit_status_lines()
|
|
|
|
print("yes")
|
|
|
|
except OSError:
|
|
|
|
print("no (failed to request monit status)")
|
|
|
|
elif action == 'config':
|
|
|
|
procs = parse_processes()
|
2016-10-23 01:27:50 +02:00
|
|
|
print('graph_title Per process stats from Monit')
|
2016-10-23 03:40:44 +02:00
|
|
|
print('graph_vlabel usage of memory [MB] or cpu [%]')
|
2016-10-23 01:27:50 +02:00
|
|
|
print('graph_category monit')
|
2007-07-21 09:42:54 +02:00
|
|
|
for process in procs:
|
|
|
|
for stat in procs[process]:
|
2016-10-23 01:27:50 +02:00
|
|
|
print("monit_%s_%s.label %s.%s" % (process, stat, process, stat))
|
2015-06-08 17:10:04 +02:00
|
|
|
if stat == 'total_memory':
|
2016-10-23 03:20:05 +02:00
|
|
|
# the allocated memory may never be zero
|
|
|
|
print("monit_%s_%s.warning 1:" % (process, stat))
|
|
|
|
else:
|
|
|
|
for process, stats in parse_processes().items():
|
|
|
|
for stat_key, stat_value in stats.items():
|
|
|
|
print("monit_%s_%s.value %s" % (process, stat_key, stat_value))
|