#!/usr/bin/python3

'''
Check for backuppc archive age.

Usage: check_backuppc -w 28h -c 9d
       warning after 28 hours, critical after 9 days

pc/$host/backups file format:

backup_id type(full|incr) start_time end_time
total_files total_bytes existing_files existing_bytes new_files new_bytes
xfer_errors bad_files bad_share tar_errs
comp_level comp_bytes ? ? ? method(rsync) ? UTF-8 version(3.1.0)
'''

import os, time, sys, getopt

backup_dir = os.path.expanduser("~backuppc/pc")
default_warn_time = '28h' # 28 hours
default_crit_time = '9d' # 9 days

def convert_time_suffix(s):
    suffixes = dict(
      s = 1,
      m = 60,
      h = 3600,
      d = 3600*24,
      w = 3600*31
    )
    for suffix, multiply in suffixes.items():
      if s.endswith(suffix):
        return int(s[:-1])*multiply
    return int(s)

class log_entry(dict):
  def __init__(self, id, type, start, end, total_files, total_bytes,
               exist_files, exist_bytes, new_files, new_bytes,
               xfer_errors, bad_files, bad_share, tar_errs, *rest):
      dict.__init__(self,
        id = int(id),
        type = type,
        start = int(start),
        end = int(end or 0),
        total_files = int(total_files or 0),
        total_bytes = int(total_bytes or 0),
        exist_files = int(exist_files or 0),
        exist_bytes = int(exist_bytes or 0),
        new_files = int(new_files or 0),
        new_bytes = int(new_bytes or 0),
        xfer_errors = int(xfer_errors or 0),
        bad_files = int(bad_files or 0),
        bad_share = int(bad_share or 0),
        tar_errs = int(tar_errs or 0),
        rest = rest
      )
  def __getattr__(self, key):
      return self[key]
  def age(self):
      return time.time() - self['start']
  def age_str(self):
      return "%4.2fd" % (self.age()/24.0/3600.0)

class backups_log:
  def __init__(self, filename):
      self.f = open(filename, "rt")
  def readlines(self):
      while True:
        line = self.f.readline()
        if not line:
          break
        yield log_entry(*line.strip().split("\t"))
  def last(self):
      return list(self.readlines())[-1]

def host2str(hosts):
    return ', '.join([
      "%s(%s)" % (k, v.age_str())
      for k, v in hosts.items()
    ])

if __name__ == "__main__":
  try:
    opts, files = getopt.gnu_getopt(sys.argv[1:], 'hw:c:B:',
      [])
  except getopt.GetoptError as err:
    print("Error:", str(err))
    sys.exit(11)
  if files:
    print("Unknown parameters:", files.join(' '))
    sys.exit(12)
  opts = dict(opts)
  warn_age = convert_time_suffix(opts.get('-w', default_warn_time))
  crit_age = convert_time_suffix(opts.get('-c', default_crit_time))
  backup_dir = opts.get('-B', backup_dir)
  warn_hosts = {}
  crit_hosts = {}
  all_hosts = {}
  # check hosts
  try:
    dirs = os.listdir(backup_dir)
  except OSError as err:
    # permission denied?
    print("CRITICAL - OSError: %s" % err.args[1])
    sys.exit(2)
  for host in dirs:
    try:
      log = backups_log(os.path.join(backup_dir, host, 'backups'))
    except IOError:
      continue
    last = log.last()
    all_hosts[host] = last
    if last.age()>=crit_age:
      crit_hosts[host] = last
    elif last.age()>=warn_age:
      warn_hosts[host] = last
  # return status
  d = dict([
    (k, v.age()/24/3600)
    for k, v in all_hosts.items()
  ])
  if not all_hosts:
    print("CRITICAL - no hosts found")
    sys.exit(2)
  if crit_hosts:
    print("CRITICAL on host: %s, warning on hosts: %s"
          % (host2str(crit_hosts), host2str(warn_hosts)))
    sys.exit(2)
  elif warn_hosts:
    print("WARNING on host: %s" % host2str(warn_hosts))
    sys.exit(1)
  print("OK for hosts: %s" % host2str(all_hosts))
