\x89PNG\r\n\x1a\n\x00\x00\x00\x0DIHDR\x00\x00\x00\x01\x00 \x00\x00\x01\x08\x06\x00\x00\x00\x1F\x15\xC4\x89\x00\x00\x00 \x0AIDATx\x9Ccb\x00\x00\x00\x06\x00\x03\x1A\x05\x9D\x00\x00 \x00\x00IEND\xAE\x42\x60\x82
| Path : /var/www/html/yedekledb24/DatabaseBackupMasterv7/ |
|
B-Con CMD Config cPanel C-Rdp D-Log Info Jump Mass Ransom Symlink vHost Zone-H |
| Current File : /var/www/html/yedekledb24/DatabaseBackupMasterv7/perform_incremental_mysql_backup.py |
import os
import subprocess
import tempfile
import logging
import shutil
import tarfile
from models import BackupLog
from pathlib import Path
logger = logging.getLogger(__name__)
def perform_incremental_mysql_backup(db_conn, temp_file, job, parent_log_id=None):
"""
Perform incremental MySQL/MariaDB backup using XtraBackup or MariaBackup.
Args:
db_conn (DatabaseConnection): Database connection object
temp_file (str): Path to temporary file for backup
job (BackupJob): Backup job configuration
parent_log_id (int, optional): ID of parent backup log entry
Returns:
bool: True if backup successful, False otherwise
"""
try:
# Detect database type and select the appropriate backup tool
db_type = db_conn.db_type.lower()
# Select the appropriate backup tool based on database type
if db_type == 'mariadb':
backup_tool = 'mariabackup'
logger.info("Using MariaBackup for MariaDB")
elif db_type == 'mysql':
backup_tool = 'xtrabackup'
logger.info("Using Percona XtraBackup for MySQL")
else:
logger.error(f"Unsupported database type for incremental backup: {db_type}")
return False
# Check if the selected backup tool is installed
try:
subprocess.run([backup_tool, '--version'], capture_output=True, check=True)
except (subprocess.SubprocessError, FileNotFoundError):
logger.error(f"{backup_tool} is not installed. Cannot perform incremental backup.")
return False
# Set MySQL/MariaDB connection parameters
mysql_args = {
'host': db_conn.host,
'port': db_conn.port,
'user': db_conn.username,
'password': db_conn.password,
'database': db_conn.database
}
# Create a temporary directory for the backup
backup_dir = tempfile.mkdtemp(prefix=f"backup_{mysql_args['database']}_")
# Determine backup type
backup_type = job.backup_method # 'full', 'incremental', 'differential'
# Default command for full backup
cmd = [
backup_tool,
'--backup',
f'--target-dir={backup_dir}',
f'--host={mysql_args["host"]}',
f'--port={mysql_args["port"]}',
f'--user={mysql_args["user"]}',
f'--password={mysql_args["password"]}'
]
# For incremental/differential backup, find the base backup
parent_backup_dir = None
if backup_type in ['incremental', 'differential']:
if parent_log_id:
parent_backup = BackupLog.query.get(parent_log_id)
else:
# Find the last full backup for this job
parent_backup = BackupLog.query.filter_by(
job_id=job.id,
backup_method='full',
status='success'
).order_by(BackupLog.timestamp.desc()).first()
if not parent_backup:
logger.error(f"No parent backup found for {backup_type} backup. Performing full backup instead.")
backup_type = 'full'
else:
# Extract the parent backup to a temporary location
if os.path.exists(parent_backup.backup_file):
logger.info(f"Using parent backup from {parent_backup.timestamp}")
# Create a temporary directory for the parent backup
parent_backup_dir = tempfile.mkdtemp(prefix=f"backup_parent_")
# Extract the parent backup
if parent_backup.backup_file.endswith('.tar.gz'):
with tarfile.open(parent_backup.backup_file, 'r:gz') as tar:
tar.extractall(path=parent_backup_dir)
# Find the extracted directory in parent_backup_dir
subdirs = [d for d in os.listdir(parent_backup_dir) if os.path.isdir(os.path.join(parent_backup_dir, d))]
if subdirs:
parent_backup_dir = os.path.join(parent_backup_dir, subdirs[0])
else:
logger.error(f"Unsupported parent backup format: {parent_backup.backup_file}")
backup_type = 'full' # Fall back to full backup
# Add incremental backup parameters
if backup_type == 'incremental' and parent_backup_dir:
cmd.append('--incremental')
cmd.append(f'--incremental-basedir={parent_backup_dir}')
elif backup_type == 'differential' and parent_backup_dir:
cmd.append('--incremental')
# Find the original full backup (base) for differential backup
if parent_backup.backup_method == 'full':
cmd.append(f'--incremental-basedir={parent_backup_dir}')
else:
# Traverse up the backup chain until we find a full backup
base_backup = parent_backup
while base_backup and base_backup.backup_method != 'full':
base_backup = BackupLog.query.get(base_backup.parent_backup_id)
if base_backup and os.path.exists(base_backup.backup_file):
# Extract the base backup
base_backup_dir = tempfile.mkdtemp(prefix=f"backup_base_")
with tarfile.open(base_backup.backup_file, 'r:gz') as tar:
tar.extractall(path=base_backup_dir)
# Find the extracted directory in base_backup_dir
subdirs = [d for d in os.listdir(base_backup_dir) if os.path.isdir(os.path.join(base_backup_dir, d))]
if subdirs:
base_backup_dir = os.path.join(base_backup_dir, subdirs[0])
cmd.append(f'--incremental-basedir={base_backup_dir}')
else:
logger.error("Could not find base backup for differential backup")
backup_type = 'full' # Fall back to full backup
else:
logger.error(f"Parent backup file not found: {parent_backup.backup_file}")
backup_type = 'full' # Fall back to full backup
# Add any extra options specified for XtraBackup
if job.xtrabackup_options:
extra_options = job.xtrabackup_options.split()
cmd.extend(extra_options)
# Execute backup command
logger.info(f"Executing {backup_type} backup with {backup_tool}")
logger.debug(f"Backup command: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"Backup error: {result.stderr}")
return False
# For full backups, prepare the backup to make it consistent
if backup_type == 'full':
prepare_cmd = [
backup_tool,
'--prepare',
f'--target-dir={backup_dir}'
]
prepare_result = subprocess.run(prepare_cmd, capture_output=True, text=True)
if prepare_result.returncode != 0:
logger.error(f"Backup prepare error: {prepare_result.stderr}")
return False
# Create a tarball of the backup directory
target_dir = os.path.dirname(temp_file)
os.makedirs(target_dir, exist_ok=True)
with tarfile.open(temp_file, "w:gz") as tar:
tar.add(backup_dir, arcname=os.path.basename(backup_dir))
# If encryption is enabled, encrypt the tarball
if job.encrypt_backup and job.encryption_type == 'symmetric' and job.encryption_passphrase:
try:
from encryption_utils import encrypt_file
encrypted_file = encrypt_file(
temp_file,
recipients=None,
output_file=f"{temp_file}.gpg",
passphrase=job.encryption_passphrase
)
if encrypted_file:
# Remove the unencrypted file and update the temp_file path
os.unlink(temp_file)
temp_file = encrypted_file
else:
logger.error("Failed to encrypt backup file")
except Exception as e:
logger.error(f"Encryption error: {str(e)}")
# Clean up temporary directories
try:
shutil.rmtree(backup_dir)
if parent_backup_dir:
shutil.rmtree(os.path.dirname(parent_backup_dir))
if 'base_backup_dir' in locals() and base_backup_dir:
shutil.rmtree(os.path.dirname(base_backup_dir))
except Exception as e:
logger.warning(f"Failed to clean up temporary directories: {str(e)}")
logger.info(f"Successfully completed {backup_type} backup with {backup_tool}")
return True
except Exception as e:
logger.error(f"Backup error: {str(e)}")
return False