Backup Scripts
Backup Bash Script für eine Liste von Servern auf Basis von Bash
Backup Bash Script für eine Liste von Servern auf Basis von Bash
backup.config
#!/bin/bash
##### basic configuration #######################################
# where is the list of servers? #
SERVERLIST=/usr/local/bin/backup/lists/backup.list #
MIXED_SERVERLIST=/usr/local/bin/backup/lists/backup.list.mixed #
MIXED_SERVERLIST_TMP=/usr/local/bin/backup/lists/backup.list.tmp#
#
# thread management #
THREAD_COUNT_FILE=/tmp/.backup.threads.tmp #
MAX_THREADS=5 #
LOCKFILE=/tmp/.backup.lock.tmp #
WAIT_FOR_FREE_THREAD=5 #
WAIT_FOR_LOCKFILE=2 #
#
# where should the backup be saved? #
DATA_PATH=/raid/backup #
#
# test if enough space is left? #
CHECK_HDMINFREE=true #
HDMINFREE=99 #
HDMINFREE_WARN=97 #
#
# administrator email address #
ADMIN_EMAIL=admin@digicol.de #
#
# tools dir #
TOOLS_DIR=tools/ #
#
#stopfile #
STOPFILE=/tmp/backup.stop #
#################################################################
##### configuration of rsync part ###############################
# excludelist of filepatterns – excludes them from backup #
EXCLUDES=‘–exclude=/proc ‚ #
EXCLUDES=$EXCLUDES‘–exclude=/tmp ‚ #
EXCLUDES=$EXCLUDES‘–exclude=/home/digicol/install ‚ #
EXCLUDES=$EXCLUDES‘–exclude=*Thumbs.db ‚ #
EXCLUDES=$EXCLUDES‘–exclude=*.vmdk ‚ #
#################################################################
##### configuration of rotate part ##############################
# remove garbage? #
# this only happens if the „days of backup“ have been changed #
COLLECT_GARBAGE=true #
#################################################################
##### configuration of statusmail part ##########################
STATS_MAILTEXT=/tmp/mail.backup #
STATS_MAIL_SUBJECT=“Statusmail dcbackup“ #
STATS_TMPSIZE=/tmp/.size #
#################################################################
##### functions for mailing errors ##############################
# $1 = subject ; $2 = mailtext #
# exit with error! #
mail_err() #
{ #
echo $2 | mail -s „BACKUP ERROR: $1“ $ADMIN_EMAIL #
exit 1 #
} #
#
mail_warn() #
{ #
echo $2 | mail -s „BACKUP WARNING: $1“ $ADMIN_EMAIL #
} #
#################################################################
backup-rotate
#!/bin/bash
# include the config file
. /usr/local/bin/backup/backup.config
# enough params?
if [ -n „$1“ ] && [ -n „$2“ ]; then
SERVER=“$1″
DAYS=“$2″
else
echo „Error: Usage $0 <fqdn-hostname> <days>“
logger „BACKUP Error: Usage $0 <fqdn-hostname> <days>“
mail_err „wrong usage of script $0“ „wrong usage of script $0. Server: $SERVER“
fi
# fix the DAYS var that we can start with the 0!
DAYS=$((DAYS-1))
# checks the minimum percentage of free space
if $CHECK_HDMINFREE; then
PERCENTAGE=`df $DATA_PATH | tail -n1 | awk ‚{print $4}‘ | sed -e ’s/%//g’`
if [ $PERCENTAGE -ge $HDMINFREE_WARN ]; then
echo „Warning: device nearly full ($PERCENTAGE%)“
logger „BACKUP Warning: device nearly full ($PERCENTAGE%)“
mail_warn „nearly out of space!“ „Warning: device nearly full ($PERCENTAGE%)“
fi
if [ $PERCENTAGE -ge $HDMINFREE ]; then
echo „Fatal: not enough space left on device for rotating backups“
logger „BACKUP Fatal: not enough space left on device for rotating backups“
mail_err „ran out of space!“ „Fatal: not enough space left on device ($DATA_PATH) for rotating backups. stopped at server: $SERVER“
fi
fi
# create a first directory if needed
if ! [ -d $DATA_PATH/$SERVER/daily.0 ] ; then
mkdir -p $DATA_PATH/$SERVER/daily.0
fi
echo „backup of $SERVER is being rotated“
logger „BACKUP backup of $SERVER is being rotated“
# deleting highest snapshot
if [ -d $DATA_PATH/$SERVER/daily.$DAYS ] ; then
rm -rf $DATA_PATH/$SERVER/daily.$DAYS
fi
# deleting garbage of old snapshots
if $COLLECT_GARBAGE; then
if [ `ls -d $DATA_PATH/$SERVER/daily.* | wc -l` -gt $((DAYS+1)) ]; then
NUM_TO_DELETE=`ls -d $DATA_PATH/$SERVER/daily.* | wc -l`
until [ $NUM_TO_DELETE -lt $((DAYS + 1)) ]; do
rm -r $DATA_PATH/$SERVER/daily.$NUM_TO_DELETE
NUM_TO_DELETE=$((NUM_TO_DELETE – 1))
done
fi
fi
# move all other backups one step higher
for OLD in `seq 1 $DAYS | tac` ; do
if [ -d $DATA_PATH/$SERVER/daily.$OLD ] ; then
NEW=$[ $OLD + 1 ]
# save timestamp
touch $DATA_PATH/.timestamp -r $DATA_PATH/$SERVER/daily.$OLD
mv $DATA_PATH/$SERVER/daily.$OLD $DATA_PATH/$SERVER/daily.$NEW
# return timestamp
touch $DATA_PATH/$SERVER/daily.$NEW -r $DATA_PATH/.timestamp
fi
done
# hardlink copy from level-0 to level-1 of the snapshot
if [ -d $DATA_PATH/$SERVER/daily.0 ] ; then
cp -al $DATA_PATH/$SERVER/daily.0 $DATA_PATH/$SERVER/daily.1
fi
# finished!
echo „$SERVER finished rotating“
logger „BACKUP $SERVER finished rotating“
backup-rsync
#!/bin/bash
# include the config file
. /usr/local/bin/backup/backup.config
# enough params?
if [ -n „$1“ ] && [ -n „$2“ ]; then
SERVER=“$1″
FOLDER=“$2″
echo $SERVER
echo $FOLDER
else
echo „Error: Usage $0 <hostname or ip> <folder>“
logger „BACKUP Error: Usage $0 <hostname or ip> <folder>“
mail_err „wrong usage of script $0“ „wrong usage of script $0. Server: $SERVER“
fi
# check if it is a windows machine
WIN=`expr match „$FOLDER“ ‚[:a-z]*//’`
if [ $WIN -eq 0 ]; then
SSH=‘-e ssh‘
fi
# checks the minimum percentage of free space
if $CHECK_HDMINFREE; then
PERCENTAGE=`df $DATA_PATH | tail -n1 | awk ‚{print $4}‘ | sed -e ’s/%//g’`
if [ $PERCENTAGE -ge $HDMINFREE_WARN ]; then
echo „Warning: device nearly full ($PERCENTAGE%)“
logger „BACKUP Warning: device nearly full ($PERCENTAGE%)“
mail_warn „nearly out of space!“ „Warning: device nearly full ($PERCENTAGE%)“
fi
if [ $PERCENTAGE -ge $HDMINFREE ]; then
echo „Fatal: not enough space left on device for syncing backups“
logger „BACKUP Fatal: not enough space left on device for syncing backups“
mail_err „ran out of space!“ „Fatal: not enough space left on device ($DATA_PATH) for syncing backups. stopped at server: $SERVER“
fi
fi
# create a first directory if needed
if ! [ -d $DATA_PATH/$SERVER/daily.0 ] ; then
mkdir -p $DATA_PATH/$SERVER/daily.0
fi
# create a excludes file if needed
if ! [ -f excludes/$SERVER ] ; then
touch excludes/$SERVER
fi
echo „backup from: $SERVER“
logger „BACKUP backup from: $SERVER“
rsync -avzR –numeric-ids $SSH –delete –delete-excluded $EXCLUDES –exclude-from=“excludes/$SERVER“ $EXTRAOPT $SERVER:$FOLDER $DATA_PATH/$SERVER/daily.0
# test the return of rsync
# 0 = no error
# 23 = harmless – why? i don’t know, really! simply believe it…
# 24 = harmless – why? i don’t know, really! simply believe it…
# 255 = harmless – why? i don’t know, really! simply believe it…
# everything else is absolutely bad!
ERROR_CODE=$?
if ! [ $ERROR_CODE = 24 -o $ERROR_CODE = 0 -o $ERROR_CODE = 23 -o $ERROR_CODE = 255 ] ; then
echo „error on $SERVER while rsync. error code: $ERROR_CODE“
logger „BACKUP error on $SERVER while rsync. error code: $ERROR_CODE“
# only send mail if it’s not a Windows machine.
# Somehow rsync always exits with error if it’s a Windows rsync client.
if [ $WIN -eq 0 ]; then
mail_warn „rsync error!“ „error on $SERVER ($FOLDER) while rsync. error code: $ERROR_CODE“
fi
fi
# touch path to save timestamp
touch $DATA_PATH/$SERVER/daily.0
# finished!
echo „$SERVER finished successfully“
logger „BACKUP $SERVER finished successfully“
# for safety
sync
# count down the threads file
while true ; do
if [ ! -f $LOCKFILE ] ; then
(set -C; : > $LOCKFILE) 2> /dev/null
if [ $? == 0 ] ; then
THREAD_COUNT=`cat $THREAD_COUNT_FILE`
echo $((THREAD_COUNT – 1)) > $THREAD_COUNT_FILE
rm $LOCKFILE
break
else
logger „BACKUP ERROR creating lockfile, trying again“
sleep $WAIT_FOR_LOCKFILE
fi
else
sleep $WAIT_FOR_LOCKFILE
fi
done
do_backup
#!/bin/bash -x
. backup.config
# touch a stopfile
logger „BACKUP touching Stopfile“
(set -C; : > $STOPFILE) 2> /dev/null
###############################################################################
# rotate #
###############################################################################
#hack for the oracle dumps of dc5projektor
#only leave 10 days of the dump
#deactivated because tar’ed dumps are way smaller
#so we can hold a few more days
# find $DATA_PATH/dc5projektor.digicol.local/daily.*/dot/oracle/expdp/ -name ‚*dc5projektor_*.dmp.gz‘ -mtime +10 -delete
#hack for the demo device. only leave the last (daily.0) dump to save space
#rm -rf „$DATA_PATH“/demo.digicol.local/daily.[!^0]*/dot/dc/device/
#hack for vmware server – leave only fullbackup of the vmware machines
#rm -rf „$DATA_PATH“/vmware.digicol.local/daily.[!^0]*/datastore2-slow/*
#rm -rf /raid/backup/vmware.digicol.local/daily.[!^0]*/vmfs/volumes/4d0a2233-30b397db-3d59-0024e874c898*
if [ „$1“ != „norotate“ ] ; then
while read LINE ; do
if [[ `echo $LINE | grep „^#####“` ]]; then
SERVER=`echo $LINE | awk ‚{print $2}’`
DAYS=`echo $LINE | awk ‚{print $4}’`
./backup-rotate $SERVER $DAYS
fi
done < $SERVERLIST
fi
###############################################################################
# end rotate #
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# rsync #
###############################################################################
# only to be shure that there’s no lockfile
if [ -f $LOCKFILE ] ; then rm $LOCKFILE ; fi
# clear the mixed serverlist
> $MIXED_SERVERLIST
> $MIXED_SERVERLIST_TMP
# reset the threads
echo „0“ > $THREAD_COUNT_FILE
# mix the serverlist
grep -Ev „^#|^$“ $SERVERLIST > $MIXED_SERVERLIST_TMP
perl „$TOOLS_DIR“randomize.pl $MIXED_SERVERLIST_TMP > $MIXED_SERVERLIST
rm $MIXED_SERVERLIST_TMP
# bring up the threads and sync!
while read IP DIR ; do
while true ; do
THREAD_COUNT=`cat $THREAD_COUNT_FILE`
if [ $THREAD_COUNT -lt $MAX_THREADS ] ; then
### lockfile handling
while true ; do
if [ ! -f $LOCKFILE ] ; then
(set -C; : > $LOCKFILE) 2> /dev/null
if [ $? == 0 ] ; then
./backup-rsync $IP $DIR &
### once again for shure
THREAD_COUNT=`cat $THREAD_COUNT_FILE`
echo $((THREAD_COUNT + 1)) > $THREAD_COUNT_FILE
rm $LOCKFILE
break
else
logger „BACKUP ERROR creating lockfile, trying again“
sleep $WAIT_FOR_LOCKFILE
fi
else
sleep $WAIT_FOR_LOCKFILE
fi
done
### end lockfile handling
break
else
sleep $WAIT_FOR_FREE_THREAD
fi
done
done < $MIXED_SERVERLIST
# remove stopfile only if no rsync process is running!
while true ; do
if [ `cat $THREAD_COUNT_FILE` -eq 0 ] ; then
logger „BACKUP removing Stopfile“
rm $STOPFILE
break
else
sleep 60
fi
done
###############################################################################
# end rsync #
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
# stats #
###############################################################################
echo „The directory sizes:“ > $STATS_MAILTEXT
echo „“ >> $STATS_MAILTEXT
for SERVER in `ls -d $DATA_PATH/*`; do
# manually exclude the demo server
if [ „$SERVER“ = $DATA_PATH“/demo.digicol.local“ ]; then continue; fi
if [ -f $SERVER/.size ]; then
OLDSIZE=`cat $SERVER/.size`
else
OLDSIZE=“0″
fi
du -sm $SERVER | awk ‚{print $1}‘ > $STATS_TMPSIZE
NEWSIZE=`cat $STATS_TMPSIZE`
if [ $NEWSIZE -gt $OLDSIZE ] ; then SIGN=“+“ ; else SIGN=““ ; fi
echo „Server: ${SERVER##*/}“ >> $STATS_MAILTEXT
echo „Total: „$NEWSIZE“M“ >> $STATS_MAILTEXT
echo „Diff: $SIGN$((NEWSIZE – OLDSIZE))M“ >> $STATS_MAILTEXT
echo „“ >> $STATS_MAILTEXT
mv $STATS_TMPSIZE $SERVER
done
cat „$STATS_MAILTEXT“ | mail -s „$STATS_MAIL_SUBJECT“ „$ADMIN_EMAIL“
#extra stats
bash „$TOOLS_DIR“stats
###############################################################################
# end stats #
###############################################################################
lists/backup.list
# please leave the structure of this file as-is
# it’s for your own good 😉
# if you remove a directory from the list you
# remove it manually from the filesystem too!
# example: rm -r /raid/backup/server/daily.*/path/
##### 10.20.0.1 days: 7 #####
10.20.0.1 /etc
##### tux-03.digicol.dc days: 7 #####
tux-03.digicol.dc /etc
tux-03.digicol.dc /root
tux-03.digicol.dc /var/www
tux-03.digicol.dc /var/subversion
tux-03.digicol.dc /var/spool/cron
##### db1.digicol.dc days: 4 #####
db1.digicol.dc /etc
db1.digicol.dc /root
db1.digicol.dc /var/spool/cron
db1.digicol.dc /backup
db1.digicol.dc /usr/local/bin
##### db2.digicol.dc days: 4 #####
db2.digicol.dc /etc
db2.digicol.dc /root
db2.digicol.dc /var/spool/cron
db2.digicol.dc /backup/
db2.digicol.dc /usr/local/bin
##### dcx.digicol.dc days: 7 #####
dcx.digicol.dc /etc
dcx.digicol.dc /root
dcx.digicol.dc /var/opt/dcx
### END ###
tools/randomize.pl
#!/usr/bin/perl -w
# Set the random seed
srand(time|$$);
# Suck in everything in the file.
@a = <>;
# Get random lines, write ‚em out, mark ‚em done.
while ( @a ) {
$choice = splice(@a, rand @a, 1);
print $choice;
}
tools/randomize.rb
#!/usr/bin/ruby
abort „usage: #{$0} <file>“ if ARGV.size != 1
#shuffle modified from „Programming in Ruby“ by Thomas and Hunt
# get the lines:
lines = IO.readlines(ARGV[0])
# pick a random line, remove it, and print it
lines.size.times do
print lines.delete_at(rand(lines.size))
end
tools/stats
#!/bin/bash
STATS_MAILTEXT=/tmp/stats.tmp
SERVERLIST=/usr/local/bin/backup/lists/backup.list
OFFSET=30
function getDate()
{
DAY=`date | awk ‚{print $3}’`
if [[ ${#DAY} -lt 2 ]] ; then
DATE=`date | awk ‚{print $2″ „$3}’`
else
DATE=`date | awk ‚{print $2″ „$3}’`
fi
}
function getStats()
{
echo „Harddisk:“ > $STATS_MAILTEXT
echo „`df -h`“ >> $STATS_MAILTEXT
echo „“ >> $STATS_MAILTEXT
echo „Times:“ >> $STATS_MAILTEXT
getDate
while read line ; do
if [[ `echo $line | grep „#####“` ]]; then
SERVER=`echo $line | awk ‚{print $2}’`
START_STRING=`grep „$DATE“ /var/log/messages | grep BACKUP | grep „backup from: $SERVER“`
END_STRING=`grep „$DATE“ /var/log/messages | grep BACKUP | grep „$SERVER finished successfully“`
START_TIME=`echo „$START_STRING“ | head -n1 | awk ‚{print $3}’`
START_TIME=`date -d $START_TIME +%s`
END_TIME=`echo „$END_STRING“ | tail -n1 | awk ‚{print $3}’`
END_TIME=`date -d $END_TIME +%s`
TIME_DIFF=$((END_TIME – START_TIME))
TIME_DIFF_TEMP=$((TIME_DIFF – OFFSET))
STRIPES=“|“
while true ; do
if [[ $TIME_DIFF_TEMP -gt $OFFSET ]] ; then
STRIPES=$STRIPES“-„
TIME_DIFF_TEMP=$((TIME_DIFF_TEMP – $OFFSET))
else
STRIPES=$STRIPES“>“
break
fi
done
if [[ $TIME_DIFF -le 60 ]] ; then
DIFF_TEXT=$TIME_DIFF“ seconds“
elif [[ $TIME_DIFF -gt 60 ]] && [[ $TIME_DIFF -le 120 ]] ; then
DIFF_TEXT=$((TIME_DIFF / 60))“ minute“
else
DIFF_TEXT=$((TIME_DIFF / 60))“ minutes“
fi
echo „$SERVER:“ >> $STATS_MAILTEXT
echo „$DIFF_TEXT“ >> $STATS_MAILTEXT
echo „$STRIPES“ >> $STATS_MAILTEXT
echo „“ >> $STATS_MAILTEXT
fi
done < $SERVERLIST
echo „`grep „$DATE“ /var/log/messages |
grep BACKUP |
sed -e „s/loo logger: BACKUP //g“ |
grep -v „testing VPN connection“ |
grep -v „VPN connection already established“ |
sed -e „s/$DATE //g“`“ >> $STATS_MAILTEXT
cat „$STATS_MAILTEXT“ | /bin/mail -s „Times for dcbackup“ „bastian@digicol.de„
# cat $STATS_MAILTEXT
rm $STATS_MAILTEXT
}
getStats
