[init] first commit
This commit is contained in:
155
rsync_python.py
Normal file
155
rsync_python.py
Normal file
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/python3
|
||||
#######################################################
|
||||
# Python rsync Backup script
|
||||
# Sebastian Kraft, 24.06.2013
|
||||
#
|
||||
#######################################################
|
||||
|
||||
#-----------------------------------------------------
|
||||
# Config
|
||||
|
||||
# Source directories
|
||||
DIRECTORY_LIST = (
|
||||
"/home/kraft/Bilder",
|
||||
"/home/kraft/Dokumente",
|
||||
"/home/kraft/Projekte/",
|
||||
"/home/kraft/Videos/",
|
||||
"/home/kraft/Musik/"
|
||||
)
|
||||
|
||||
# Target directory to store the backups
|
||||
TARGET_DIRECTORY = "/media/kraft/fantec_ext4/Kraft_Backup/"
|
||||
|
||||
# Some settings
|
||||
WRITE_LOGFILE = True # write a logfile to TARGET_DIRECTORY
|
||||
|
||||
# TODO
|
||||
#ALLOW_EMPTY_BACKUPS = TRUE # perform backup even if there are no changes detected by rsync
|
||||
#CHECK_BACKUPS = True # check file consistency after every backup run
|
||||
#------------------------------------------------------
|
||||
|
||||
#######################################################
|
||||
|
||||
#------------------------------------------------------
|
||||
# Some functions
|
||||
|
||||
|
||||
def ask_ok(prompt, retries=4, complaint='Please type yes or no...'):
|
||||
while True:
|
||||
ok = input(prompt)
|
||||
if ok in ('y', 'Y', 'yes', 'Yes'):
|
||||
return True
|
||||
if ok in ('n', 'N', 'no', 'No'):
|
||||
return False
|
||||
print(complaint)
|
||||
|
||||
|
||||
def print2log(s, filehandle=0):
|
||||
global WRITE_LOGFILE
|
||||
sys.stdout.buffer.write(bytes(s, "utf-8"))
|
||||
sys.stdout.flush()
|
||||
# '\r' has no effect for file write
|
||||
if (WRITE_LOGFILE==True) and filehandle and (s.find('\r')==-1):
|
||||
filehandle.write(bytes(s, "utf-8"))
|
||||
|
||||
#------------------------------------------------------
|
||||
# Main program
|
||||
|
||||
import time
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
# check if target directory is mounted
|
||||
if not os.path.exists(TARGET_DIRECTORY):
|
||||
print("\nERROR: Target directory \n>> "+TARGET_DIRECTORY+" <<\nis not available! If it's located on an external or network drive check if it is correctly mounted in the expected place.\n\n")
|
||||
sys.exit()
|
||||
|
||||
# prepare logfile
|
||||
if WRITE_LOGFILE:
|
||||
logFile = open(os.path.join(TARGET_DIRECTORY, 'rsync_' + time.strftime( "%Y-%m-%dT%H:%M:%S") + '.log'), 'wb')
|
||||
else:
|
||||
logFile = 0
|
||||
|
||||
numBackupItems = len(DIRECTORY_LIST)
|
||||
currBackupItem = 0
|
||||
|
||||
# iterate over directories
|
||||
for backupDir in DIRECTORY_LIST:
|
||||
|
||||
currBackupItem = currBackupItem + 1
|
||||
|
||||
countStr = "("+str(currBackupItem)+"/"+str(numBackupItems)+")"
|
||||
|
||||
print2log("\n-----------------------------------------------------------\n", logFile)
|
||||
print2log("Backing up " + backupDir + " " + countStr +"\n", logFile)
|
||||
print2log("-----------------------------------------------------------\n", logFile)
|
||||
|
||||
# check if source directory exists
|
||||
if not os.path.exists(backupDir):
|
||||
print2log("ERROR: Source directory does not exist! Skipping...\n", logFile)
|
||||
input("Press any key to proceed with next backup item...")
|
||||
continue
|
||||
|
||||
timestamp = time.strftime( "%Y-%m-%dT%H:%M:%S")
|
||||
current_backup_target = os.path.join(TARGET_DIRECTORY, os.path.basename(os.path.normpath(backupDir)))
|
||||
previous_backup_link = ""
|
||||
|
||||
# check for previous backups
|
||||
prevBackupsFound = False
|
||||
if os.path.exists(current_backup_target):
|
||||
dirListing = os.listdir(current_backup_target)
|
||||
dirListing = [name for name in os.listdir(current_backup_target) if os.path.isdir(os.path.join(current_backup_target,name))]
|
||||
# match directory names of type 2013-06-24T18:44:31
|
||||
rex = re.compile('[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]{8}$')
|
||||
dirListing = [x for x in dirListing if rex.match(x)]
|
||||
numOldBackups = len(dirListing)
|
||||
if numOldBackups>0:
|
||||
#dirListing.sort(key=lambda s: os.path.getmtime(os.path.join(current_backup_target, s)))
|
||||
dirListing.sort()
|
||||
dirListing.reverse()
|
||||
previous_backup_link = os.path.join(current_backup_target, dirListing[0])
|
||||
print2log("Previous backup will be used as hard link reference: "+previous_backup_link+"\n", logFile)
|
||||
previous_backup_link = '--link-dest="' + previous_backup_link +'" '
|
||||
prevBackupsFound = True
|
||||
|
||||
# if no old backups are found, ask to create a new one
|
||||
if not prevBackupsFound:
|
||||
print2log("WARNING: No previous data for incremental backups were found!\n", logFile)
|
||||
if ask_ok("Should a complete backup be performed? (y/n)"):
|
||||
if not os.path.exists(current_backup_target):
|
||||
os.mkdir(current_backup_target)
|
||||
else:
|
||||
# continue with next backup item
|
||||
continue
|
||||
|
||||
# assemble rsync commandline and run it
|
||||
rsynccmd = 'rsync -aP ' + previous_backup_link + ' ' + backupDir + ' ' + os.path.join(current_backup_target,timestamp+"_tmp")
|
||||
print2log("+" + countStr + "+ " + rsynccmd + "\n\n", logFile)
|
||||
rsyncproc = subprocess.Popen(rsynccmd,
|
||||
shell=True,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
# read rsync output and print to console
|
||||
while True:
|
||||
next_line = rsyncproc.stdout.readline().decode("utf-8")
|
||||
if not next_line:
|
||||
break
|
||||
print2log("+" + countStr + "+ " + next_line, logFile)
|
||||
|
||||
# wait until process is really terminated
|
||||
exitcode = rsyncproc.wait()
|
||||
# check exit code
|
||||
if exitcode==0:
|
||||
os.rename(os.path.join(current_backup_target,timestamp+"_tmp"), os.path.join(current_backup_target,timestamp))
|
||||
print2log("done \n\n", logFile)
|
||||
else:
|
||||
print2log("\nWARNING: looks like some error occured :( \n\n", logFile)
|
||||
break
|
||||
|
||||
# close logfile
|
||||
if (WRITE_LOGFILE==True) and logFile:
|
||||
logFile.close()
|
Reference in New Issue
Block a user