Setup new backup manager

ToDo: Add legacy backups to new manager to make them more module
This commit is contained in:
= 2024-12-13 22:57:49 -05:00
parent a4256b66e8
commit bd55cdce16
3 changed files with 128 additions and 111 deletions

View File

@ -2,20 +2,24 @@ import base64
import hashlib import hashlib
import pathlib import pathlib
import shutil import shutil
import uuid
import zlib import zlib
import datetime
from app.classes.shared.crypto_helper import CryptoHelper from app.classes.shared.crypto_helper import CryptoHelper
# Set byte constants
BYTE_FALSE = bytes.fromhex("00")
BYTE_TRUE = bytes.fromhex("01")
class BackupManager: class BackupManager:
def __init__(self, server_instance): def __init__(self, server_instance):
self.server_instance = server_instance self.server_instance = server_instance
self.crypto_helper = CryptoHelper() self.crypto_helper = CryptoHelper()
# Set byte constants ####################################################################################
BYTE_FALSE = bytes.fromhex("00") ########################## SNAPSHOT METHODS ######################################
BYTE_TRUE = bytes.fromhex("01") ####################################################################################
@staticmethod @staticmethod
def blake2_hash_bytes(bytes_to_hash: bytes) -> bytes: def blake2_hash_bytes(bytes_to_hash: bytes) -> bytes:
@ -211,12 +215,12 @@ class BackupManager:
# Compress bytes if set to true # Compress bytes if set to true
if use_compression: if use_compression:
file_chunk = self.compress_bytes(file_chunk) file_chunk = self.compress_bytes(file_chunk)
compression = self.BYTE_TRUE compression = BYTE_TRUE
else: else:
compression = self.BYTE_FALSE compression = BYTE_FALSE
# Placeholder for encryption # Placeholder for encryption
encryption = self.BYTE_FALSE encryption = BYTE_FALSE
nonce = bytes.fromhex("000000000000000000000000") nonce = bytes.fromhex("000000000000000000000000")
# Create Chunk # Create Chunk
@ -321,16 +325,21 @@ class BackupManager:
""" """
return self.b64_to_bytes(input_b64).decode("utf-8") return self.b64_to_bytes(input_b64).decode("utf-8")
def backup(self) -> uuid.UUID: def backup(self, conf: dict) -> None:
""" """
Perform the backup. Perform the backup.
Iterate over files in source dir. Apply save function. Save file information to manifest. Iterate over files in source dir. Apply save function.
Save file information to manifest.
:return: UUID of backup. :return: UUID of backup.
""" """
# Initialize backup stuff. # Initialize backup stuff.\
backup_id = uuid.uuid4() backup_id = str(
source_path = pathlib.Path(__file__).parent / "source_files" datetime.datetime.now()
repo_path = pathlib.Path(__file__).parent / "backup_repository" .astimezone(self.server_instance.tz)
.strftime("%Y-%m-%d_%H-%M-%S")
)
source_path = pathlib.Path(self.server_instance.server_path)
repo_path = pathlib.Path(conf["backup_location"]) / "snapshots"
manifest_path = repo_path / "manifests" / f"{str(backup_id)}.manifest" manifest_path = repo_path / "manifests" / f"{str(backup_id)}.manifest"
# Get list of files to backup. # Get list of files to backup.
@ -373,7 +382,6 @@ class BackupManager:
# Backup complete # Backup complete
manifest_file.close() manifest_file.close()
return backup_id
def recover( def recover(
self, self,
@ -505,10 +513,14 @@ class BackupManager:
# Read use compression byte # Read use compression byte
use_compression_byte = chunk_file.read(1) use_compression_byte = chunk_file.read(1)
if use_compression_byte == self.BYTE_TRUE: if use_compression_byte == BYTE_TRUE:
try: try:
return self.decompress_bytes(chunk_file.read()) return self.decompress_bytes(chunk_file.read())
except zlib.error as why: except zlib.error as why:
raise RuntimeError(f"Unable to decompress file {chunk_path}.") from why raise RuntimeError(f"Unable to decompress file {chunk_path}.") from why
else: else:
return chunk_file.read() return chunk_file.read()
####################################################################################
########################## LEGACY BACKUP METHODS #################################
####################################################################################

View File

@ -1,2 +1,3 @@
class CryptoHelper: class CryptoHelper:
print("hi") def __init__(self):
return

View File

@ -1185,108 +1185,112 @@ class ServerInstance:
self.helper.ensure_dir_exists(backup_location) self.helper.ensure_dir_exists(backup_location)
try: if conf["snapshot"]:
backup_filename = ( self.backup_manager.backup(conf)
f"{backup_location}/" else:
f"{datetime.datetime.now().astimezone(self.tz).strftime('%Y-%m-%d_%H-%M-%S')}" # pylint: disable=line-too-long
)
logger.info(
f"Creating backup of server '{self.settings['server_name']}'"
f" (ID#{self.server_id}, path={self.server_path}) "
f"at '{backup_filename}'"
)
excluded_dirs = HelpersManagement.get_excluded_backup_dirs(backup_id)
server_dir = Helpers.get_os_understandable_path(self.settings["path"])
self.file_helper.make_backup( try:
Helpers.get_os_understandable_path(backup_filename), backup_filename = (
server_dir, f"{backup_location}/"
excluded_dirs, f"{datetime.datetime.now().astimezone(self.tz).strftime('%Y-%m-%d_%H-%M-%S')}" # pylint: disable=line-too-long
self.server_id,
backup_id,
conf["backup_name"],
conf["compress"],
)
while (
len(self.list_backups(conf)) > conf["max_backups"]
and conf["max_backups"] > 0
):
backup_list = self.list_backups(conf)
oldfile = backup_list[0]
oldfile_path = f"{backup_location}/{oldfile['path']}"
logger.info(f"Removing old backup '{oldfile['path']}'")
os.remove(Helpers.get_os_understandable_path(oldfile_path))
logger.info(f"Backup of server: {self.name} completed")
results = {
"percent": 100,
"total_files": 0,
"current_file": 0,
"backup_id": backup_id,
}
if len(WebSocketManager().clients) > 0:
WebSocketManager().broadcast_page_params(
"/panel/server_detail",
{"id": str(self.server_id)},
"backup_status",
results,
) )
server_users = PermissionsServers.get_server_user_list(self.server_id)
for user in server_users:
WebSocketManager().broadcast_user(
user,
"notification",
self.helper.translation.translate(
"notify",
"backupComplete",
HelperUsers.get_user_lang_by_id(user),
).format(self.name),
)
if was_server_running:
logger.info( logger.info(
"Backup complete. User had shutdown preference. Starting server." f"Creating backup of server '{self.settings['server_name']}'"
f" (ID#{self.server_id}, path={self.server_path}) "
f"at '{backup_filename}'"
) )
self.run_threaded_server(HelperUsers.get_user_id_by_name("system")) excluded_dirs = HelpersManagement.get_excluded_backup_dirs(backup_id)
time.sleep(3) server_dir = Helpers.get_os_understandable_path(self.settings["path"])
if conf["after"]:
if self.check_running(): self.file_helper.make_backup(
logger.debug( Helpers.get_os_understandable_path(backup_filename),
"Found running server and send command option. Sending command" server_dir,
excluded_dirs,
self.server_id,
backup_id,
conf["backup_name"],
conf["compress"],
)
while (
len(self.list_backups(conf)) > conf["max_backups"]
and conf["max_backups"] > 0
):
backup_list = self.list_backups(conf)
oldfile = backup_list[0]
oldfile_path = f"{backup_location}/{oldfile['path']}"
logger.info(f"Removing old backup '{oldfile['path']}'")
os.remove(Helpers.get_os_understandable_path(oldfile_path))
logger.info(f"Backup of server: {self.name} completed")
results = {
"percent": 100,
"total_files": 0,
"current_file": 0,
"backup_id": backup_id,
}
if len(WebSocketManager().clients) > 0:
WebSocketManager().broadcast_page_params(
"/panel/server_detail",
{"id": str(self.server_id)},
"backup_status",
results,
) )
self.send_command(conf["after"]) server_users = PermissionsServers.get_server_user_list(self.server_id)
# pause to let people read message. for user in server_users:
HelpersManagement.update_backup_config( WebSocketManager().broadcast_user(
backup_id, user,
{"status": json.dumps({"status": "Standby", "message": ""})}, "notification",
) self.helper.translation.translate(
time.sleep(5) "notify",
except Exception as e: "backupComplete",
logger.exception( HelperUsers.get_user_lang_by_id(user),
f"Failed to create backup of server {self.name} (ID {self.server_id})" ).format(self.name),
) )
results = { if was_server_running:
"percent": 100, logger.info(
"total_files": 0, "Backup complete. User had shutdown preference. Starting server."
"current_file": 0, )
"backup_id": backup_id, self.run_threaded_server(HelperUsers.get_user_id_by_name("system"))
} time.sleep(3)
if len(WebSocketManager().clients) > 0: if conf["after"]:
WebSocketManager().broadcast_page_params( if self.check_running():
"/panel/server_detail", logger.debug(
{"id": str(self.server_id)}, "Found running server and send command option. Sending command"
"backup_status", )
results, self.send_command(conf["after"])
# pause to let people read message.
HelpersManagement.update_backup_config(
backup_id,
{"status": json.dumps({"status": "Standby", "message": ""})},
) )
if was_server_running: time.sleep(5)
logger.info( except Exception as e:
"Backup complete. User had shutdown preference. Starting server." logger.exception(
f"Failed to create backup of server {self.name} (ID {self.server_id})"
)
results = {
"percent": 100,
"total_files": 0,
"current_file": 0,
"backup_id": backup_id,
}
if len(WebSocketManager().clients) > 0:
WebSocketManager().broadcast_page_params(
"/panel/server_detail",
{"id": str(self.server_id)},
"backup_status",
results,
)
if was_server_running:
logger.info(
"Backup complete. User had shutdown preference. Starting server."
)
self.run_threaded_server(HelperUsers.get_user_id_by_name("system"))
HelpersManagement.update_backup_config(
backup_id,
{"status": json.dumps({"status": "Failed", "message": f"{e}"})},
) )
self.run_threaded_server(HelperUsers.get_user_id_by_name("system"))
HelpersManagement.update_backup_config(
backup_id,
{"status": json.dumps({"status": "Failed", "message": f"{e}"})},
)
self.set_backup_status() self.set_backup_status()
def last_backup_status(self): def last_backup_status(self):