Implement basic logic to trigger snapshots through Crafty.

This commit is contained in:
Wout Bouckaert 2024-08-11 15:27:35 -06:00
parent e6a577c172
commit 2e47d5b21e
No known key found for this signature in database
8 changed files with 130 additions and 33 deletions

View File

@ -1,3 +1,6 @@
import base64
class CryptoHelper:
def __init__(self, helper):
self.helper = helper
@ -5,3 +8,7 @@ class CryptoHelper:
def say_hello_world(self):
print(self.test)
@staticmethod
def bytes_to_b64(input_bytes: bytes) -> str:
return base64.b64encode(input_bytes).decode("UTF-8").rstrip("\n")

View File

@ -117,6 +117,7 @@ class Backups(BaseModel):
default = BooleanField(default=False)
status = CharField(default='{"status": "Standby", "message": ""}')
enabled = BooleanField(default=True)
backup_type = CharField(default="zip_archive")
class Meta:
table_name = "backups"
@ -368,6 +369,7 @@ class HelpersManagement:
"after": backup.after,
"default": backup.default,
"enabled": backup.enabled,
"backup_type": backup.backup_type,
}
else:
data = Backups.select().where(Backups.server_id == server_id).execute()
@ -375,13 +377,10 @@ class HelpersManagement:
@staticmethod
def get_default_server_backup(server_id: str) -> dict:
print(server_id)
bu_query = Backups.select().where(
Backups.server_id == server_id,
Backups.default == True, # pylint: disable=singleton-comparison
)
for item in bu_query:
print("HI", item)
backup_model = bu_query.first()
if backup_model:

View File

@ -3,6 +3,7 @@ import time
import datetime
import json
import logging
import pathlib
from zoneinfo import ZoneInfo
@ -35,11 +36,13 @@ class BackupManager:
self.tz = ZoneInfo("Europe/London")
def backup_starter(self, backup_config, server):
if backup_config.get("type", "zip_vault") == "zip_vault":
self.zip_vault(backup_config, server)
def zip_vault(self, backup_config, server):
"""Notify users of backup starting, and start the backup.
Args:
backup_config (_type_): _description_
server (_type_): Server object to backup
"""
# Notify users of backup starting
logger.info(f"Starting server {server.name}" f" (ID {server.server_id}) backup")
server_users = PermissionsServers.get_server_user_list(server.server_id)
# Alert the start of the backup to the authorized users.
@ -53,6 +56,14 @@ class BackupManager:
)
time.sleep(3)
# Start the backup
if backup_config.get("backup_type", "zip_vault") == "zip_vault":
self.zip_vault(backup_config, server)
else:
self.snapshot_backup(backup_config, server)
def zip_vault(self, backup_config, server):
# Adjust the location to include the backup ID for destination.
backup_location = os.path.join(
backup_config["backup_location"], backup_config["backup_id"]
@ -126,32 +137,35 @@ class BackupManager:
)
time.sleep(5)
except Exception as e:
logger.exception(
"Failed to create backup of server"
f" {server.name} (ID {server.server_id})"
)
results = {
"percent": 100,
"total_files": 0,
"current_file": 0,
"backup_id": backup_config["backup_id"],
}
if len(WebSocketManager().clients) > 0:
WebSocketManager().broadcast_page_params(
"/panel/server_detail",
{"id": str(server.server_id)},
"backup_status",
results,
)
HelpersManagement.update_backup_config(
backup_config["backup_id"],
{"status": json.dumps({"status": "Failed", "message": f"{e}"})},
)
self.fail_backup(e, backup_config, server)
server.backup_server(
backup_config,
)
def fail_backup(self, why: Exception, backup_config: dict, server):
logger.exception(
"Failed to create backup of server"
f" {server.name} (ID {server.server_id})"
)
results = {
"percent": 100,
"total_files": 0,
"current_file": 0,
"backup_id": backup_config["backup_id"],
}
if len(WebSocketManager().clients) > 0:
WebSocketManager().broadcast_page_params(
"/panel/server_detail",
{"id": str(server.server_id)},
"backup_status",
results,
)
HelpersManagement.update_backup_config(
backup_config["backup_id"],
{"status": json.dumps({"status": "Failed", "message": f"{why}"})},
)
def list_backups(self, backup_config: dict, server_id) -> list:
if not backup_config:
logger.info(
@ -197,3 +211,37 @@ class BackupManager:
)
logger.info(f"Removing old backup '{oldfile['path']}'")
os.remove(Helpers.get_os_understandable_path(oldfile_path))
def snapshot_backup(self, backup_config, server):
logger.info(f"Starting snapshot style backup for {server.name}")
# Adjust the location to include the backup ID for destination.
backup_location = os.path.join(
pathlib.Path(backup_config["backup_location"]), "snapshot_backups"
)
try:
self.ensure_snapshot_directory_is_valid(backup_location)
except PermissionError as why:
self.fail_backup(why, backup_config, server)
def ensure_snapshot_directory_is_valid(self, backup_path: pathlib.Path) -> bool:
backup_path.mkdir(exist_ok=True)
backup_readme_path = backup_path / "README.txt"
if not backup_readme_path.exists():
logger.info("Is this doing anything?")
try:
logger.info("Attempting to make snapshot storage directory.")
with open(backup_readme_path, "w", encoding="UTF-8") as f:
f.write(
"Crafty snapshot backup storage dir. Please do not touch"
"these files."
)
except PermissionError as why:
raise PermissionError(
f"Unable to write to snapshot backup storage path"
f": {backup_readme_path}"
) from why
return True

View File

@ -30,6 +30,12 @@ BACKUP_PATCH_SCHEMA = {
"before": {"type": "string"},
"after": {"type": "string"},
"excluded_dirs": {"type": "array"},
"backup_type": {
"type": "string",
"enum": ["zip_vault", "snapshot"],
"error": "enumErr",
"fill": True,
},
},
"additionalProperties": False,
"minProperties": 1,
@ -45,6 +51,12 @@ BASIC_BACKUP_PATCH_SCHEMA = {
"before": {"type": "string"},
"after": {"type": "string"},
"excluded_dirs": {"type": "array"},
"backup_type": {
"type": "string",
"enum": ["zip_vault", "snapshot"],
"error": "enumErr",
"fill": True,
},
},
"additionalProperties": False,
"minProperties": 1,

View File

@ -19,6 +19,12 @@ backup_patch_schema = {
"before": {"type": "string"},
"after": {"type": "string"},
"excluded_dirs": {"type": "array"},
"backup_type": {
"type": "string",
"enum": ["zip_vault", "snapshot"],
"error": "enumErr",
"fill": True,
},
},
"additionalProperties": False,
"minProperties": 1,
@ -34,6 +40,12 @@ basic_backup_patch_schema = {
"before": {"type": "string"},
"after": {"type": "string"},
"excluded_dirs": {"type": "array"},
"backup_type": {
"type": "string",
"enum": ["zip_vault", "snapshot"],
"error": "enumErr",
"fill": True,
},
},
"additionalProperties": False,
"minProperties": 1,

View File

@ -73,8 +73,7 @@
data['lang']) }} </th>
<th scope="col" style="width: 50%; min-width: 50px;">{{ translate('serverBackups',
'storageLocation', data['lang']) }}</th>
<th scope="col" style="width: 10%; min-width: 50px;">{{ translate('serverBackups',
'maxBackups', data['lang']) }}</th>
<th scope="col" style="width: 10%; min-width: 50px;">Backup Type</th>
<th scope="col" style="width: 10%; min-width: 50px;">{{ translate('serverBackups', 'actions',
data['lang']) }}</th>
</tr>
@ -102,8 +101,8 @@
<td id="{{backup.backup_location}}" class="type">
<p style="overflow: scroll;" class="no-scroll">{{backup.backup_location}}</p>
</td>
<td id="{{backup.max_backups}}" class="trigger" style="overflow: scroll; max-width: 30px;">
<p>{{backup.max_backups}}</p>
<td id="{{backup.max_backups}}_{{backup.backup_id}}" class="trigger" style="overflow: scroll; max-width: 30px;">
<p>{{backup.backup_type}}</p>
</td>
<td id="backup_edit" class="action">
<button

View File

@ -76,6 +76,10 @@
placeholder="{{ translate('serverBackups', 'myBackup', data['lang']) }}">
{% end %}
<br>
<input type="radio" class="form-check-input" name="backup_type" id="zip_archive" value="zip_archive" checked>
 <label for="True">Full Zip Archive</label><br>
<input type="radio" class="form-check-input" name="backup_type" id="zip_archive" value="snapshot">
 <label for="True">Snapshot</label><br>
<br>
{% if data['super_user'] %}
<label for="server_name">{{ translate('serverBackups', 'storageLocation', data['lang']) }} <small

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
"""
Write your migrations here.
"""
migrator.add_columns("backups", backup_type=peewee.CharField(default="zip_archive"))
def rollback(migrator, database, **kwargs):
"""
Write your rollback migrations here.
"""
migrator.drop_columns("backups", ["backup_type"])