Skip to content

Commit 3fe515a

Browse files
committed
Also backup config files
1 parent 2a322c8 commit 3fe515a

File tree

1 file changed

+39
-3
lines changed

1 file changed

+39
-3
lines changed

scripts/dstack-backup.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import argparse
3232
import subprocess
3333
import shutil
34+
import tarfile
3435
from datetime import datetime, timedelta
3536
from pathlib import Path
3637
from typing import Dict, List, Optional, Tuple
@@ -182,6 +183,31 @@ def update_backup_time(self, vm_id: str, backup_type: str):
182183
logger.debug(
183184
f"Updated {backup_type} backup timestamp for VM {vm_id} to {datetime.fromtimestamp(current_time)}")
184185

186+
def _backup_vm_configs(self, vm_dir: Path, backup_dir: Path) -> bool:
187+
"""Backup VM configuration files as tar.gz"""
188+
vm_configs = ["vm-manifest.json", "shared/"]
189+
config_archive = backup_dir / "vm-configs.tar.gz"
190+
191+
logger.info(f"Creating VM config backup: {config_archive}")
192+
193+
try:
194+
with tarfile.open(config_archive, 'w:gz') as tar:
195+
for config_item in vm_configs:
196+
config_path = vm_dir / config_item
197+
if config_path.exists():
198+
# Add to archive with relative path
199+
tar.add(config_path, arcname=config_item)
200+
logger.debug(f"Added {config_item} to config archive")
201+
else:
202+
logger.warning(
203+
f"Config item {config_item} not found, skipping")
204+
205+
logger.info(f"VM config backup completed: {config_archive}")
206+
return True
207+
except Exception as e:
208+
logger.error(f"Failed to create VM config backup: {e}")
209+
return False
210+
185211
def perform_backup(self, vm_id: str, vm_name: str, backup_type: str, hd: str) -> bool:
186212
"""Perform a backup for the specified VM"""
187213
logger.info(f"Performing {backup_type} backup...")
@@ -198,13 +224,16 @@ def perform_backup(self, vm_id: str, vm_name: str, backup_type: str, hd: str) ->
198224
# Set backup level based on type
199225
backup_level = "full" if backup_type == "full" else "inc"
200226

227+
vm_configs = ["vm-manifest.json", "shared/"]
228+
201229
# Create or update latest symlink
202230
latest_dir = backup_dir / "latest"
203231

204232
def do_backup():
205233
# For full backups, clear bitmaps first
206234
if backup_level == "full":
207-
logger.info(f"Clearing bitmaps for full backup of VM {vm_name}")
235+
logger.info(
236+
f"Clearing bitmaps for full backup of VM {vm_name}")
208237
if qmp_socket.exists():
209238
try:
210239
# Use absolute path for qmp_socket
@@ -259,7 +288,13 @@ def do_backup():
259288
# Get return code
260289
returncode = process.wait()
261290
if returncode == 0:
262-
logger.info(f"Backup successful")
291+
logger.info(f"Disk backup successful")
292+
293+
# Backup VM configuration files
294+
if not self._backup_vm_configs(vm_dir, abs_latest_dir):
295+
logger.error("VM config backup failed")
296+
return False
297+
263298
self.update_backup_time(vm_id, backup_type)
264299

265300
# Rotate backups if needed
@@ -385,7 +420,8 @@ def run(self):
385420
hd = vm['hd']
386421

387422
logger.info("-" * 50)
388-
logger.info(f"[{i+1}/{total_vms}] Processing VM: {vm_name} ({vm_id})")
423+
logger.info(
424+
f"[{i+1}/{total_vms}] Processing VM: {vm_name} ({vm_id})")
389425

390426
# Check if backup is needed
391427
backup_type = self.needs_backup(vm_id)

0 commit comments

Comments
 (0)