mirror of
https://github.com/munin-monitoring/contrib.git
synced 2025-07-21 18:41:03 +00:00
[plugin/user/cronjobs] added plugin to monitor cronjobs
Graphs the number of cronjobs active at a certain moment, per user
This commit is contained in:
parent
7761514c09
commit
271e21d34e
1 changed files with 165 additions and 0 deletions
165
plugins/user/cronjobs
Executable file
165
plugins/user/cronjobs
Executable file
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- python -*-
|
||||
"""
|
||||
=head1 NAME
|
||||
|
||||
Plugin to monitor the number of cronjobs running per user, gathering data from
|
||||
syslog.
|
||||
|
||||
=head1 INSTALLATION
|
||||
|
||||
Usage: Place in /etc/munin/plugins/ (or link it there using ln -s)
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
Add this to your /etc/munin/plugin-conf.d/munin-node:
|
||||
|
||||
=over 2
|
||||
|
||||
[cronjobs]
|
||||
user root
|
||||
env.syslog_path /var/log/syslog # default value
|
||||
env.cron_ident_regex crond? # for finding cron entries in the syslog,
|
||||
case-insensitive (default value)
|
||||
|
||||
=back
|
||||
|
||||
The plugin needs to run as root in order to read from syslog.
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
2019-09-09: v 1.0 pcy <pcy.ulyssis.org>: created
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
Parameters understood:
|
||||
|
||||
config (required)
|
||||
autoconf (optional - used by munin-config)
|
||||
|
||||
=head1 MAGIC MARKERS
|
||||
|
||||
#%# family=auto
|
||||
#%# capabilities=autoconf
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import sys
|
||||
import struct
|
||||
import time
|
||||
|
||||
|
||||
syslog_path = os.getenv('syslog_path', '/var/log/syslog')
|
||||
cron_ident_regex = os.getenv('cron_ident_regex', 'crond?')
|
||||
|
||||
# expected format:
|
||||
# <date> <hostname> CRON[<pid>]: (<user>) CMD ...
|
||||
# example:
|
||||
# Aug 16 22:00:01 zap CRON[23060]: (root) CMD (/usr/local/bin/dnsconfig -s)
|
||||
cron_syslog_regex = re.compile('^.* %s %s\[[0-9]*\]: \((.*?)\)' # noqa: W605
|
||||
% (socket.gethostname(), cron_ident_regex),
|
||||
re.I)
|
||||
|
||||
STATEFILE = os.getenv('MUNIN_STATEFILE')
|
||||
|
||||
|
||||
def loadstate():
|
||||
if not os.path.isfile(STATEFILE):
|
||||
return None
|
||||
|
||||
with open(STATEFILE, 'rb') as f:
|
||||
tstamp = struct.unpack('d', f.read())[0]
|
||||
return datetime.fromtimestamp(tstamp, tz=timezone.utc)
|
||||
|
||||
|
||||
def savestate(state):
|
||||
with open(STATEFILE, 'wb') as f:
|
||||
f.write(struct.pack('d', state.timestamp()))
|
||||
|
||||
|
||||
def extrcronuser(line: str):
|
||||
t = cron_syslog_regex.search(line)
|
||||
if t is None:
|
||||
return None
|
||||
gr = t.groups()
|
||||
return gr[0] if len(gr) == 1 else None
|
||||
|
||||
|
||||
def getcronlines():
|
||||
with open(syslog_path, 'r') as f:
|
||||
for x in f.readlines():
|
||||
user = extrcronuser(x)
|
||||
if user is not None:
|
||||
yield (x, user)
|
||||
|
||||
|
||||
def getcronusers(lines):
|
||||
return set(x[1] for x in lines)
|
||||
|
||||
|
||||
def autoconf():
|
||||
if shutil.which('crontab') is None:
|
||||
print("no (need cron installed)")
|
||||
elif not os.access(syslog_path, os.R_OK):
|
||||
print("no (cannot access syslog file '%s')" % syslog_path)
|
||||
else:
|
||||
print("yes")
|
||||
|
||||
|
||||
def config():
|
||||
usernames = getcronusers(getcronlines())
|
||||
print("""\
|
||||
graph_title Cron jobs per user
|
||||
graph_vlabel jobs
|
||||
graph_category processes""")
|
||||
for n in usernames:
|
||||
print(n + ".label " + n)
|
||||
print(n + ".info jobs of user " + n)
|
||||
|
||||
|
||||
def fetch():
|
||||
STATE = loadstate()
|
||||
# why is there no stdlib function for this?!
|
||||
localtz = timezone(timedelta(seconds=time.localtime().tm_gmtoff))
|
||||
now = datetime.now()
|
||||
now_withtz = datetime.now(tz=localtz)
|
||||
yearsfx = ' ' + str(now.year)
|
||||
pyearsfx = ' ' + str(now.year - 1)
|
||||
cronlines = list(getcronlines())
|
||||
allnames = getcronusers(cronlines)
|
||||
counts = {}
|
||||
|
||||
hostname = socket.gethostname()
|
||||
for ln, name in cronlines:
|
||||
datestr = ln[:ln.index(hostname)].strip()
|
||||
logdate = datetime.strptime(datestr + yearsfx, "%b %d %H:%M:%S %Y")
|
||||
if logdate > now:
|
||||
logdate = datetime.strptime(datestr + pyearsfx, "%b %d %H:%M:%S %Y")
|
||||
# add timezone info (ugly hack), as strptime doesn't want to do this
|
||||
logdate = now_withtz + (logdate - now)
|
||||
if STATE is None or logdate > STATE:
|
||||
counts[name] = (counts[name] + 1) if name in counts else 1
|
||||
|
||||
for n in allnames:
|
||||
if n in counts:
|
||||
print("%s.value %d" % (n, counts[n]))
|
||||
else:
|
||||
print("%s.value 0" % n)
|
||||
|
||||
savestate(now_withtz)
|
||||
|
||||
|
||||
if len(sys.argv) >= 2:
|
||||
if sys.argv[1] == 'autoconf':
|
||||
autoconf()
|
||||
elif sys.argv[1] == 'config':
|
||||
config()
|
||||
else:
|
||||
fetch()
|
||||
else:
|
||||
fetch()
|
Loading…
Add table
Add a link
Reference in a new issue