diff --git a/configsync.py b/cfgs.py similarity index 81% rename from configsync.py rename to cfgs.py index b4dd81c..3ca43dc 100644 --- a/configsync.py +++ b/cfgs.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + import os import sys import subprocess @@ -10,8 +12,14 @@ import stat import pwd import grp + CONFIG_SYNC_VERSION = "1.0.0" -CONFIG_SYNC_FILE = "/etc/configsync.conf" +CONFIG_SYNC_FILE = "/etc/cfgs.conf" + + +# ----------------------------------------- +# color defintions +# ----------------------------------------- class bcolors: HEADER = '\033[95m' @@ -23,6 +31,11 @@ class bcolors: BOLD = '\033[1m' UNDERLINE = '\033[4m' + +# ----------------------------------------- +# UI helper functions +# ----------------------------------------- + def check_dependencies(): status = True try: @@ -34,59 +47,27 @@ def check_dependencies(): return status + def print_splash(): - print(bcolors.HEADER + "---=== CONFIG-SYNC ===---" + bcolors.ENDC) + print(bcolors.HEADER + "---=== cfgs ===---" + bcolors.ENDC) print("Version: " + CONFIG_SYNC_VERSION) print() print(bcolors.HEADER + "---=== USAGE ===---" + bcolors.ENDC) - print("configsync init") - print("configsync add ") - print("configsync remove ") - print("configsync store") - print("configsync restore") + print("cfgs init") + print("cfgs add ") + print("cfgs remove ") + print("cfgs store") + print("cfgs restore") sys.exit(1) -def validate_or_create_config(config): - if not os.path.exists(CONFIG_SYNC_FILE): - print(bcolors.OKBLUE + "Creating default configuration file: " + CONFIG_SYNC_FILE + bcolors.ENDC) - config['DEFAULT'] = {} - config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] = "/opt/configsync/storage" - config['DEFAULT']['INITIALIZED'] = "" - config['GIT'] = {} - config['GIT']['REMOTE_REPOSITORY'] = "" - config['GIT']['USER'] = "configsync" - config['GIT']['EMAIL'] = "configsync@" + socket.gethostname() - config['GIT']['SSHKEY'] = str(Path.home()) + "/.ssh/id_rsa" - - update_config(config) - else: - config.read(CONFIG_SYNC_FILE) - - return config - -def get_config(): - config = configparser.ConfigParser() - config = validate_or_create_config(config) - - return config - -def update_config(config): - try: - config.write(open(CONFIG_SYNC_FILE, 'w')) - except PermissionError as e: - print(bcolors.FAIL + "Unable to write config file! Error: " + e.strerror + bcolors.ENDC) - sys.exit(1) - except Exception as e: - print(bcolors.FAIL + "Unable to write config file! Error: " + str(e) + bcolors.ENDC) - sys.exit(1) def init_dialog(): config = get_config() - print(bcolors.HEADER + "---=== Welcome to CONFIG-SYNC! ===---" + bcolors.ENDC) + print(bcolors.HEADER + "---=== Welcome to cfgs! ===---" + bcolors.ENDC) print() if config['DEFAULT']['INITIALIZED']: - reinitialize = input(bcolors.WARNING + "WARNING: CONFIG-SYNC has already been initialized! Do you really want to change the configuration? [y/N]: " + bcolors.ENDC) + reinitialize = input(bcolors.WARNING + "WARNING: cfgs has already been initialized! Do you really want to change the configuration? [y/N]: " + bcolors.ENDC) if not reinitialize or reinitialize != "y": print("Skipping new initialization!") sys.exit(0) @@ -120,10 +101,52 @@ def init_dialog(): update_config(config) + +# ----------------------------------------- +# local config helper functions +# ----------------------------------------- + +def validate_or_create_config(config): + if not os.path.exists(CONFIG_SYNC_FILE): + print(bcolors.OKBLUE + "Creating default configuration file: " + CONFIG_SYNC_FILE + bcolors.ENDC) + config['DEFAULT'] = {} + config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] = "/opt/cfgs/storage" + config['DEFAULT']['INITIALIZED'] = "" + config['GIT'] = {} + config['GIT']['REMOTE_REPOSITORY'] = "" + config['GIT']['USER'] = "cfgs" + config['GIT']['EMAIL'] = "cfgs@" + socket.gethostname() + config['GIT']['SSHKEY'] = str(Path.home()) + "/.ssh/id_rsa" + + update_config(config) + else: + config.read(CONFIG_SYNC_FILE) + + return config + + +def get_config(): + config = configparser.ConfigParser() + config = validate_or_create_config(config) + + return config + + +def update_config(config): + try: + config.write(open(CONFIG_SYNC_FILE, 'w')) + except PermissionError as e: + print(bcolors.FAIL + "Unable to write config file! Error: " + e.strerror + bcolors.ENDC) + sys.exit(1) + except Exception as e: + print(bcolors.FAIL + "Unable to write config file! Error: " + str(e) + bcolors.ENDC) + sys.exit(1) + + def create_initialization_file(path): try: - with open(path + "/configsync.info", mode='w') as file: - file.write('CONFIG-SYNC Repo initialized: %s.\n' % ( datetime.datetime.now())) + with open(path + "/cfgs.info", mode='w') as file: + file.write('cfgs repo initialized: %s. host: %s\n' % ( datetime.datetime.now(), socket.gethostname() )) except PermissionError as e: print(bcolors.FAIL + "Unable to write initialization file! Error: " + e.strerror + bcolors.ENDC) sys.exit(1) @@ -132,7 +155,7 @@ def create_initialization_file(path): sys.exit(1) try: - open(path + "/configsync.db", mode='a').close() + open(path + "/cfgs.db", mode='a').close() except PermissionError as e: print(bcolors.FAIL + "Unable to write database file! Error: " + e.strerror + bcolors.ENDC) sys.exit(1) @@ -140,17 +163,22 @@ def create_initialization_file(path): print(bcolors.FAIL + "Unable to write database file! Error: " + str(e) + bcolors.ENDC) sys.exit(1) + +# ----------------------------------------- +# local database helper functions +# ----------------------------------------- + def add_file_to_db(file_path): config = get_config() try: insert = True - with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/configsync.db", "r+") as search: + with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/cfgs.db", "r+") as search: for line in search: line = line.rstrip() # remove '\n' at end of line if file_path == line: insert = False if insert: - with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/configsync.db", mode='a') as file: + with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/cfgs.db", mode='a') as file: file.write('%s\n' % (file_path)) except PermissionError as e: print(bcolors.FAIL + "Unable to write db file! Error: " + e.strerror + bcolors.ENDC) @@ -159,10 +187,11 @@ def add_file_to_db(file_path): print(bcolors.FAIL + "Unable to write db file! Error: " + str(e) + bcolors.ENDC) sys.exit(1) + def delete_file_from_db(file_path): config = get_config() try: - with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/configsync.db", "r+") as search: + with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/cfgs.db", "r+") as search: lines = search.readlines() search.seek(0) for line in lines: @@ -176,20 +205,28 @@ def delete_file_from_db(file_path): print(bcolors.FAIL + "Unable to write db file! Error: " + str(e) + bcolors.ENDC) sys.exit(1) -def setup_git_ssh_environment(): + +# ----------------------------------------- +# git helper functions +# ----------------------------------------- + +def git_setup_ssh_environment(): config = get_config() os.environ['GIT_SSH_COMMAND'] = "ssh -i " + config['GIT']['SSHKEY'] + def git_is_repo(path): return subprocess.call(["git", "branch"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, cwd=path) == 0 + def git_set_author(path, user, email): status = subprocess.call(["git", "config", "user.name", user], stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL, cwd=path) == 0 status |= subprocess.call(["git", "config", "user.email", email], stderr=subprocess.STDOUT, stdout=subprocess.DEVNULL, cwd=path) == 0 return status + def git_set_upstream(path, remote_url): # make sure that remote origin does not exist status = subprocess.call(["git", "remote", "rm", "origin"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, cwd=path) == 0 @@ -198,11 +235,13 @@ def git_set_upstream(path, remote_url): return status + def git_init_repo(path): status = subprocess.call(["git", "init"], cwd=path) == 0 return status + def git_push(path, message = "configuration update"): status = subprocess.call(["git", "add", "-A"], cwd=path) == 0 status |= subprocess.call(["git", "commit", "-a", "-m", message], cwd=path) == 0 @@ -210,11 +249,13 @@ def git_push(path, message = "configuration update"): return status + def git_pull(path): status = subprocess.call(["git", "pull", "origin", "master"], cwd=path) == 0 return status + def git_check_status(status): config = get_config() if not status: @@ -222,6 +263,11 @@ def git_check_status(status): print(bcolors.FAIL + "FATAL: Repository path: " + config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + bcolors.ENDC) sys.exit(1) + +# ----------------------------------------- +# main business logic +# ----------------------------------------- + def init_local_repo(): config = get_config() @@ -241,15 +287,16 @@ def init_local_repo(): git_check_status(git_set_author(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'], config['GIT']['USER'], config['GIT']['EMAIL'])) git_check_status(git_set_upstream(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'], config['GIT']['REMOTE_REPOSITORY'])) - setup_git_ssh_environment() + git_setup_ssh_environment() git_check_status(git_pull(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'])) create_initialization_file(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY']) - git_check_status(git_push(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'], "configsync initialized")) + git_check_status(git_push(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'], "cfgs initialized")) git_check_status(git_pull(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'])) - print(bcolors.OKGREEN + "CONFIG-SYNC initialization successfull!" + bcolors.ENDC) - print(bcolors.OKBLUE + "You can now use CONFIG-SYNC to keep your config files up to date." + bcolors.ENDC) + print(bcolors.OKGREEN + "cfgs initialization successfull!" + bcolors.ENDC) + print(bcolors.OKBLUE + "You can now use cfgs to keep your config files up to date." + bcolors.ENDC) + def add_file(filepath): config = get_config() @@ -259,12 +306,12 @@ def add_file(filepath): print(bcolors.WARNING + "Invalid file, skipping!" + bcolors.ENDC) sys.exit(1) else: - print(bcolors.OKBLUE + "Adding '" + abs_path + "' to CONFIG-SYNC..." + bcolors.ENDC) + print(bcolors.OKBLUE + "Adding '" + abs_path + "' to cfgs..." + bcolors.ENDC) local_path = config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + abs_path if os.path.exists(local_path): - print(bcolors.WARNING + "File is already registered to CONFIG-SYNC, skipping!" + bcolors.ENDC) + print(bcolors.WARNING + "File is already registered to cfgs, skipping!" + bcolors.ENDC) sys.exit(1) else: target_directory = os.path.abspath(os.path.join(local_path, os.pardir)) @@ -279,8 +326,8 @@ def add_file(filepath): file_group_name = grp.getgrgid(file_group).gr_name try: - with open(local_path + ".cfsinfo", mode='w') as file: - file.write('%s;%s;%s;%s;%s' % (file_permissions, file_owner, file_group, file_owner_name, file_group_name)) + with open(local_path + ".cfgs", mode='w') as file: + file.write('%s;%s;%s;%s;%s;%s' % ( file_permissions, file_owner, file_group, file_owner_name, file_group_name, socket.gethostname() )) except PermissionError as e: print(bcolors.FAIL + "Unable to write stat file! Error: " + e.strerror + bcolors.ENDC) sys.exit(1) @@ -296,8 +343,9 @@ def add_file(filepath): add_file_to_db(abs_path) - print(bcolors.OKGREEN + "File added to CONFIG-SYNC!" + bcolors.ENDC) - print(bcolors.OKBLUE + "Use configsync store to push the file to the remote repository!" + bcolors.ENDC) + print(bcolors.OKGREEN + "File added to cfgs!" + bcolors.ENDC) + print(bcolors.OKBLUE + "Use cfgs store to push the file to the remote repository!" + bcolors.ENDC) + def remove_file(filepath): config = get_config() @@ -306,10 +354,10 @@ def remove_file(filepath): local_path = config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + abs_path if not os.path.exists(local_path): - print(bcolors.WARNING + "File not registered to CONFIG-SYNC, skipping!" + bcolors.ENDC) + print(bcolors.WARNING + "File not registered to cfgs, skipping!" + bcolors.ENDC) sys.exit(1) else: - print(bcolors.OKBLUE + "Removing '" + abs_path + "' from CONFIG-SYNC..." + bcolors.ENDC) + print(bcolors.OKBLUE + "Removing '" + abs_path + "' from cfgs..." + bcolors.ENDC) target_directory = os.path.abspath(os.path.join(abs_path, os.pardir)) if not os.path.exists(target_directory): @@ -333,25 +381,26 @@ def remove_file(filepath): os.chown(abs_path, file_owner, file_group) os.chmod(abs_path, file_permissions) os.remove(local_path) - os.remove(local_path + ".cfsinfo") + os.remove(local_path + ".cfgs") delete_file_from_db(abs_path) - print(bcolors.OKGREEN + "File removed from CONFIG-SYNC!" + bcolors.ENDC) - print(bcolors.OKBLUE + "Use configsync store to push the file to the remote repository!" + bcolors.ENDC) - + print(bcolors.OKGREEN + "File removed from cfgs!" + bcolors.ENDC) + print(bcolors.OKBLUE + "Use cfgs store to push the file to the remote repository!" + bcolors.ENDC) + + def store(): config = get_config() - setup_git_ssh_environment() + git_setup_ssh_environment() git_push(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY']) - print(bcolors.OKGREEN + "CONFIG-SYNC store successfull!" + bcolors.ENDC) + print(bcolors.OKGREEN + "cfgs store successfull!" + bcolors.ENDC) print(bcolors.OKBLUE + "You can now restore the configuration on your other systems." + bcolors.ENDC) def update_local_metadata(): config = get_config() - with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/configsync.db", "r") as search: + with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/cfgs.db", "r") as search: for line in search: abs_path = line.rstrip() # remove '\n' at end of line local_path = config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + abs_path @@ -359,7 +408,7 @@ def update_local_metadata(): if not os.path.exists(local_path): print(bcolors.WARNING + "Invalid file in database, skipping! (" + abs_path + ")" + bcolors.ENDC) else: - with open(local_path + ".cfsinfo", "r") as metadata_file: + with open(local_path + ".cfgs", "r") as metadata_file: metadata_string = metadata_file.readline().rstrip() metadata = metadata_string.split(";") @@ -369,10 +418,11 @@ def update_local_metadata(): os.chmod(local_path, file_permissions) os.chown(local_path, file_owner, file_group) - + + def restore(): config = get_config() - setup_git_ssh_environment() + git_setup_ssh_environment() git_pull(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY']) update_local_metadata() @@ -382,7 +432,7 @@ def restore(): print(bcolors.WARNING + "Skipping restore process!" + bcolors.ENDC) sys.exit(0) else: - with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/configsync.db", "r") as search: + with open(config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + "/cfgs.db", "r") as search: for line in search: abs_path = line.rstrip() # remove '\n' at end of line local_path = config['DEFAULT']['LOCAL_STORAGE_DIRECTORY'] + abs_path @@ -405,9 +455,14 @@ def restore(): os.chown(abs_path, file_owner, file_group, follow_symlinks=False) - print(bcolors.OKGREEN + "CONFIG-SYNC restore successfull!" + bcolors.ENDC) + print(bcolors.OKGREEN + "cfgs restore successfull!" + bcolors.ENDC) print(bcolors.OKBLUE + "Configuration files have been updated. A reboot might be required." + bcolors.ENDC) + +# ----------------------------------------- +# main entry point +# ----------------------------------------- + def main(argv): if not check_dependencies(): sys.exit(1) @@ -437,5 +492,6 @@ def main(argv): else: print_splash() + if __name__ == "__main__": main(sys.argv) \ No newline at end of file