implement restore

This commit is contained in:
Marc Koch 2020-12-03 21:46:00 +01:00
parent 9ad8e12522
commit 3bcf77e0b8
Signed by: marc
GPG Key ID: AC2D4E00990A6767
2 changed files with 53 additions and 16 deletions

View File

@ -30,8 +30,10 @@ class Container:
self.exceptions = {} self.exceptions = {}
self.SUCCESS = F"{Fore.GREEN}success{Style.RESET_ALL}" self.SUCCESS = F"{Fore.GREEN}success{Style.RESET_ALL}"
self.FAILED = F"{Fore.RED}failed{Style.RESET_ALL}" self.FAILED = F"{Fore.RED}failed{Style.RESET_ALL}"
self.__restore_dump_file = ""
global quiet_mode self.__restore_dump_file_path = ""
self.__restore_tar_file_path = ""
self.__restore_tar_file = ""
# Create backup dir if it does not yet exist # Create backup dir if it does not yet exist
def __create_backup_dir(self): def __create_backup_dir(self):
@ -88,7 +90,8 @@ class Container:
def __import_db(self) -> bool: def __import_db(self) -> bool:
try: try:
os.system( 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 status = True # TODO: Implement test if database import was successful
_print(F"Import Nextcloud database: {self.SUCCESS if status else self.FAILED}") _print(F"Import Nextcloud database: {self.SUCCESS if status else self.FAILED}")
return status return status
@ -109,6 +112,8 @@ class Container:
status = os.path.isdir(os.path.join(self.tmp_dir, "config")) status = os.path.isdir(os.path.join(self.tmp_dir, "config"))
_print(F"Export Nextcloud configuration: {self.SUCCESS if status else self.FAILED}") _print(F"Export Nextcloud configuration: {self.SUCCESS if status else self.FAILED}")
return status return status
else:
_print(F"Export Nextcloud configuration: {self.FAILED}")
except: except:
_print(F"Export Nextcloud configuration: {self.FAILED}") _print(F"Export Nextcloud configuration: {self.FAILED}")
self.exceptions.update({'__export_config': traceback.format_exc()}) self.exceptions.update({'__export_config': traceback.format_exc()})
@ -118,11 +123,12 @@ class Container:
def __import_config(self) -> bool: def __import_config(self) -> bool:
try: try:
if os.path.isdir(os.path.join(self.tmp_dir, "config")): 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") 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} rm -r config")
os.system(F"docker exec {self.app_container} tar -xf config.tar") 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 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}") _print(F"Import Nextcloud configuration: {self.SUCCESS if status else self.FAILED}")
return status return status
@ -142,7 +148,7 @@ class Container:
return status return status
except: except:
_print(F"Zip backup: {self.FAILED}") _print(F"Zip backup: {self.FAILED}")
self.exceptions.update({'__tar_db': traceback.format_exc()}) self.exceptions.update({'__tar_backup': traceback.format_exc()})
return False return False
# Untar backup # Untar backup
@ -150,12 +156,12 @@ class Container:
try: try:
with tarfile.open(backup_file_path, 'r:gz') as tarball: with tarfile.open(backup_file_path, 'r:gz') as tarball:
tarball.extractall(self.tmp_dir) 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}") _print(F"Unzip backup: {self.SUCCESS if status else self.FAILED}")
return status return status
except: except:
_print(F"Unzip backup: {self.FAILED}") _print(F"Unzip backup: {self.FAILED}")
self.exceptions.update({'__untar_db': traceback.format_exc()}) self.exceptions.update({'__untar_backup': traceback.format_exc()})
return False return False
# Set secure file permissions # Set secure file permissions
@ -194,13 +200,18 @@ class Container:
def restore_backup(self, backup_file_path): 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(): if self.__create_tmp_dir():
try: try:
step_status = [ step_status = [
self.__untar_backup(backup_file_path), self.__untar_backup(backup_file_path),
#self.__import_config(), self.__import_config(),
#self.__import_db(), self.__import_db(),
#self.__delete_tmp_dir() self.__delete_tmp_dir()
] ]
for step in step_status: for step in step_status:
if not step: if not step:

View File

@ -24,6 +24,7 @@ def restore():
utils.all_containers = "--all" in sys.argv utils.all_containers = "--all" in sys.argv
utils.quiet_mode = "--quiet" in sys.argv utils.quiet_mode = "--quiet" in sys.argv
utils.no_log = "--nolog" in sys.argv utils.no_log = "--nolog" in sys.argv
utils.no_confirm = "--yes" in sys.argv
# Load settings # Load settings
settings = Path(__file__).parent / "settings.yaml" settings = Path(__file__).parent / "settings.yaml"
@ -33,7 +34,7 @@ def restore():
log = Log(settings_list['log']['log_dir']) log = Log(settings_list['log']['log_dir'])
containers = Container.instantiate_containers(settings_list) 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} containers_wanted = {name: container for name, container in containers.items() if name in sys.argv}
if containers_wanted: if containers_wanted:
containers = 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])} containers = {containers_to_choose_from[choice_index]: containers.get(containers_to_choose_from[choice_index])}
container: Container container: Container
results = {}
for container in containers.values(): for container in containers.values():
# Start backup restore # Start restore
_print("----------------------------------------------") _print("----------------------------------------------")
_print(F"Restore backup for {container.name}") _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 = [file.name for file in backup_files.values()]
backup_files_to_choose_from.sort(reverse=True) backup_files_to_choose_from.sort(reverse=True)
_print() _print()
# Choose backup to restore from
terminal_menu = TerminalMenu(backup_files_to_choose_from, title="Which backup do you want to restore?") terminal_menu = TerminalMenu(backup_files_to_choose_from, title="Which backup do you want to restore?")
choice_index = terminal_menu.show() choice_index = terminal_menu.show()
backup_file = backup_files.get(backup_files_to_choose_from[choice_index]) backup_file = backup_files.get(backup_files_to_choose_from[choice_index])
print(backup_file.path) print(backup_file.path)
confirm = input(F"Are you sure that you want to restore {backup_files_to_choose_from[choice_index]}? " # Confirm restore
F"(Type: yes)\n").lower() == "yes" if not utils.no_confirm:
if 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) result = container.restore_backup(backup_file.path)
else: else:
break 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__': if __name__ == '__main__':
restore() restore()