#!/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)