Create proxmox-ftpbackup.sh
This commit is contained in:
parent
3b6ed4fea2
commit
8f4b6926c8
1 changed files with 146 additions and 0 deletions
146
proxmox-ftpbackup.sh
Normal file
146
proxmox-ftpbackup.sh
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
### FTP config
|
||||||
|
FTPHOST="your.ftp.com"
|
||||||
|
FTPPATH="dump/"
|
||||||
|
FTPUSER=""
|
||||||
|
FTPPASS=""
|
||||||
|
### Purge after x days
|
||||||
|
PURGE=3
|
||||||
|
### Storage ID to move
|
||||||
|
STORAGE="local-backup"
|
||||||
|
|
||||||
|
##### CONFIG END #####
|
||||||
|
|
||||||
|
### FUNCTIONS
|
||||||
|
function log {
|
||||||
|
echo "[HOOK] $*"
|
||||||
|
}
|
||||||
|
function purge_ftp {
|
||||||
|
VM=$1
|
||||||
|
log "PURGE for VM ${VM} started."
|
||||||
|
|
||||||
|
PURGEDATE=$(date --date="${PURGE} days ago" +%s)
|
||||||
|
FILES=$(curl -s -u "${FTPUSER}:${FTPPASS}" --list-only "ftp://${FTPHOST}/${FTPPATH}" | sort)
|
||||||
|
RETURNCODE=$?
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then
|
||||||
|
log " LIST: ftp://${FTPHOST}/${FTPPATH} - FAILED (curl): $RETURNCODE"
|
||||||
|
return $RETURNCODE
|
||||||
|
fi
|
||||||
|
for FILE in ${FILES[@]}; do
|
||||||
|
SPLITFILE="${FILE/./ }"; SPLITFILE="${SPLITFILE//-/ }"; SPLITFILE=(${SPLITFILE//_/-})
|
||||||
|
|
||||||
|
VMSTR="${SPLITFILE[2]}"
|
||||||
|
DATESTR="${SPLITFILE[3]}"
|
||||||
|
TIMESTR="${SPLITFILE[4]}"; TIMESTR="${TIMESTR//-/:}"
|
||||||
|
if [[ $(date --date="$DATESTR $TIMESTR" +%s) -lt $PURGEDATE && "$VMSTR" == "$VM" ]]; then
|
||||||
|
curl -s -u "${FTPUSER}:${FTPPASS}" --head -Q "-DELE ${FTPPATH}${FILE}" "ftp://${FTPHOST}"
|
||||||
|
RETURNCODE=$?
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then
|
||||||
|
log " DELETE: $FILE - FAILED (curl): $RETURNCODE"
|
||||||
|
return $RETURNCODE
|
||||||
|
else
|
||||||
|
log " DELETE: $FILE - SUCCESS"
|
||||||
|
fi
|
||||||
|
elif [[ "$VMSTR" == "$VM" ]]; then
|
||||||
|
log " KEEP: $FILE - not older than ${PURGE} days"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
function upload_ftp {
|
||||||
|
FILE=$1
|
||||||
|
log "UPLOAD for ${FILE} started."
|
||||||
|
|
||||||
|
RETURNCODE=1
|
||||||
|
TRIES=0
|
||||||
|
while [[ $RETURNCODE -ne 0 && $TRIES -lt 3 ]]; do
|
||||||
|
((TRIES++))
|
||||||
|
curl -s -u "${FTPUSER}:${FTPPASS}" --keepalive-time 30 -T "$FILE" "ftp://${FTPHOST}/${FTPPATH}"
|
||||||
|
RETURNCODE=$?
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then
|
||||||
|
# try to mitigate curl (28): Timeout, curl(55)
|
||||||
|
# usually file transfers fine, but control channel is killed by the firewall
|
||||||
|
FILENAME=$(basename "${FILE}")
|
||||||
|
LOCALSIZE=$(stat --printf="%s" "${FILE}" | tr -d '[[:space:]]')
|
||||||
|
REMOTESIZE=$(curl -sI -u "${FTPUSER}:${FTPPASS}" "ftp://${FTPHOST}/${FTPPATH}${FILENAME}" | awk '/Content-Length/ { print $2 }' | tr -d '[[:space:]]')
|
||||||
|
log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - local: ${LOCALSIZE}, remote: ${REMOTESIZE}"
|
||||||
|
if [[ "$REMOTESIZE" -eq "$LOCALSIZE" ]]; then
|
||||||
|
log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - WARN (curl): $RETURNCODE, but seems complete"
|
||||||
|
RETURNCODE=0
|
||||||
|
else
|
||||||
|
log " UPLOAD #${TRIES}: $FILE to ftp://${FTPHOST}/${FTPPATH} - FAILED (curl): $RETURNCODE"
|
||||||
|
if [[ $RETURNCODE -eq 55 ]]; then
|
||||||
|
curl -s -u "${FTPUSER}:${FTPPASS}" --head -Q "-DELE ${FTPPATH}${FILENAME}" "ftp://${FTPHOST}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then
|
||||||
|
log " UPLOAD: $FILE to ftp://${FTPHOST}/${FTPPATH} - FAILED PERMANENTLY"
|
||||||
|
else
|
||||||
|
log " UPLOAD: $FILE to ftp://${FTPHOST}/${FTPPATH} - SUCCESS"
|
||||||
|
fi
|
||||||
|
return $RETURNCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
### MAIN
|
||||||
|
PHASE=$1
|
||||||
|
if [[ "$PHASE" == "job-start" || "$PHASE" == "job-end" || "$PHASE" == "job-abort" ]]; then
|
||||||
|
#DUMPDIR
|
||||||
|
#STOREID
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [[ "$PHASE" == "backup-start" || "$PHASE" == "backup-end" || "$PHASE" == "backup-abort" || "$PHASE" == "log-end" || "$PHASE" == "pre-stop" || "$PHASE" == "pre-restart" || "$PHASE" == "post-restart" ]]; then
|
||||||
|
MODE=$2 # stop,suspend,snapshot
|
||||||
|
VMID=$3
|
||||||
|
#DUMPDIR
|
||||||
|
#STOREID
|
||||||
|
#VMTYPE # openvz,qemu
|
||||||
|
#HOSTNAME
|
||||||
|
|
||||||
|
if [[ "$PHASE" == "backup-end" && "$STOREID" == "$STORAGE" ]]; then
|
||||||
|
#TARFILE
|
||||||
|
log "transfer backup archive of VM ${VMID} to FTP"
|
||||||
|
upload_ftp "$TARFILE"
|
||||||
|
RETURNCODE=$?
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then exit $RETURNCODE; fi
|
||||||
|
log "remove local backup archive of VM ${VMID}"
|
||||||
|
rm "$TARFILE"
|
||||||
|
|
||||||
|
log "purge old backup and log files of VM ${VMID} from FTP"
|
||||||
|
purge_ftp $VMID
|
||||||
|
RETURNCODE=$?
|
||||||
|
exit $RETURNCODE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$PHASE" == "log-end" && "$STOREID" == "$STORAGE" ]]; then
|
||||||
|
#LOGFILE
|
||||||
|
log "transfer log file of VM ${VMID} to FTP"
|
||||||
|
upload_ftp "$LOGFILE"
|
||||||
|
RETURNCODE=$?
|
||||||
|
if [[ $RETURNCODE -ne 0 ]]; then exit $RETURNCODE; fi
|
||||||
|
log "remove local log file of VM ${VMID}"
|
||||||
|
rm "$LOGFILE"
|
||||||
|
|
||||||
|
exit $RETURNCODE
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# manual tasks
|
||||||
|
|
||||||
|
if [[ $PHASE == "manual-cleanup" ]]; then
|
||||||
|
VMID=$2
|
||||||
|
if [[ -z $VMID ]]; then
|
||||||
|
echo "Usage: $(basename $0) manual-cleanup <VMID>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "purge old backup and log files of VM ${VMID} from FTP"
|
||||||
|
purge_ftp $VMID
|
||||||
|
RETURNCODE=$?
|
||||||
|
log "purge old backup and log files of VM ${VMID} from local storage"
|
||||||
|
rm -r ${STORAGEDIR}/dump/vzdump-*-${VMID}-*
|
||||||
|
exit $RETURNCODE
|
||||||
|
fi
|
Loading…
Reference in a new issue