#!/usr/local/bin/python # # check_rdiff_backup: check liveness of a rdiff-backup repository # Dan R. K. Ports # # Copyright (c) 2007-2008 Dan R. K. Ports # # 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. # # $Revision$ $Date$ import sys, os, re, popen2, optparse, glob, time from datetime import datetime, timedelta from time import strptime # # Nagios helpers # OK = 0 WARN = 1 CRIT = 2 stat = OK def updateStat(x): global stat stat = max(stat,x) msgs = [] # # Options # parser = optparse.OptionParser() parser.add_option("-d", "--dir", dest="path", help="path to rdiff backup repository", metavar="PATH") parser.add_option("-a", "--age", dest="days", type="float", help="acceptable backup age (days)", metavar="DAYS") parser.add_option("-r", "--runtime-warn", dest="runtimeWarn", type="float", help="warning backup runtime (hours)", metavar="HOURS") parser.add_option("-R", "--runtime-crit", dest="runtimeCrit", type="float", help="critical backup runtime (hours)", metavar="HOURS") parser.add_option("-s", "--min-size", dest="minSize", type="float", help="minimum repository size (MB)", metavar="MB") parser.add_option("-c", "--max-change", dest="maxChange", type="float", help="warning changed size (MB)", metavar="MB") (options, args) = parser.parse_args() try: if not os.path.isdir(options.path): raise Exception("invalid repository directory") dataPath = os.path.join(options.path, "rdiff-backup-data") if not os.path.isdir(dataPath): raise Exception("no rdiff-backup-data directory") currentMirrors = glob.glob(os.path.join(dataPath, "current_mirror*")) currentMirrors.sort() if (len(currentMirrors) != 1 and len(currentMirrors) != 2): raise Exception("neither 1 nor 2 current mirrors") # # Check last successful backup # timestamp = os.path.basename(currentMirrors[0]).split(".")[1] # Check freshness age = (time.time() - os.stat(currentMirrors[0]).st_mtime)/86400.0 if (age > options.days): updateStat(CRIT) msgs.append("backup is %f days old" % age) # Check statistics statsFile = file(os.path.join(dataPath, "session_statistics." + timestamp + ".data")) stats = {} for x in statsFile: stats[x.split(" ")[0]] = " ".join(x.split(" ")[1:]) sourceSize = int(stats["SourceFileSize"].split()[0])/1048576.0 if (sourceSize < options.minSize): updateStat(CRIT) msgs.append("source file size dropped below %f MB: %f MB" % (options.minSize, sourceSize)) sizeChange = int(stats["TotalDestinationSizeChange"].split()[0])/1048576.0 if (sizeChange > options.maxChange): updateStat(WARN) msgs.append("transferred more than %f MB: %f MB" % (options.maxChange, sizeChange)) # # Check backup progess # if len(currentMirrors) == 2: msgs.append("backup in progress") timestamp = os.path.basename(currentMirrors[1]).split(".")[1] pid = int(file(currentMirrors[1]).readline().split()[1]) proc = popen2.Popen4("ps -w -o command= -p " + str(pid)) if (proc.wait() != 0): raise Exception("failed to run ps") cmdline = proc.fromchild.readline() if not "rdiff-backup" in cmdline: updateStat(CRIT) msgs.append("backup interrupted!") # Check freshness age = (time.time() - os.stat(currentMirrors[1]).st_mtime)/3600.0 if (age > options.runtimeCrit): updateStat(CRIT) msgs.append("backup has been in progress for %f hours" % age) elif (age > options.runtimeWarn): updateStat(WARN) msgs.append("backup has been in progress for %f hours" % age) except Exception, e: msgs.append(str(e)) updateStat(CRIT) if stat == OK: out = "OK " elif stat == WARN: out = "WARN " else: out = "CRIT " out += "; ".join(msgs) print out sys.exit(stat)