From 3bcf77e0b8b344636449769fbddcd8b6cdf2a07c Mon Sep 17 00:00:00 2001 From: Marc Michalsky Date: Thu, 3 Dec 2020 21:46:00 +0100 Subject: [PATCH] implement restore --- models.py | 33 ++++++++++++++++++++++----------- restore.py | 36 +++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/models.py b/models.py index a7cb892..8f3c9d6 100644 --- a/models.py +++ b/models.py @@ -30,8 +30,10 @@ class Container: self.exceptions = {} self.SUCCESS = F"{Fore.GREEN}success{Style.RESET_ALL}" self.FAILED = F"{Fore.RED}failed{Style.RESET_ALL}" - - global quiet_mode + self.__restore_dump_file = "" + self.__restore_dump_file_path = "" + self.__restore_tar_file_path = "" + self.__restore_tar_file = "" # Create backup dir if it does not yet exist def __create_backup_dir(self): @@ -88,7 +90,8 @@ class Container: def __import_db(self) -> bool: try: os.system( - F"docker exec {self.db_container} mysql -u root --password={self.__password} < {self.__dump_file_path}") + F"docker exec -i {self.db_container} mysql -u root --password={self.__password} " + F"< {self.__restore_dump_file_path}") status = True # TODO: Implement test if database import was successful _print(F"Import Nextcloud database: {self.SUCCESS if status else self.FAILED}") return status @@ -109,6 +112,8 @@ class Container: status = os.path.isdir(os.path.join(self.tmp_dir, "config")) _print(F"Export Nextcloud configuration: {self.SUCCESS if status else self.FAILED}") return status + else: + _print(F"Export Nextcloud configuration: {self.FAILED}") except: _print(F"Export Nextcloud configuration: {self.FAILED}") self.exceptions.update({'__export_config': traceback.format_exc()}) @@ -118,11 +123,12 @@ class Container: def __import_config(self) -> bool: try: if os.path.isdir(os.path.join(self.tmp_dir, "config")): - with tarfile.open(self.__tar_file_path, 'w') as tarball: + with tarfile.open(self.__restore_tar_file_path, 'w') as tarball: tarball.add(os.path.join(self.tmp_dir, "config"), arcname="/config") - os.system(F"docker cp {self.__tar_file_path} {self.app_container}:/var/www/html/config.tar") + os.system(F"docker cp {self.__restore_tar_file_path} {self.app_container}:/var/www/html/config.tar") os.system(F"docker exec {self.app_container} rm -r config") os.system(F"docker exec {self.app_container} tar -xf config.tar") + os.system(F"docker exec {self.app_container} rm config.tar") status = True # TODO: implement a test if export into docker container was successful _print(F"Import Nextcloud configuration: {self.SUCCESS if status else self.FAILED}") return status @@ -142,7 +148,7 @@ class Container: return status except: _print(F"Zip backup: {self.FAILED}") - self.exceptions.update({'__tar_db': traceback.format_exc()}) + self.exceptions.update({'__tar_backup': traceback.format_exc()}) return False # Untar backup @@ -150,12 +156,12 @@ class Container: try: with tarfile.open(backup_file_path, 'r:gz') as tarball: tarball.extractall(self.tmp_dir) - status = os.path.isdir(os.path.join(self.tmp_dir, "config")) and os.path.isfile(self.__dump_file_path) + status = os.path.isdir(os.path.join(self.tmp_dir, "config")) _print(F"Unzip backup: {self.SUCCESS if status else self.FAILED}") return status except: _print(F"Unzip backup: {self.FAILED}") - self.exceptions.update({'__untar_db': traceback.format_exc()}) + self.exceptions.update({'__untar_backup': traceback.format_exc()}) return False # Set secure file permissions @@ -194,13 +200,18 @@ class Container: def restore_backup(self, backup_file_path): + self.__restore_dump_file = os.path.basename(backup_file_path[:-6] + "sql") + self.__restore_dump_file_path = os.path.join(self.tmp_dir, self.__restore_dump_file) + self.__restore_tar_file = os.path.basename(backup_file_path[:-3]) + self.__restore_tar_file_path = os.path.join(self.tmp_dir, self.__restore_tar_file) + if self.__create_tmp_dir(): try: step_status = [ self.__untar_backup(backup_file_path), - #self.__import_config(), - #self.__import_db(), - #self.__delete_tmp_dir() + self.__import_config(), + self.__import_db(), + self.__delete_tmp_dir() ] for step in step_status: if not step: diff --git a/restore.py b/restore.py index 09eb955..80b5418 100644 --- a/restore.py +++ b/restore.py @@ -24,6 +24,7 @@ def restore(): utils.all_containers = "--all" in sys.argv utils.quiet_mode = "--quiet" in sys.argv utils.no_log = "--nolog" in sys.argv + utils.no_confirm = "--yes" in sys.argv # Load settings settings = Path(__file__).parent / "settings.yaml" @@ -33,7 +34,7 @@ def restore(): log = Log(settings_list['log']['log_dir']) containers = Container.instantiate_containers(settings_list) - # If any container names where passed as parameters, do only restore them + # If any container names were passed as parameters, do only restore them containers_wanted = {name: container for name, container in containers.items() if name in sys.argv} if containers_wanted: containers = containers_wanted @@ -46,9 +47,10 @@ def restore(): containers = {containers_to_choose_from[choice_index]: containers.get(containers_to_choose_from[choice_index])} container: Container + results = {} for container in containers.values(): - # Start backup restore + # Start restore _print("----------------------------------------------") _print(F"Restore backup for {container.name}") @@ -61,18 +63,42 @@ def restore(): backup_files_to_choose_from = [file.name for file in backup_files.values()] backup_files_to_choose_from.sort(reverse=True) _print() + + # Choose backup to restore from terminal_menu = TerminalMenu(backup_files_to_choose_from, title="Which backup do you want to restore?") choice_index = terminal_menu.show() backup_file = backup_files.get(backup_files_to_choose_from[choice_index]) print(backup_file.path) - confirm = input(F"Are you sure that you want to restore {backup_files_to_choose_from[choice_index]}? " - F"(Type: yes)\n").lower() == "yes" - if confirm: + # Confirm restore + if not utils.no_confirm: + confirm = input(F"Are you sure that you want to restore {backup_files_to_choose_from[choice_index]}? " + F"(Type: yes)\n").lower() == "yes" + else: + confirm = False + + # Do the restore + if confirm or utils.no_confirm: result = container.restore_backup(backup_file.path) else: break + # Print result and log + if result: + _print(F"{Fore.GREEN}Backup {container.restore_tar_file} for {container.name} successfully restored.{Style.RESET_ALL}") + if not utils.no_log and settings_list['log']['logging']: + log.log(F"Restore backup ; {container.name} ; {container.restore_tar_file_path} ; SUCCESS") + else: + _print(F"{Fore.RED}Could not restore {container.restore_tar_file} for {container.name}.{Style.RESET_ALL}") + for func, traceback in container.exceptions.items(): + _print() + _print(F"{Fore.YELLOW}Exception occurred in method: Container.{func}(){Style.RESET_ALL}") + _print(traceback) + _print() + backup_status = False + if not utils.no_log and settings_list['log']['logging']: + log.log(F"Restore backup ; {container.name} ; {container.restore_tar_file_path} ; FAIL") + if __name__ == '__main__': restore()