#!/usr/bin/python3
# VERSION: 0.0.1
# DATE: 2018-08-19

import os
import json
import socket
import argparse
import subprocess
from pprint import pprint
from datetime import datetime
from datetime import timedelta

pidFile = "./sauvegarde.pid"
configFile = "./sauvegarde.json"
config = {}

def main():
    global config
    ret = 1
    parser = argparse.ArgumentParser(description='Gestion des sauvegardes.')
    parser.add_argument('-c', '--check', help='Vérification de la sauvegarde', action='store_true')
    parser.add_argument('-b', '--backup', help='Saugegarde', action='store_true')
    parser.add_argument('--repository', help='Destination de la sauvegarde')
    parser.add_argument('--database', help='Gestion de la sauvegarde des bases de données', action='store_true')
    parser.add_argument('--no-database', help='Gestion de la sauvegarde des bases de données', action='store_true')

    args = parser.parse_args()

    pid = str(os.getpid())
    absPidFile = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), pidFile))

    # Check running lock file
    if os.path.isfile(absPidFile):
        running = True
        with open(absPidFile) as pidF:
            for line in pidF:
                if not os.path.isdir('/proc/{}'.format(line)):
                    print("Process is not running anymore")
                    running = False

        if not running:
            os.unlink(absPidFile)

    # Return value if running process
    if os.path.isfile(absPidFile):
        if args.check:
            print("UNKNOWN: Backup or check already running")
            ret = 3
        else:
            print("[ERROR] Process already running")
            ret = 1
        exit(ret)

    with open(absPidFile, 'w') as pidF:
        pidF.write(pid)
    try:

        if args.check:
            loadConfig()
            ret = runCheck()
        elif args.backup:
            print("Run backup")
            loadConfig()
            if config['database']:
                # Run mysql dump
                mysqlDump()
            # Export package list
            dpkgDump()
            # Borg backup
            ret = borgBackup()
            # Borg prune
            borgPrune()
        else:
            if args.repository or args.database or args.no_database:
                loadConfig()

                if args.repository:
                    config['repository'] = args.repository
                if args.database:
                    config['database'] = True
                elif args.no_database:
                    config['database'] = False

                storeConfig()
                print("Check configuration")
                pprint(config)
                ret = 0

    finally:
        os.unlink(absPidFile)
        exit(ret)

def loadConfig():
    global config
    absConfigFile = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), configFile))
    if os.path.exists(absConfigFile):
        with open(absConfigFile) as jsonData:
            config = json.load(jsonData)

def storeConfig():
    absConfigFile = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), configFile))
    with open(absConfigFile, 'w') as jsonData:
        json.dump(config, jsonData, indent=4)

def runCheck():
    os.environ['BORG_RELOCATED_REPO_ACCESS_IS_OK'] = "yes"
    loadConfig()

    now = datetime.today()

    if "lastBackup" in config:
        delta = now - datetime.strptime(config['lastBackup'],"%Y-%m-%dT%H:%M:%S")
        if delta.days < 1:
            print("OK: backup is ok - {}".format(config['lastBackup']))
            return(0)

    cmd = ['borg', 'list', '--last', '1', '--json', '--short', config['repository']]
    # print("Commande : {}".format(" ".join(cmd)))
    res = subprocess.run(cmd, stdout=subprocess.PIPE)
    result = json.loads(res.stdout.decode('utf-8'))

    if len(result['archives']) > 0:
        last = datetime.strptime(result['archives'][0]['time'][:-7],"%Y-%m-%dT%H:%M:%S")
        delta = now - last
        if delta.days > 2:
            print("CRITICAL: last backup is {} days old.".format(delta.days))
            return(3)
        elif delta.days > 1:
            print("WARNING: last backup is {} days old.".format(delta.days))
            return(2)
        else:
            print("OK: last backup is {} hours old.".format(delta.seconds // 3600))
            return(0)

def mysqlDump():
    dumpFile = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "mysql-backup.sql"))
    cmd = ['mysqldump', '--opt', '--all-databases']

    st = os.stat(dumpFile)
    mtime = datetime.fromtimestamp(st.st_mtime)
    now = datetime.today()

    delta = now - mtime
    check = timedelta(hours=12)

    if delta > check:
        print("Commande : {}".format(" ".join(cmd)))
        with open(dumpFile, 'w') as outFile:
            res = subprocess.run(cmd, stdout=outFile)
    else:
        print("Sauvegarde BDD dernière modification il y a => {}".format(delta))

def dpkgDump():
    dumpFile = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "liste-install.txt"))
    cmd = ['dpkg', '--get-selections']

    st = os.stat(dumpFile)
    mtime = datetime.fromtimestamp(st.st_mtime)
    now = datetime.today()

    delta = now - mtime
    check = timedelta(hours=12)

    if delta > check:
        print("Commande : {}".format(" ".join(cmd)))
        with open(dumpFile, 'w') as outFile:
            res = subprocess.run(cmd, stdout=outFile)
    else:
        print("Sauvegarde DPKG dernière modification il y a => {}".format(delta))

def borgBackup():
    os.environ['BORG_RELOCATED_REPO_ACCESS_IS_OK'] = "yes"
    loadConfig()
    hostname = socket.gethostname().split('.', 1)[0]

    now = datetime.today()

    cmd = ['borg', 'create', '-v', '--stats', '--exclude-caches', '--one-file-system', '--json', "{}::{}-{}".format(config['repository'], hostname, now.strftime("%Y-%m-%d")), '/']

    print("Commande : {}".format(" ".join(cmd)))
    res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if res.returncode == 0:
        result = json.loads(res.stdout.decode('utf-8'))
        with open("backup-log-{}.log".format(now.strftime("%Y-%m-%d")), 'w') as jsonData:
            json.dump(result, jsonData, indent=4)
        returnValue = 0
    else:
        result = res.stderr.decode('utf-8')
        print(result)
        if "Archive {}-{} already exists".format(hostname, now.strftime("%Y-%m-%d")) in result:
            print("OK archive exists")
            returnValue = 0
        else:
            returnValue = res.returncode

    return(returnValue)

def borgPrune():
    os.environ['BORG_RELOCATED_REPO_ACCESS_IS_OK'] = "yes"
    loadConfig()
    hostname = socket.gethostname().split('.', 1)[0]

    cmd = ['borg', 'prune', '-v', '--prefix', "{}-".format(hostname), '--keep-daily=7', '--keep-weekly=4', '--keep-monthly=6', config['repository']]

    print("Commande : {}".format(" ".join(cmd)))
    res = subprocess.run(cmd, stdout=subprocess.PIPE)

    return(res.returncode)

if __name__ == "__main__":
    main()
