3 # check_bacula_client Nagios plugin to check Bacula client backups
4 # Copyright (C) 2010 Tom Payne
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.
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.
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/>.
20 from datetime import datetime, timedelta
21 from optparse import OptionParser, OptionValueError
29 OK, WARNING, CRITICAL, UNKNOWN = xrange(0, 4)
30 status_message = 'OK WARNING CRITICAL UNKNOWN'.split()
32 MULTIPLIERS = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400, 'w': 604800}
33 DIVISORS = ((60, 'minutes'), (60, 'hours'), (24, 'days'), (7, 'weeks'))
36 def parse_period(option, opt_str, value, parser):
37 m = re.match(r'(\d+(?:\.\d+)?)(%s)\Z' % '|'.join(MULTIPLIERS.keys()), value)
39 raise OptionValueError('invalid period - %s' % value)
40 setattr(parser.values, option.dest, timedelta(seconds=float(m.group(1)) * MULTIPLIERS[m.group(2)]))
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'])
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)
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():
64 job_id = int(m.group(1))
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]))
70 raise RuntimeError('no terminated jobs')
71 age = datetime.now() - finished
72 if options.warning and age > options.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'
83 message = '%s, %d files, %sB, %s (%.1f %s ago)' % (level, files, bytes, finished, age, units)
85 exit_status, message = (CRITICAL, str(sys.exc_info()[1]))
87 child.expect(pexpect.EOF)
88 print '%s: %s' % (status_message[exit_status], message)
92 if __name__ == '__main__':