From 7535298a7429cb10c9110802a0817acf16deb773 Mon Sep 17 00:00:00 2001 From: Marc Michalsky Date: Mon, 23 Nov 2020 16:27:19 +0100 Subject: [PATCH] Initial commit --- .gitignore | 3 + Models/Backup.py | 0 Models/Container.py | 91 +++++++++++++++++++++++ Models/NextcloudBackupException.py | 8 ++ Models/__pycache__/Backup.cpython-38.pyc | Bin 0 -> 152 bytes README.md | 1 + backup.py | 61 +++++++++++++++ example_settings.yml | 17 +++++ main.py | 5 ++ requirements.txt | 2 + restore.py | 1 + upgrade.py | 1 + 12 files changed, 190 insertions(+) create mode 100644 .gitignore create mode 100644 Models/Backup.py create mode 100644 Models/Container.py create mode 100644 Models/NextcloudBackupException.py create mode 100644 Models/__pycache__/Backup.cpython-38.pyc create mode 100644 README.md create mode 100644 backup.py create mode 100644 example_settings.yml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 restore.py create mode 100644 upgrade.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ceb8e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +venv/ +.idea/ +settings.yaml \ No newline at end of file diff --git a/Models/Backup.py b/Models/Backup.py new file mode 100644 index 0000000..e69de29 diff --git a/Models/Container.py b/Models/Container.py new file mode 100644 index 0000000..87dee5a --- /dev/null +++ b/Models/Container.py @@ -0,0 +1,91 @@ +import datetime +import os +import stat +import tarfile +from Models.NextcloudBackupException import NextcloudBackupException + + +class Container: + + def __init__(self, name, password, backup_folder, compose_file_path) -> None: + self.__datetime = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") + self.name = name + self.__password = password + self.backup_folder = backup_folder + self.compose_file_path = compose_file_path + self.__dump_file = self.name + '_' + self.__datetime + '.sql' + self.__dump_file_path = os.path.join(self.backup_folder, self.__dump_file) + self.__tar_file = self.name + '_' + self.__datetime + 'tar.gz' + self.__tar_file_path = os.path.join(self.backup_folder, self.__tar_file) + + def __str__(self) -> str: + return F"name: {self.name}\npassword: {self.__password}\ncompose_file_path: {self.compose_file_path}" + + # Create backup folder if it does not yet exist + def create_backup_dir(self): + if not os.path.isfile(self.backup_folder): + try: + os.makedirs(self.backup_folder) + except OSError as e: + raise OSError(e) + return True + else: + return False + + # Dump database + def __dump_db(self) -> bool: + try: + os.system(F"docker exec {self.name} mysqldump --password={self.__password} --all-databases > " + F"{self.__dump_file}") + return os.path.isfile(self.__dump_file) + except Exception as e: + raise Exception(e) + + # Tar config and settings folder within container and copy it into backup folder + def __export_config(self) -> bool: + try: + os.system(F"docker exec {self.name} tar -czf config_settings.tar config settings") + os.system(F"docker cp {self.name}:/var/www/html/config_settings.tar {self.__tar_file_path}") + os.system(F"docker exec {self.name} rm config_settings.tar") + return os.path.isfile(self.__tar_file_path) + except Exception as e: + raise NextcloudBackupException(e) + + # Tar database with config and settings + def __tar_db(self) -> bool: + try: + with tarfile.open(self.__tar_file_path, 'w:gz') as tarball: + tarball.add(self.__dump_file_path, arcname=self.__dump_file) + return tarball.gettarinfo(self.__dump_file).isfile() + except Exception as e: + raise NextcloudBackupException(e) + + # Set secure file permissions + def __set_file_permissions(self) -> bool: + try: + os.chmod(self.__tar_file_path, stat.S_IREAD) + return oct(os.stat(self.__tar_file_path).st_mode)[-3:] == 600 + except Exception as e: + raise NextcloudBackupException(e) + + # Create backup + def create_backup(self) -> dict: + try: + return {"database dump": self.__dump_db(), + "export config": self.__export_config(), + "include db in backup": self.__tar_db(), + "set secure backup file permissions": self.__set_file_permissions()} + except NextcloudBackupException as e: + raise NextcloudBackupException(e) + + @staticmethod + def deserialize_containers(data: dict) -> list: + containers = [] + for name, values in data['nextcloud_containers'].items(): + containers.append(Container( + name, + values['mysql_password'], + values['backup_folder'], + values['compose_file_path']) + ) + return containers diff --git a/Models/NextcloudBackupException.py b/Models/NextcloudBackupException.py new file mode 100644 index 0000000..8e22ba4 --- /dev/null +++ b/Models/NextcloudBackupException.py @@ -0,0 +1,8 @@ + +class NextcloudBackupException(Exception): + def __init__(self, message): + self.message = message + + + def __write_to_log(self): + pass diff --git a/Models/__pycache__/Backup.cpython-38.pyc b/Models/__pycache__/Backup.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bc444dd729942a1404fa5a9d1d5770d8f083caa GIT binary patch literal 152 zcmWIL<>g`k0*Ohx;z9Id5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!Hmenx(7s(x-_ zQL=tOWpYMhQEos{epYI7NwI!jYDGzMPJU@hd~tG7W&u#dH$Npcr&!-9F*&=mK(C