import os import sched import time import sys from termcolor import colored, cprint import getpass import json import datetime import uuid import logging import logging.handlers import multiprocessing syslog = True debug = False config_path = '/etc/python-backup.json' Config = None ############### # Helpers # ############### def log(message): if debug: cprint('[DEBUG]: %s' % message, 'yellow') def error(message): cprint('[ERROR]: %s' % message, 'white', 'on_red') def printHelp(): cprint( 'Please run backup.py like so:\n~$ python3 ./backup.py\t\t\tRun with default configuration file (/etc/python-backup.json)\n\ or\n~$ python3 ./backup.py {parameter}\n\ -h (--help)\t\t\tShow help\n\ -v (--version)\t\t\tShow script version\n\ -d (--debug)\t\t\tShow debug messages\n\ -c (--config) [PATH]\t\tConfiguration file path\n\ --single\t\t\tRun single backup - otherwise run in daemon mode\n', 'cyan') def printVersion(): cprint('Python-backup version 1.0\nBy Dominik Dancs | email> do@dancs.sk | web> https://do.dancs.sk', 'cyan') exit(0) def loadConfig(): global config_path, Config, syslog try: Config = json.load(open(config_path)) try: Config['syslog'] if isinstance(Config['syslog'], bool) and not Config['syslog']: syslog = False Config['unit-name'] Config['to-backup'] if not isinstance(Config['to-backup'], list): raise Exception('Paths to backup must be a list!') for location in Config['to-backup']: location['path'] location['name'] Config['compression-level'] if not isinstance(Config['compression-level'], int) or Config['compression-level'] < 0 or Config['compression-level'] > 9: raise Exception( 'Compression level must be a number between 0 - 9!') Config['cpu-threads'] if not isinstance(Config['cpu-threads'], int): raise Exception('Number of CPU threads must be a number!') if Config['cpu-threads'] < 1 or Config['cpu-threads'] > multiprocessing.cpu_count(): raise Exception('Invalid number of CPU threads!') Config['backup-locations'] if not isinstance(Config['backup-locations'], list): raise Exception('Backup locations must be a priority list!') for location in Config['backup-locations']: location['path'] location['priority'] if not isinstance(location['priority'], int) or location['priority'] < 0 or location['priority'] > 999: raise Exception( 'Location priority must be a number from 0 - 999') except Exception as e: if e == 'syslog': error('Please specify unit name!') if e == 'unit-name': error('Please specify unit name!') elif e == 'to-backup': error('Please specify paths to backup!') elif e == 'compression-level': error('Please specify compression level (0-9)!') elif e == 'cpu-threads': error('Please specify number of available CPU threads!') elif e == 'backup-locations': error('Please specify backup locations!') else: error('Config file does not follow standards!') log(e) exit(1) except Exception as e: error('Unable to read config file!') exit(1) ######################## # Argument parsing # ######################## # not enough parameters supplied - show help if len(sys.argv) > 0: i = 1 while i < len(sys.argv): arg = sys.argv[i] i += 1 if arg == '-h' or arg == '--help': printHelp() exit(0) if arg == '-v' or arg == '--version': printVersion() exit(0) if arg == '-d' or arg == '--debug': debug = True elif arg == '-c' or arg == '--config': try: config_path = sys.argv[i] i += 1 except: error('No config file provided!') printHelp() exit(1) else: error('Unrecognized argument') printHelp() exit(1) loadConfig() ######################## # Setup syslog # ######################## if syslog: logger = logging.getLogger('python-backup') handler = logging.handlers.SysLogHandler(address='/dev/log') handler.setFormatter(logging.Formatter( '%(name)s: [%(levelname)s] %(message)s' )) logger.addHandler(handler)