raise check interval
[mirror/dsa-nagios.git] / dsa-nagios-checks / checks / dsa-check-bacula
1 #!/usr/bin/env python
2 #
3 #   check_bacula_client  Nagios plugin to check Bacula client backups
4 #   Copyright (C) 2010  Tom Payne
5 #
6 #   This program is free software: you can redistribute it and/or modify
7 #   it under the terms of the GNU General Public License as published by
8 #   the Free Software Foundation, either version 3 of the License, or
9 #   (at your option) any later version.
10 #
11 #   This program is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15 #
16 #   You should have received a copy of the GNU General Public License
17 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
20 from datetime import datetime, timedelta
21 from optparse import OptionParser, OptionValueError
22 import re
23 import sys
24 import time
25
26 import pexpect
27
28
29 OK, WARNING, CRITICAL, UNKNOWN = xrange(0, 4)
30 status_message = 'OK WARNING CRITICAL UNKNOWN'.split()
31
32 MULTIPLIERS = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400, 'w': 604800}
33 DIVISORS = ((60, 'minutes'), (60, 'hours'), (24, 'days'), (7, 'weeks'))
34
35
36 def parse_period(option, opt_str, value, parser):
37     m = re.match(r'(\d+(?:\.\d+)?)(%s)\Z' % '|'.join(MULTIPLIERS.keys()), value)
38     if not m:
39         raise OptionValueError('invalid period - %s' % value)
40     setattr(parser.values, option.dest, timedelta(seconds=float(m.group(1)) * MULTIPLIERS[m.group(2)]))
41
42
43 def main(argv):
44     parser = OptionParser()
45     parser.add_option('-H', metavar='ADDRESS', dest='host', help='client name')
46     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')
47     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')
48     parser.add_option('-b', metavar='PATH', dest='bconsole', help='path to bconsole')
49     parser.set_defaults(bconsole='/usr/bin/bconsole')
50     options, args = parser.parse_args(argv[1:])
51     exit_status, message = OK, None
52     child = pexpect.spawn(options.bconsole, ['-n'])
53     try:
54         child.expect(r'\n\*')
55         child.sendline('status client=%s.debian.org-fd' % options.host)
56         if child.expect_list([re.compile(r'Terminated Jobs:'), re.compile(r'Error: Client resource .* does not exist.')]):
57             raise RuntimeError('unknown client %s' % options.host)
58         child.expect(r'\n\*')
59         r = re.compile(r'\s*(\d+)\s+(\S+)\s+(\S+)\s+(\d+\.\d+\s+[KMGTP]|0)\s+OK\s+(\S+\s+\S+)')
60         job_id = level = files = bytes = finished = None
61         for line in child.before.splitlines():
62             m = r.match(line)
63             if m:
64                 job_id = int(m.group(1))
65                 level = m.group(2)
66                 files = int(re.sub(r',', '', m.group(3)))
67                 bytes = re.sub(r'\s+', '', m.group(4))
68                 finished = datetime(*(time.strptime(m.group(5), '%d-%b-%y %H:%M')[0:6]))
69         if job_id is None:
70             raise RuntimeError('no terminated jobs')
71         age = datetime.now() - finished
72         if options.warning and age > options.warning:
73             exit_status = WARNING
74         if options.critical and age > options.critical:
75             exit_status = CRITICAL
76         age, units = 24.0 * 60 * 60 * age.days + age.seconds, 'seconds'
77         for d, u in DIVISORS:
78             if age < d:
79                 break
80             else:
81                 age /= d
82                 units = u
83         message = '%s, %d files, %sB, %s (%.1f %s ago)' % (level, files, bytes, finished, age, units)
84     except RuntimeError:
85         exit_status, message = (CRITICAL, str(sys.exc_info()[1]))
86     child.sendeof()
87     child.expect(pexpect.EOF)
88     print '%s: %s' % (status_message[exit_status], message)
89     sys.exit(exit_status)
90
91
92 if __name__ == '__main__':
93     main(sys.argv)