add dsa-check-bacula
authorMartin Zobel-Helas <zobel@debian.org>
Fri, 8 Feb 2013 16:33:26 +0000 (17:33 +0100)
committerMartin Zobel-Helas <zobel@debian.org>
Fri, 8 Feb 2013 16:33:26 +0000 (17:33 +0100)
Signed-off-by: Martin Zobel-Helas <zobel@debian.org>
dsa-nagios-checks/dsa-check-bacula [new file with mode: 0755]

diff --git a/dsa-nagios-checks/dsa-check-bacula b/dsa-nagios-checks/dsa-check-bacula
new file mode 100755 (executable)
index 0000000..69988fe
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+#
+#   check_bacula_client  Nagios plugin to check Bacula client backups
+#   Copyright (C) 2010  Tom Payne
+#
+#   This program is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from datetime import datetime, timedelta
+from optparse import OptionParser, OptionValueError
+import re
+import sys
+import time
+
+import pexpect
+
+
+OK, WARNING, CRITICAL, UNKNOWN = xrange(0, 4)
+status_message = 'OK WARNING CRITICAL UNKNOWN'.split()
+
+MULTIPLIERS = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400, 'w': 604800}
+DIVISORS = ((60, 'minutes'), (60, 'hours'), (24, 'days'), (7, 'weeks'))
+
+
+def parse_period(option, opt_str, value, parser):
+    m = re.match(r'(\d+(?:\.\d+)?)(%s)\Z' % '|'.join(MULTIPLIERS.keys()), value)
+    if not m:
+        raise OptionValueError('invalid period - %s' % value)
+    setattr(parser.values, option.dest, timedelta(seconds=float(m.group(1)) * MULTIPLIERS[m.group(2)]))
+
+
+def main(argv):
+    parser = OptionParser()
+    parser.add_option('-H', metavar='ADDRESS', dest='host', help='client name')
+    parser.add_option('-w', metavar='PERIOD', type=str, dest='warning', action='callback', callback=parse_period, help='generate warning if last successful backup older than PERIOD')
+    parser.add_option('-c', metavar='PERIOD', type=str, dest='critical', action='callback', callback=parse_period, help='generate critical if last successful backup older than PERIOD')
+    parser.add_option('-b', metavar='PATH', dest='bconsole', help='path to bconsole')
+    parser.set_defaults(bconsole='/usr/bin/bconsole')
+    options, args = parser.parse_args(argv[1:])
+    exit_status, message = OK, None
+    child = pexpect.spawn(options.bconsole, ['-n'])
+    try:
+        child.expect(r'\n\*')
+        child.sendline('status client=%s.debian.org-fd' % options.host)
+        if child.expect_list([re.compile(r'Terminated Jobs:'), re.compile(r'Error: Client resource .* does not exist.')]):
+            raise RuntimeError('unknown client %s' % options.host)
+        child.expect(r'\n\*')
+        r = re.compile(r'\s*(\d+)\s+(\S+)\s+(\S+)\s+(\d+\.\d+\s+[KMGTP]|0)\s+OK\s+(\S+\s+\S+)')
+        job_id = level = files = bytes = finished = None
+        for line in child.before.splitlines():
+            m = r.match(line)
+            if m:
+                job_id = int(m.group(1))
+                level = m.group(2)
+                files = int(re.sub(r',', '', m.group(3)))
+                bytes = re.sub(r'\s+', '', m.group(4))
+                finished = datetime(*(time.strptime(m.group(5), '%d-%b-%y %H:%M')[0:6]))
+        if job_id is None:
+            raise RuntimeError('no terminated jobs')
+        age = datetime.now() - finished
+        if options.warning and age > options.warning:
+            exit_status = WARNING
+        if options.critical and age > options.critical:
+            exit_status = CRITICAL
+        age, units = 24.0 * 60 * 60 * age.days + age.seconds, 'seconds'
+        for d, u in DIVISORS:
+            if age < d:
+                break
+            else:
+                age /= d
+                units = u
+        message = '%s, %d files, %sB, %s (%.1f %s ago)' % (level, files, bytes, finished, age, units)
+    except RuntimeError:
+        exit_status, message = (CRITICAL, str(sys.exc_info()[1]))
+    child.sendeof()
+    child.expect(pexpect.EOF)
+    print '%s: %s' % (status_message[exit_status], message)
+    sys.exit(exit_status)
+
+
+if __name__ == '__main__':
+    main(sys.argv)