Export backup images to a tar archive

This helps ensure an easier restore as the tar archive(s) can be sent
directly to the API via the normal process of image uploading, instead
of individual disks.
This commit is contained in:
Joshua Boniface 2023-10-23 09:56:50 -04:00
parent fabb97cf48
commit 38abd078af
1 changed files with 36 additions and 6 deletions

View File

@ -29,6 +29,7 @@ from distutils.util import strtobool
from uuid import UUID from uuid import UUID
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from datetime import datetime from datetime import datetime
from shutil import rmtree
from json import dump as jdump from json import dump as jdump
import daemon_lib.common as common import daemon_lib.common as common
@ -1427,19 +1428,46 @@ def backup_vm(
f'ERROR: Failed to export snapshot for volume(s) {", ".join(which_snapshot_export_failed)}: {", ".join(msg_snapshot_export_failed)}', f'ERROR: Failed to export snapshot for volume(s) {", ".join(which_snapshot_export_failed)}: {", ".join(msg_snapshot_export_failed)}',
) )
# 7. Create and dump VM backup information # 7. Archive RBD images to a single tar file
backup_archive_file = f"{vm_target_root}/{domain}.{datestring}.pvcdisks"
tar_cmd_base = [
"tar",
"--create",
"--file",
backup_archive_file,
"--directory",
f"{vm_target_root}/.{datestring}",
]
tar_cmd_files = [f"{v}.{export_fileext}" for p, v in vm_volumes]
tar_cmd = tar_cmd_base + tar_cmd_files
retcode, stdout, stderr = common.run_os_command(tar_cmd)
if retcode:
return (
False,
f"ERROR: Failed to combine export files into archive: {stderr}",
)
is_exports_remove_failed = False
msg_exports_remove_failed = ""
try:
rmtree(f"{vm_target_root}/.{datestring}")
except Exception as e:
is_exports_remove_failed = True
msg_exports_remove_failed = str(e)
# 8. Create and dump VM backup information
backup_type = "incremental" if incremental_parent is not None else "full" backup_type = "incremental" if incremental_parent is not None else "full"
vm_backup = { vm_backup = {
"type": backup_type, "type": backup_type,
"datestring": datestring, "datestring": datestring,
"incremental_parent": incremental_parent, "incremental_parent": incremental_parent,
"vm_detail": vm_detail, "vm_detail": vm_detail,
"backup_files": [f".{datestring}/{v}.{export_fileext}" for p, v in vm_volumes], "backup_archive_file": backup_archive_file,
} }
with open(f"{vm_target_root}/{domain}.{datestring}.pvcbackup", "w") as fh: with open(f"{vm_target_root}/{domain}.{datestring}.pvcbackup", "w") as fh:
jdump(vm_backup, fh) jdump(vm_backup, fh)
# 8. Remove snapshots if retain_snapshot is False # 9. Remove snapshots if retain_snapshot is False
is_snapshot_remove_failed = False is_snapshot_remove_failed = False
which_snapshot_remove_failed = list() which_snapshot_remove_failed = list()
msg_snapshot_remove_failed = list() msg_snapshot_remove_failed = list()
@ -1457,11 +1485,13 @@ def backup_vm(
tend = time.time() tend = time.time()
ttot = round(tend - tstart, 2) ttot = round(tend - tstart, 2)
if is_snapshot_remove_failed: if retain_snapshots:
retmsg = f"WARNING: Successfully backed up VM '{domain}' ({backup_type}@{datestring}) to '{target_path}' in {ttot} seconds, but failed to remove snapshot as requested for volume(s) {', '.join(which_snapshot_remove_failed)}: {', '.join(msg_snapshot_remove_failed)}"
elif retain_snapshots:
retmsg = f"Successfully backed up VM '{domain}' ({backup_type}@{datestring}, snapshots retained) to '{target_path}' in {ttot} seconds." retmsg = f"Successfully backed up VM '{domain}' ({backup_type}@{datestring}, snapshots retained) to '{target_path}' in {ttot} seconds."
else: else:
retmsg = f"Successfully backed up VM '{domain}' ({backup_type}@{datestring}) to '{target_path}' in {ttot} seconds." retmsg = f"Successfully backed up VM '{domain}' ({backup_type}@{datestring}) to '{target_path}' in {ttot} seconds."
if is_exports_remove_failed:
retmsg = f"WARNING: Failed to remove raw export files: {msg_exports_remove_failed}\n{retmsg}"
if is_snapshot_remove_failed:
retmsg = f"WARNING: Failed to remove snapshot as requested for volume(s) {', '.join(which_snapshot_remove_failed)}: {', '.join(msg_snapshot_remove_failed)}\n{retmsg}"
return True, retmsg return True, retmsg