summaryrefslogtreecommitdiff
path: root/nagios-check-rdiff-backup
diff options
context:
space:
mode:
Diffstat (limited to 'nagios-check-rdiff-backup')
-rwxr-xr-xnagios-check-rdiff-backup151
1 files changed, 151 insertions, 0 deletions
diff --git a/nagios-check-rdiff-backup b/nagios-check-rdiff-backup
new file mode 100755
index 0000000..7df30af
--- /dev/null
+++ b/nagios-check-rdiff-backup
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+
+# Copyright 2010, 2011 Peter Palfrader
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import datetime
+import dateutil.parser
+import optparse
+import os
+import re
+import sys
+import glob
+
+
+parser = optparse.OptionParser()
+parser.set_usage("%prog [options] <backuprepository>")
+parser.add_option("-w", "--warn", metavar="AGE", dest="warn",
+ help="Warn if backup older than (default: 28h)")
+parser.add_option("-c", "--critical", metavar="AGE", dest="critical",
+ help="Warn if backup older than (default: 72h)")
+(options, args) = parser.parse_args()
+
+if len(args) == 0:
+ parser.print_help()
+ sys.exit(1)
+
+log = []
+exitcode = 0
+backup_per_code = {}
+
+def code_to_prefix(code):
+ if code == 3: return 'UNKNOWN: '
+ elif code == 2: return 'CRITICAL: '
+ elif code == 1: return 'WARNING: '
+ elif code == 0: return 'OK: '
+ else: raise ValueError
+
+def convert_time(s, default_unit='h'):
+ m = re.match('([0-9]+)([smhdw])?$', s)
+ if m is None: raise ValueError
+ ticks = int(m.group(1))
+ unit = m.group(2)
+ if unit is None: unit = default_unit
+
+ if unit == 's': None
+ elif unit == 'm': ticks *= 60
+ elif unit == 'h': ticks *= 60*60
+ elif unit == 'd': ticks *= 60*60*24
+ elif unit == 'w': ticks *= 60*60*24*7
+ else: raise ValueError
+ return ticks
+
+def record(code, base, msg):
+ global exitcode
+
+ if not code in backup_per_code: backup_per_code[code] = []
+ backup_per_code[code].append(base)
+
+ prefix = code_to_prefix(code)
+ log.append("%s%s: %s"%(prefix, base, msg))
+
+ if code > exitcode: exitcode = code
+
+def get_ts_from_fn(base, fn):
+ m = re.match('current_mirror\.(.*)\.data$', fn)
+ if m is None:
+ record(2, base, "backup wedged - cannot parse current mirrors filename '%s'."%(fn))
+ return None
+ try:
+ ts = dateutil.parser.parse(m.group(1))
+ except ValueError:
+ record(2, base, "backup wedged - cannot parse datestring '%s' from filename '%s'."%(m.group(1), fn))
+ return None
+ return ts
+
+if options.warn is None: options.warn = '28'
+if options.critical is None: options.critical = '72'
+options.warn = datetime.timedelta(seconds = convert_time(options.warn))
+options.critical = datetime.timedelta(seconds = convert_time(options.critical))
+
+
+for base in args:
+ data_dir = os.path.join(base, 'rdiff-backup-data')
+
+ if not os.path.exists(base):
+ record(2, base, "not a directory")
+ continue
+ if not os.path.isdir(data_dir):
+ record(2, base, "does not seem to be an rdiff-backup backup directory")
+ continue
+ os.chdir(data_dir)
+
+ current_mirrors = glob.glob('current_mirror.*.data')
+ if len(current_mirrors) == 0:
+ record(2, base, "no current backups? (current_mirror.* not found)")
+ continue
+ elif len(current_mirrors) == 2: # one backup is currently running, presumably
+ current_mirrors.sort()
+ elif len(current_mirrors) == 1: # only one backup is current, good.
+ None
+ else:
+ record(2, base, "backup wedged - too many current_mirror.* files")
+ continue
+
+ ts = get_ts_from_fn(base, current_mirrors[0])
+ age = datetime.datetime.now(ts.tzinfo) - ts
+ if age > options.critical:
+ record(2, base, "backup is old (%s)."%(age))
+ continue
+ elif age > options.warn:
+ record(1, base, "backup is old (%s)."%(age))
+ continue
+ else:
+ record(0, base, "backup is current (age: %s)."%(age))
+ continue
+
+
+
+
+def record(code, base, msg):
+
+ if not code in backup_per_code: backup_per_code[code] = []
+ backup_per_code[code].append(base)
+
+report = []
+keys = backup_per_code.keys()
+keys.sort(reverse=True)
+for code in keys:
+ prefix = code_to_prefix(code)
+ report.append( prefix + ', '.join(backup_per_code[code]) )
+print '; '.join(report)
+for l in log: print l
+sys.exit(exitcode)