egscripts/egdbback/egdbback-new.bak

473 lines
13 KiB
Bash
Executable file

#!/bin/bash
#
# egdbback, copyright 2005-2008 Egressive Limited, http://egressive.com
#
# this script runs periodic database backups...
#
#==========================================
# The Variables - these must be define
# before they're referenced
#==========================================
VERSION="0.3"
EGDB_NAME=`basename $0`
EGDB_DIR=/etc/egscripts/egdbback
EGDB_CMD=$EGDB_DIR/egdbback
EGDB_CONF_DIR=$EGDB_DIR/conf
SITE_CONF="$EGDB_CONF_DIR/site.conf"
# the appropriate mysql paths
MYDIR=/var/lib/mysql
#
# this provides values for MACHINE_NAME and EMAIL
. $SITE_CONF
#
LOGS=/var/log
LOG=$LOGS/egdbback.log
#==========================================
# Defaults - these shouldn't need changing
#==========================================
PERIODS="hourly daily weekly monthly yearly"
hourlyNUM="24"
dailyNUM="7"
weeklyNUM="4"
monthlyNUM="12"
yearlyNUM="7"
# required programs
MAIL=`which mail`
GREP=`which grep`
LS=`which ls`
ID=`which id`
DATE=`which date`
DF=`which df`
GZIP=`which gzip`
GZIPSUF=gz
RM=`which rm`
LN=`which ln`
CUT=`which cut`
AWK=`which awk`
# determine today's date
TODAY=`$DATE '+%Y-%m-%d-%a'`
# determine today's date
NOW=`$DATE '+%H-%M-%S'`
# a timestamp for logging purposes
TIMESTAMP=`$DATE '+%Y-%m-%d %H:%M.%S'`
# temporary holding point for email
TMP_DIR=/tmp
TMP_EMAIL=$TMP_DIR/$EGDB_NAME"_tmp_email_"$TODAY.$TIME
#
#==========================================
# MySQL vars...
MYTMP=$MYBKP/tmp-$DATE
MYDUMP=`which mysqldump`
MYSQL=`which mysql`
MYSHOW=`which mysqlshow`
# pattern for "ls" command to build list of
# pruneable backup files...
# -1t = 1 column, ordered by time of last mod
PRUNEABLES_CMD="ls -1t"
#
#==========================================
# The Functions - these have to be defined
# before they're called! Note, refer
# to them without the "()" and provide
# up to one argument, referred to as "$@"
# in the function...
#==========================================
#
# function to direct a message...
message() {
# a timestamp for logging purposes
timestamp
if test -w $LOG ; then
echo "$EGDB_NAME: $TIMESTAMP $@" >> $LOG
fi
if test -w $TMP_EMAIL ; then
echo "$EGDB_NAME: $TIMESTAMP $@" >> $TMP_EMAIL
fi
verbose "$TIMESTAMP $@"
}
#
# function to direct a message...
verbose() {
if test $VERBOSE ; then
echo "$@"
fi
}
#
# insert a blank line into the log and on the console
insert_blank() {
echo "" >> $TMP_EMAIL
verbose ""
}
#
# update date and time info..
today() {
# determine today's date
TODAY=`$DATE '+%Y-%m-%d-%a'`
}
#
#
now() {
# determine today's date
NOW=`$DATE '+%H-%M-%S'`
}
#
#
timestamp() {
# a timestamp for logging purposes
TIMESTAMP=`$DATE '+%Y-%m-%d %H:%M.%S'`
}
#
#
roll_tests() {
hourly_ROLL=0
daily_ROLL=0
weekly_ROLL=0
monthly_ROLL=0
# hour of the day 0-23
if test `$DATE '+%k'` -eq "0" ; then
message "it's time to create a new daily backup!"
hourly_ROLL=1
fi
# day of the week 0-6
if test `$DATE '+%w'` -eq "0" ; then
message "it's time to create a new weekly backup!"
daily_ROLL=1
fi
# day of month 0-31ish
if test `$DATE '+%w'` -eq "00" ; then
message "it's time to create a new monthly backup!"
weekly_ROLL=1
fi
# day of year 0-366ish
if test `$DATE '+%j'` -eq "000" ; then
message "it's time to create a new yearly backup!"
yearly_ROLL=1
fi
}
#
# create the temporary email file
create_tmp_email() {
if test -d $TMP_DIR ; then
if test -w $TMP_DIR ; then
touch $TMP_EMAIL 2>&1
else
error "Email tmp directory $TMP_DIR is not writable"
fi
else
error "Email tmp directory $TMP_DIR does not exist"
fi
if test -w $TMP_EMAIL ; then
message "created temporary email $TMP_EMAIL"
else
error "Failed to create temporary email $TMP_EMAIL"
fi
}
#
# send the contents of the temporary file to the
# designated report recipient
send_email_report() {
if test -f $TMP_EMAIL ; then
message "sending email report to $EMAIL_TO"
if test $ERROR_STATUS == 1 ; then
EMAIL_SUBJ="[ERROR] $MACHINE_NAME $DEFAULT_EMAIL_SUBJ"
else
EMAIL_SUBJ="[SUCCESS] $MACHINE_NAME $DEFAULT_EMAIL_SUBJ"
fi
# check space again to see how close things are to full
message "Printing disk space for reference purposes:"
DISK_SPACE=`$DF`
message "$DISK_SPACE"
# send it to each email address in the list...
for EMAIL_ADD in $EMAIL_TO
do
RES=`$MAIL -s "$EMAIL_SUBJ" $EMAIL_ADD < $TMP_EMAIL`
if test -z $RES ; then
if test $ERROR_STATUS == 1 ; then
message "Error email report successfully sent to $EMAIL_ADD"
else
message "Email report successfully sent to $EMAIL_ADD"
fi
else
if ! test $ERROR_STATUS == 1 ; then
error "Email report send to $EMAIL_ADD failed with this message: $RES"
fi
fi
done
if test -w $TMP_EMAIL ; then
$RM $TMP_EMAIL 2>&1
else
if ! test $ERROR_STATUS == 1 ; then
error "Failed to remove email message file, $TMP_EMAIL: permission denied."
fi
fi
if test -f $TMP_EMAIL ; then
error "Failed to remove email message file $TMP_EMAIL for an unknown reason"
else
message "successfully removed temporary email $TMP_EMAIL"
fi
else
if ! test $ERROR_STATUS == 1 ; then
error "Email message file, $TMP_EMAIL, does not exist."
fi
fi
}
#
# function to direct an error...
error() {
#
# recognise that the system experienced
# an error and send out a special email, and halt
# the program!
timestamp
ERROR_STATUS=1
#
# get current user details
USER_DETAILS=`$ID`
if test -w $LOG ; then
echo "$EGDB_NAME: **ERROR** $TIMESTAMP $@" >> $LOG
echo "$EGDB_NAME: user details - $USER_DETAILS" >> $LOG
fi
if test -w $TMP_EMAIL ; then
echo "$EGDB_NAME: **ERROR** $TIMESTAMP $@" >> $TMP_EMAIL
echo " user details: $USER_DETAILS" >> $LOG
fi
verbose "$TIMESTAMP **ERROR** $@"
verbose " user details: $USER_DETAILS"
send_email_report
exit 1
}
#
# build list of backup configuration files
get_confs() {
CONFS=`$LS -1 $EGDB_CONF_DIR/$CONF_ROOT*.conf 2>&1`
TEST=`echo $CONFS | $GREP -c "No such file or directory" -`
#TEST2=`echo $CONFS | $GREP -c "$EGDB_CONF_DIR" -`
if test $TEST == 1 ; then
error "No configuration files found with root $CONF_ROOT in $EGDB_CONF_DIR"
fi
#echo "CONFS = $CONFS"
}
#
# convert last monthly into daily, daily->weekly, weekly->monthly, monthly->yearly
roll_over() {
for PERIOD in $PERIODS
do
# Clean out old backups
verbose "sorting out $PERIOD"
# this subsitutes the PERIOD, e.g. hourly, weekly, etc. to build the variable compounded with NUM
BU_TO_KEEP=$[${PERIOD}NUM]
message "keeping last $BU_TO_KEEP backups"
PATTERN="$BU_DIR/$PERIOD-$DB.*"
# build the list, with the suffix...
PRUNEABLES=`$PRUNEABLES_CMD $PATTERN.$GZIPSUF`
message "pruning older files based on $PATTERN.$GZIPSUF"
message "pruneable files: $PRUNEABLES"
if test "$?" -eq "0" ; then
#
# set counter
NUM=0
# go through the list of files and remove those we don't want
for PRUNEABLE in $PRUNEABLES
do
NUM=$(($NUM + 1))
if test $NUM -gt $BU_TO_KEEP ; then
message "deleting $PRUNEABLE"
if test $DRY_RUN == 1 ; then
message "would delete $PRUNEABLE if this wasn't a dry run"
else
rm $PRUNEABLE 2>&1 > /dev/null
fi
else
message "keeping $PRUNEABLE"
fi
done
fi
done
}
# delete old backups
delete_old() {
#
if test -n $FROOT && test -n $BU_TO_KEEP ; then
# pattern to search for to build the list...
PATTERN="$BU_DIR/$FROOT.*"
# build the list, with the suffix...
PRUNEABLES=`$PRUNEABLES_CMD $PATTERN.$GZIPSUF`
if test "$?" -eq "0" ; then
message "pruning older files based on $PATTERN.$GZIPSUF"
message "keeping last $BU_TO_KEEP backups"
#
# set counter
NUM=0
# go through the list of files and remove those we don't want
for PRUNEABLE in $PRUNEABLES
do
NUM=$(($NUM + 1))
if test $NUM -gt $BU_TO_KEEP ; then
message "deleting $PRUNEABLE"
if test $DRY_RUN == 1 ; then
message "would delete $PRUNEABLE if this wasn't a dry run"
else
rm $PRUNEABLE 2>&1 > /dev/null
fi
else
message "keeping $PRUNEABLE"
fi
done
fi
else
message "keeping older backups, missing root_filename..."
fi
}
#
#
check_space() {
RES=`$DF -h`
#message "$RES"
TEST=0
PCENT="100%"
TEST=`echo "$RES" | $GREP -c "$PCENT"`
if test $TEST == 1 ; then
PART_LINE=`echo "$RES" | $GREP "$PCENT"`
# get the device name which is showing $PCENT
TEST=`echo $PART_LINE | $CUT -f 1 -d ' '`
# if it has a partition number in the name, it's probably a block
# device rather than, say, a CDROM...
TEST2=`echo $TEST | $GREP -c [0-9]`
echo "Test2 = $TEST2"
if test $TEST2 == 1 ; then
error "Partition $TEST is full - detail: $PART_LINE"
else
message "false alarm - it's only the $TEST volume..."
fi
fi
}
#
# this is where we do all the backup-related stuff - what ebu used to do...
do_backup() {
# first, find out what dbs there are
DBLIST=`$MYSHOW -u $USER -p$PASSWORD | $AWK '{ if ($2!~/^$/&&$2!="Databases") { print $2;}}'`
#message "List of databases: $DBLIST"
# for each db in the list, create a compressed backup in the right place
message "creating a backup of the MySQL databases on $MACHINE_NAME..."
for DB in $DBLIST
do
FROOT=$CONF_ROOT-$DB
now
today
FNAME=$FROOT.$TODAY.$NOW
# --opt is same as --add-drop-table --add-locks --all --extended-insert --quick --lock-tables
CMD="$MYDUMP -u $USER -p$PASSWORD --opt $DB"
# create the new backup!
if test $DRY_RUN == 1 ; then
message "I would be doing this (if it was not a dry run):"
message "$CMD > $BU_DIR/$FNAME"
else
message "backing up table $DB..."
message "running $MYDUMP with user $USER, creating $BU_DIR/$FNAME"
$CMD > $BU_DIR/$FNAME
if test -f $BU_DIR/$FNAME ; then
$GZIP $BU_DIR/$FNAME
else
error "backup sql file, $BU_DIR/$FNAME, doesn't exist!"
fi
fi
# delete old backups, so that only BU_TO_KEEP of them are stored at once
roll_over
delete_old
done
}
#========================================
# The Error Checking
#========================================
#
# first, check that all the necessary files and stuff are there and usable
#
# if the log doesn't exist, create it
if ! test -f $LOG ; then
message "creating non-existent log file $LOG"
RET=`touch $LOG 2>&1 /dev/null`
TEST=`echo $RET | $GREP -c "Permission denied" -`
if test $TEST == 1 ; then
error "Failed to create log file $LOG, user cannot write to that directory"
fi
fi
# checking whether it's there an is writable
if ! test -w $LOG ; then
error "Log file $LOG not writable"
fi
#========================================
# The Functional Part of the Script
#========================================
# set variable defaults
ERROR_STATUS=0 # initially assume no errors
VERBOSE=0 # initially assume quiet output
CONF_ROOT="hourly" # set it to something we know so we can test if it's changed
DRY_RUN=0 # assume this is not a dry run...
CURRENT_LINK="none"
MODE=help
# process any arguments
while test $# -ne 0 ; do # while there are arguments
message "argument: $1"
case $1 in
--help|-h|-?)
verbose "setting mode to HELP"
MODE=help
;;
--verbose|-v)
verbose "setting verbostity to ON"
VERBOSE=1
;;
--dryrun)
verbose "setting dryrun to ON"
DRY_RUN=1
;;
*)
verbose "unknown option: $1"
message "unknown option: $1"
MODE=help
;;
esac
shift
done
if ! test $MODE == "help" ; then
MODE=run
fi
#
# set things in motion based on the script arguments
case $MODE in
run)
create_tmp_email
get_confs
# run backup for each file
for BU_CONF in $CONFS
do
message "do backup with conf: $BU_CONF"
. $BU_CONF # grab the current variables for this conf file...
do_backup
# put a space in the email to separate tasks
insert_blank
done
#
# send the resulting email
send_email_report
;;
help)
if test $CONF_ROOT; then
echo "error: please specify a period for egbackup"
fi
echo ""
echo "$EGDB_NAME, version $VERSION, copyright 2005-7 Egressive Ltd, http://egressive.com"
echo "==================="
echo "usage: $EGDB_NAME"
echo "== options =="
echo "-v or --verbose - give extra feedback on progress to stdout"
echo "-c or --configroot config_filename - root for configuration filenames"
echo "--dryrun - do everything else, but *don't* delete anything or run the backup"
echo ""
;;
esac