refactor: implement automatic cleanup of incomplete backup directories and ensure retention policy enforcement using finally blocks
This commit is contained in:
parent
8851a1e0e7
commit
a0bbe9a3b8
53
gui_app.py
53
gui_app.py
@ -349,8 +349,6 @@ def enforce_retention_policy(info, log_path=None):
|
|||||||
|
|
||||||
retention_type = info.get('retention_type', 'keep_all')
|
retention_type = info.get('retention_type', 'keep_all')
|
||||||
retention_val = info.get('retention_value', 5)
|
retention_val = info.get('retention_value', 5)
|
||||||
if retention_type == 'keep_all':
|
|
||||||
return
|
|
||||||
|
|
||||||
vm_name = info.get('vm_name')
|
vm_name = info.get('vm_name')
|
||||||
parent_dest = info.get('dest')
|
parent_dest = info.get('dest')
|
||||||
@ -363,18 +361,39 @@ def enforce_retention_policy(info, log_path=None):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
subdirs = []
|
subdirs = []
|
||||||
|
failed_dirs = []
|
||||||
for name in os.listdir(vm_dir):
|
for name in os.listdir(vm_dir):
|
||||||
path = os.path.join(vm_dir, name)
|
path = os.path.join(vm_dir, name)
|
||||||
if os.path.isdir(path) and name.startswith('backup-'):
|
if os.path.isdir(path) and name.startswith('backup-'):
|
||||||
subdirs.append((name, path))
|
# Check for manifest.json to verify successful completion
|
||||||
|
manifest_file = os.path.join(path, 'manifest.json')
|
||||||
|
if os.path.exists(manifest_file):
|
||||||
|
subdirs.append((name, path))
|
||||||
|
else:
|
||||||
|
failed_dirs.append((name, path))
|
||||||
|
|
||||||
# Sort chronologically by folder name (backup-YYYYMMDDHHMMSS)
|
# Always clean up failed/incomplete backup directories immediately
|
||||||
|
if failed_dirs:
|
||||||
|
log_msg(f"Found {len(failed_dirs)} failed/incomplete backup directory(ies). Cleaning up...")
|
||||||
|
for name, path in failed_dirs:
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(path)
|
||||||
|
log_msg(f"Cleaned up failed backup directory: {name}")
|
||||||
|
except Exception as e:
|
||||||
|
log_msg(f"ERROR cleaning up failed backup {name}: {e}")
|
||||||
|
|
||||||
|
# If retention is keep_all, we don't prune successful backups, but we did clean up failed ones
|
||||||
|
if retention_type == 'keep_all':
|
||||||
|
return
|
||||||
|
|
||||||
|
# Sort successful backups chronologically by folder name (backup-YYYYMMDDHHMMSS)
|
||||||
subdirs.sort(key=lambda x: x[0])
|
subdirs.sort(key=lambda x: x[0])
|
||||||
|
|
||||||
if retention_type == 'keep_count':
|
if retention_type == 'keep_count':
|
||||||
if len(subdirs) > retention_val:
|
if len(subdirs) > retention_val:
|
||||||
to_delete = subdirs[:-retention_val]
|
to_delete = subdirs[:-retention_val]
|
||||||
log_msg(f"Enforcing count retention (keep {retention_val}). Deleting {len(to_delete)} old backup(s)...")
|
log_msg(f"Enforcing count retention (keep {retention_val}). Deleting {len(to_delete)} old successful backup(s)...")
|
||||||
for name, path in to_delete:
|
for name, path in to_delete:
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
@ -486,15 +505,6 @@ def run_job_thread(jid):
|
|||||||
is_cancelled_cb=is_cancelled,
|
is_cancelled_cb=is_cancelled,
|
||||||
)
|
)
|
||||||
success_vms.append(vm)
|
success_vms.append(vm)
|
||||||
|
|
||||||
# Enforce retention policy for this VM
|
|
||||||
vm_info = {
|
|
||||||
'vm_name': vm,
|
|
||||||
'dest': info['dest'],
|
|
||||||
'retention_type': info.get('retention_type', 'keep_all'),
|
|
||||||
'retention_value': info.get('retention_value', 5)
|
|
||||||
}
|
|
||||||
enforce_retention_policy(vm_info, log_path=log_path)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "cancelled by user" in str(e).lower():
|
if "cancelled by user" in str(e).lower():
|
||||||
failed_vms.append((vm, "Cancelled by user"))
|
failed_vms.append((vm, "Cancelled by user"))
|
||||||
@ -506,6 +516,15 @@ def run_job_thread(jid):
|
|||||||
failed_vms.append((vm, str(e)))
|
failed_vms.append((vm, str(e)))
|
||||||
with open(log_path, 'a', encoding='utf-8') as f:
|
with open(log_path, 'a', encoding='utf-8') as f:
|
||||||
f.write(f"\nERROR backing up VM {vm}: {e}\n\n")
|
f.write(f"\nERROR backing up VM {vm}: {e}\n\n")
|
||||||
|
finally:
|
||||||
|
# Always enforce retention policy (which cleans up failed folders immediately)
|
||||||
|
vm_info = {
|
||||||
|
'vm_name': vm,
|
||||||
|
'dest': info['dest'],
|
||||||
|
'retention_type': info.get('retention_type', 'keep_all'),
|
||||||
|
'retention_value': info.get('retention_value', 5)
|
||||||
|
}
|
||||||
|
enforce_retention_policy(vm_info, log_path=log_path)
|
||||||
|
|
||||||
if failed_vms:
|
if failed_vms:
|
||||||
if success_vms:
|
if success_vms:
|
||||||
@ -554,9 +573,6 @@ def run_job_thread(jid):
|
|||||||
info['status'] = 'finished'
|
info['status'] = 'finished'
|
||||||
info['progress'] = {'pct': 100, 'phase': 'done', 'detail': 'Backup completed successfully'}
|
info['progress'] = {'pct': 100, 'phase': 'done', 'detail': 'Backup completed successfully'}
|
||||||
save_jobs_db()
|
save_jobs_db()
|
||||||
|
|
||||||
# Enforce retention policy
|
|
||||||
enforce_retention_policy(info, log_path=log_path)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "cancelled by user" in str(e).lower():
|
if "cancelled by user" in str(e).lower():
|
||||||
info['status'] = 'failed (Cancelled)'
|
info['status'] = 'failed (Cancelled)'
|
||||||
@ -564,6 +580,9 @@ def run_job_thread(jid):
|
|||||||
else:
|
else:
|
||||||
info['status'] = f'failed ({e})'
|
info['status'] = f'failed ({e})'
|
||||||
save_jobs_db()
|
save_jobs_db()
|
||||||
|
finally:
|
||||||
|
# Always enforce retention policy (which cleans up failed folders immediately)
|
||||||
|
enforce_retention_policy(info, log_path=log_path)
|
||||||
|
|
||||||
|
|
||||||
def create_and_start_job(
|
def create_and_start_job(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user