Created
December 30, 2024 22:29
-
-
Save momja/83531e6af416d2afd207eaa5fca2572d to your computer and use it in GitHub Desktop.
Borg and Rsync Backup Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import subprocess | |
import sys | |
import logging | |
from dotenv import load_dotenv | |
from datetime import datetime | |
import argparse # Import argparse for handling command line arguments | |
# Set up logging (change this if you want your logs to go elsewhere) | |
log_dir = "/var/log/backup_script" | |
os.makedirs(log_dir, exist_ok=True) | |
os.chmod(log_dir, 0o755) # Set appropriate permissions | |
log_file = os.path.join(log_dir, f"backup_{datetime.now().strftime('%Y-%m-%d')}.log") | |
logging.basicConfig( | |
filename=log_file, level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s' | |
) | |
# Backup directories (Make sure these environment variables are defined) | |
# I.E. in .env file: | |
# export REMOTE_BACKUP_DIR="you@computer:/path/to/data/" | |
# export LOCAL_BACKUP_DIR="/mnt/backup/data" | |
# export BORG_REPO_DIR="/mnt/backup/data_borg" | |
# export BACKUP_DRIVE="/dev/sda1" | |
load_dotenv() | |
remote_backup_dir = os.getenv("REMOTE_BACKUP_DIR") | |
local_backup_dir = os.getenv("LOCAL_BACKUP_DIR") | |
borg_repo_dir = os.getenv("BORG_REPO_DIR") | |
backup_drive = os.getenv("BACKUP_DRIVE") | |
def run_command(command, should_exit_on_fail=True): | |
logging.info(f"Executing: {command}") | |
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) | |
output, error = process.communicate() | |
# Allow rsync warning codes (e.g., 23, 24), but exit on hard errors | |
if process.returncode != 0: | |
logging.error(f"Error executing command: {command}") | |
logging.error(f"Error message: {error.decode('utf-8')}") | |
if (should_exit_on_fail): | |
logging.error("Marked exit on failure. Exiting...") | |
sys.exit(1) | |
else: | |
logging.error("Marked continue on failure. Continuing...") | |
return output.decode('utf-8') | |
def mount_drive(): | |
logging.info("Mounting drive...") | |
# Check if the drive is already mounted | |
with open('/proc/mounts', 'r') as f: | |
mounts = f.read() | |
if '/mnt/backup' in mounts: | |
logging.info("Drive is already mounted, skipping mount step.") | |
else: | |
run_command("mount /dev/sda1 /mnt/backup") | |
def sync_files(): | |
logging.info("Syncing files...") | |
# exclude-file.txt must be in /etc/backup-config | |
# and provide a list of files to not include in synchronization. | |
# E.G. log files | |
rsync_command = ( | |
f"rsync -az --delete --exclude-from='/etc/backup-config/exclude-file.txt' -e 'ssh -c aes128-ctr' {remote_backup_dir} {local_backup_dir}" | |
) | |
output = run_command(rsync_command, False) | |
logging.info(f"Rsync output:\n{output}") | |
def backup_files(): | |
logging.info("Backing up files...") | |
borg_command = ( | |
f"borg create --stats --progress " | |
f"{borg_repo_dir}::dockerData-{{now}} " | |
f"{local_backup_dir}" | |
) | |
output = run_command(borg_command) | |
logging.info(f"Borg output:\n{output}") | |
def unmount_drive(): | |
logging.info("Unmounting drive...") | |
run_command("umount /mnt/backup") | |
def main(): | |
parser = argparse.ArgumentParser( | |
description='Backup script using rsync and Borg.' | |
) | |
parser.add_argument( | |
'--sync', | |
action='store_true', | |
help='Run rsync for file synchronization' | |
) | |
parser.add_argument( | |
'--backup', | |
action='store_true', | |
help='Run borg for backup creation' | |
) | |
args = parser.parse_args() | |
if not args.sync and not args.backup: | |
logging.error('Either --sync, --backup, or both must be specified.') | |
sys.exit(1) | |
logging.info("Starting backup process") | |
try: | |
mount_drive() | |
if args.sync: | |
sync_files() | |
if args.backup: | |
backup_files() | |
except Exception as e: | |
logging.error(f"An error occurred: {str(e)}") | |
finally: | |
unmount_drive() | |
logging.info("Backup process completed") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment