blob: 868e4bb455a692252cbcfef6a7c140e6454b6878 [file] [log] [blame]
{%- from "xtrabackup/map.jinja" import client with context %}
{%- from "xtrabackup/map.jinja" import server with context %}
#!/bin/sh
#
# Script to create full and incremental backups (for all databases on server) using innobackupex from Percona.
# http://www.percona.com/doc/percona-xtrabackup/innobackupex/innobackupex_script.html
#
# Every time it runs will generate an incremental backup except for the first time (full backup).
# FULLBACKUPLIFE variable will define your full backups schedule.
SKIPCLEANUP=false
while getopts ":skip-cleanup" opt; do
case $opt in
skip-cleanup)
echo "Cleanup will be skipped" >&2
SKIPCLEANUP=true
;;
force-full)
echo "Full backup will be force triggered"
FORCEFULL=true
;;
\?)
echo "Invalid option: -$OPTARG" >&2
;;
esac
done
USEROPTIONS="--user={{ client.database.user }} --password={{ client.database.password }} --socket=/var/run/mysqld/mysqld.sock"
#TMPFILE="/var/log/backups/innobackupex-runner.$$.tmp"
LOGDIR=/var/log/backups
TMPFILE="/var/log/backups/innobackupex-runner.log"
MYCNF=/etc/mysql/my.cnf
MYSQL=/usr/bin/mysql
MYSQLADMIN=/usr/bin/mysqladmin
BACKUPDIR={{ client.backup_dir }} # Client side backups base directory
SERVERBACKUPDIR={{ server.backup_dir }} # Server side backups base directory
FULLBACKUPDIR=$BACKUPDIR/full # Full backups directory
INCRBACKUPDIR=$BACKUPDIR/incr # Incremental backups directory
KEEP={{ client.full_backups_to_keep }} # Number of full backups (and its incrementals) to keep
{%- if client.backup_times is defined %}
INCRBEFOREFULL={{ client.incr_before_full }}
{%- else %}
HOURSFULLBACKUPLIFE={{ client.hours_before_full }} # Lifetime of the latest full backup in hours
FULLBACKUPLIFE=$(( $HOURSFULLBACKUPLIFE * 60 * 60 ))
{%- endif %}
rsyncLog=/var/log/backups/innobackupex-rsync.log
{%- if client.compression is defined %}
compression={{ client.compression }}
{%- else %}
compression=false
{%- endif %}
{%- if client.compression_threads is defined %}
compression_threads={{ client.compression_threads }}
{%- else %}
compression_threads=1
{%- endif %}
mkdir -p $LOGDIR
# Grab start time
STARTED_AT=`date +%s`
#############################################################################
# Display error message and exit
#############################################################################
error()
{
echo "$1" 1>&2
exit 1
}
# Check prerequisites before proceeding
if [ ! -d $BACKUPDIR ]; then
error "Backup destination folder: $BACKUPDIR does not exist."
fi
if [ -z "`$MYSQLADMIN $USEROPTIONS status | grep 'Uptime'`" ] ; then
error "HALTED: MySQL does not appear to be running."
fi
if ! `echo 'exit' | $MYSQL -s $USEROPTIONS` ; then
error "HALTED: Supplied mysql username or password appears to be incorrect (not copied here for security, see script)."
fi
# Some info output
echo "----------------------------"
echo
echo "$0: MySQL backup script"
echo "started: `date`"
echo
# Create full and incr backup directories if they not exist.
mkdir -p $FULLBACKUPDIR
mkdir -p $INCRBACKUPDIR
# Find latest full backup
LATEST_FULL=`find $FULLBACKUPDIR -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -nr | head -1`
# Get latest backup last modification time
LATEST_FULL_CREATED_AT=`stat -c %Y $FULLBACKUPDIR/$LATEST_FULL`
# If compression is enabled, pass it on to the backup command
if [ "$compression" = True ]; then
compress="--compress"
compression_threads="--compress-threads=$compression_threads"
echo "Setting compression to True"
echo
else
compress=
compression_threads=
fi
{%- if client.backup_times is not defined %}
# Run an incremental backup if latest full is still valid. Otherwise, run a new full one.
if [ "$LATEST_FULL" -a `expr $LATEST_FULL_CREATED_AT + $FULLBACKUPLIFE + 5` -ge $STARTED_AT ] ; then
# Create incremental backups dir if not exists.
TMPINCRDIR=$INCRBACKUPDIR/$LATEST_FULL
mkdir -p $TMPINCRDIR
# Find latest incremental backup.
LATEST_INCR=`find $TMPINCRDIR -mindepth 1 -maxdepth 1 -type d | sort -nr | head -1`
# If this is the first incremental, use the full as base. Otherwise, use the latest incremental as base.
if [ ! $LATEST_INCR ] ; then
INCRBASEDIR=$FULLBACKUPDIR/$LATEST_FULL
else
INCRBASEDIR=$LATEST_INCR
fi
echo "Running new incremental backup using $INCRBASEDIR as base."
innobackupex --defaults-file=$MYCNF $USEROPTIONS $compress $compression_threads --incremental $TMPINCRDIR --incremental-basedir $INCRBASEDIR > $TMPFILE 2>&1
else
echo "Running new full backup."
innobackupex --defaults-file=$MYCNF $USEROPTIONS $compress $compression_threads $FULLBACKUPDIR > $TMPFILE 2>&1
fi
{%- else %}
# Get number of full and incremental backups
NUMBER_OF_FULL=`find $FULLBACKUPDIR -maxdepth 1 -mindepth 1 -type d -print| wc -l`
NUMBER_OF_INCR=`find $INCRBACKUPDIR -maxdepth 2 -mindepth 2 -type d -print| wc -l`
echo "Number of Full backups stored: " $NUMBER_OF_FULL
echo "Number of Incremental backups stored: " $NUMBER_OF_INCR
echo "----------------------------"
#If number of incremental mod number of full backups to keep equals 1, run full backup, otherwise run incremental
if [ $(( ($NUMBER_OF_INCR + $NUMBER_OF_FULL) % ($INCRBEFOREFULL + 1) )) -eq 0 || FORCEFULL=true ] ; then
echo "Running new full backup."
innobackupex --defaults-file=$MYCNF $USEROPTIONS $compress $compression_threads $FULLBACKUPDIR > $TMPFILE 2>&1
else
# Create incremental backups dir if not exists.
TMPINCRDIR=$INCRBACKUPDIR/$LATEST_FULL
mkdir -p $TMPINCRDIR
# Find latest incremental backup.
LATEST_INCR=`find $TMPINCRDIR -mindepth 1 -maxdepth 1 -type d | sort -nr | head -1`
# If this is the first incremental, use the full as base. Otherwise, use the latest incremental as base.
if [ ! $LATEST_INCR ] ; then
INCRBASEDIR=$FULLBACKUPDIR/$LATEST_FULL
else
INCRBASEDIR=$LATEST_INCR
fi
echo "Running new incremental backup using $INCRBASEDIR as base."
innobackupex --defaults-file=$MYCNF $USEROPTIONS $compress $compression_threads --incremental $TMPINCRDIR --incremental-basedir $INCRBASEDIR > $TMPFILE 2>&1
fi
{%- endif %}
if [ -z "`tail -1 $TMPFILE | grep 'completed OK!'`" ] ; then
echo "$INNOBACKUPEX failed:"; echo
echo "---------- ERROR OUTPUT from $INNOBACKUPEX ----------"
cat $TMPFILE
#rm -f $TMPFILE
exit 1
fi
THISBACKUP=`awk -- "/Backup created in directory/ { split( \\\$0, p, \"'\" ) ; print p[2] }" $TMPFILE`
#rm -f $TMPFILE
echo "Databases backed up successfully to: $THISBACKUP"
echo
# rsync just the new or modified backup files
{%- if client.target is defined %}
echo "Adding ssh-key of remote host to known_hosts"
ssh-keygen -R {{ client.target.host }} 2>&1 | > $rsyncLog
ssh-keyscan {{ client.target.host }} >> ~/.ssh/known_hosts 2>&1 | >> $rsyncLog
echo "Rsyncing files to remote host"
/usr/bin/rsync -rhtPv --rsync-path=rsync --progress $BACKUPDIR/* -e ssh xtrabackup@{{ client.target.host }}:$SERVERBACKUPDIR >> $rsyncLog
# Check if the rsync succeeded or failed
if ! grep -q "rsync error: " $rsyncLog; then
echo "Rsync to remote host completed OK"
else
echo "Rsync to remote host FAILED"
exit 1
fi
{%- endif %}
# Cleanup
if [ $SKIPCLEANUP=false ] ; then
{%- if client.backup_times is not defined %}
echo "----------------------------"
echo "Cleanup. Keeping only $KEEP full backups and its incrementals."
AGE=$(($FULLBACKUPLIFE * $KEEP / 60))
find $FULLBACKUPDIR -maxdepth 1 -type d -mmin +$AGE -execdir echo "removing: "$FULLBACKUPDIR/{} \; -execdir rm -rf $FULLBACKUPDIR/{} \; -execdir echo "removing: "$INCRBACKUPDIR/{} \; -execdir rm -rf $INCRBACKUPDIR/{} \;
echo
echo "completed: `date`"
exit 0
{%- else %}
echo "----------------------------"
echo "Cleanup. Keeping only $KEEP full backups and its incrementals."
NUMBER_OF_FULL=$(( `find $FULLBACKUPDIR -maxdepth 1 -type d -print| wc -l` - 1))
FULL_TO_DELETE=$(( $NUMBER_OF_FULL - $KEEP ))
echo "Found $NUMBER_OF_FULL full backups and $KEEP should be kept. Thus $FULL_TO_DELETE will be deleted"
if [ $FULL_TO_DELETE -gt 0 ] ; then
cd $INCRBACKUPDIR
ls -t $FULLBACKUPDIR | tail -n -$FULL_TO_DELETE | xargs -d '\n' rm -rf
cd $FULLBACKUPDIR
ls -t | tail -n -$FULL_TO_DELETE | xargs -d '\n' rm -rf
else
echo "There are less full backups than required, not deleting anything."
fi
{%- endif %}
else
echo "----------------------------"
echo "--skip-cleanup parameter passed. Cleanup was not triggered"
fi