Skip to content

Commit

Permalink
ENH Add backup rotation, credential file support
Browse files Browse the repository at this point in the history
New options -r (rotating backups, keeping up to n
previous files) and -c for providing a mysql
configuration file with credentials to be used.
Allows storing these in a protected file instead
of having to provide it via script parameter
(which might be logged somewhere) or requiring
interactive entry.

Using the --defaults-extra-file option for the
mysql clients requires some working around
quoting issues (hence eval). Moreover, the option
needs to be the first.
  • Loading branch information
dschneller committed Mar 12, 2015
1 parent 74fcf7f commit 9d3cfcd
Showing 1 changed file with 60 additions and 22 deletions.
82 changes: 60 additions & 22 deletions zabbix-mysql-dump
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# NEW: Parsing of commandline arguments implemented
# ENH: Try reverse lookup of IPs and include hostname/IP in filename
# REV: Stop if database password is wrong
#
#
# 0.7.0 (2014-10-02)
# ENH: Complete overhaul to make script work with lots of Zabbix versions
#
Expand Down Expand Up @@ -56,6 +56,7 @@ DBUSER="zabbix"
DBPASS=""
QUIET="no"
REVERSELOOKUP="yes"
GENERATIONSTOKEEP=0

#
# SHOW HELP
Expand All @@ -70,21 +71,27 @@ OPTIONS
-d database - Zabbix database name (default: $DBNAME)
-u user - MySQL user to access Zabbix database (default: $DBUSER)
-p password - MySQL user password (specify "-" for a prompt)
-c file - Use credentials from mysql options file. Overrides
any -u and -p options.
-o outputdir - output directory for the MySQL dump file
(default: $DUMPDIR)
-q - No output on succesful backup. For crontab use.
-n - Skip reverse lookup of IP address for host.
(default: $DUMPDIR)
-r num - Rotate backups, keeping up to num generations in the
output directory. Uses file name and data to match.
-q - No output on succesful backup. For crontab use.
-n - Skip reverse lookup of IP address for host.
EXAMPLE
$(basename $0) -h 1.2.3.4 -d zabbixdb -u zabbix -p test
$(basename $0) -d zabbixdb -u zabbix -p - -o /tmp
$(basename $0) -h 1.2.3.4 -d zabbixdb -c /etc/mysql-backup-credentials.cnf
EOF
exit 1
fi

#
# PARSE COMMAND LINE ARGUMENTS
#
while getopts ":h:d:u:p:o:qn" opt; do
while getopts ":h:d:u:p:o:r:qnc:" opt; do
case $opt in
h) DBHOST="$OPTARG" ;;
d) DBNAME="$OPTARG" ;;
Expand All @@ -93,34 +100,51 @@ while getopts ":h:d:u:p:o:qn" opt; do
o) DUMPDIR="$OPTARG" ;;
n) REVERSELOOKUP="no" ;;
q) QUIET="yes" ;;
r) GENERATIONSTOKEEP=$(printf '%.0f' "$OPTARG") ;;
c) CNFFILE="$OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
:) echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
esac
done

if [ "$DBPASS" = "" ]; then
echo "No password given" >&2
exit 1
fi
if [ "$CNFFILE" = "" ]; then
if [ "$DBPASS" = "" ]; then
echo "No password given" >&2
exit 1
fi

if [ "$DBPASS" = "-" ]; then
read -s -p "Enter MySQL password for user '$DBUSER' (input will be hidden): " DBPASS
echo ""
if [ "$DBPASS" = "-" ]; then
read -s -p "Enter MySQL password for user '$DBUSER' (input will be hidden): " DBPASS
echo ""
fi
else
if [ ! -r "${CNFFILE}" ]; then
echo "Cannot read configuration file ${CNFFILE}" >&2
exit 1
fi
fi

#
# CONSTANTS
#
MYSQL_CONN="-h ${DBHOST} -u ${DBUSER} -p${DBPASS} ${DBNAME}"
MYSQL_BATCH="mysql --batch --silent $MYSQL_CONN"
if [ "${CNFFILE}" == "" ]; then
MYSQL_CONN="-h ${DBHOST} -u ${DBUSER} -p${DBPASS} ${DBNAME}"
else
MYSQL_CONN="--defaults-extra-file=\"${CNFFILE}\" -h ${DBHOST}"
fi

# --defaults-extra-file must be first parameter
MYSQL_BATCH="mysql $MYSQL_CONN --batch --silent"
TABLES_CMD="${MYSQL_BATCH} -e \"SELECT table_name FROM information_schema.tables WHERE table_schema = '$DBNAME'\""

DBHOSTNAME="$DBHOST"
if [ "${REVERSELOOKUP}" == "yes" ]; then
# Try resolving a given host ip
newHostname=$(dig +noall +answer -x $DBHOST | sed -r 's/((\S+)\s+)+([^\.]+)\..*/\3/')
test \! -z "$newHostname" && DBHOSTNAME="$newHostname"
fi
DUMPFILEBASE="zabbix_${DBHOSTNAME}_$(date +%Y%m%d-%H%M).sql"
DUMPFILENAME_PREFIX="zabbix_cfg_${DBHOSTNAME}"
DUMPFILEBASE="${DUMPFILENAME_PREFIX}_$(date +%Y%m%d-%H%M).sql"
DUMPFILE="${DUMPDIR}/${DUMPFILEBASE}"

#
Expand Down Expand Up @@ -156,14 +180,14 @@ fi

#
# READ TABLE LIST from __DATA__ section at the end of this script
# (http://stackoverflow.com/a/3477269/2983301)
# (http://stackoverflow.com/a/3477269/2983301)
#
DATA_TABLES=()
while read line; do
table=$(echo "$line" | cut -d" " -f1)
echo "$line" | cut -d" " -f5 | grep -qi "DATA"
test $? -eq 0 && DATA_TABLES+=($table)
done < <(sed '0,/^__DATA__$/d' "$0" | tr -s " ")
done < <(sed '0,/^__DATA__$/d' "$0" | tr -s " ")

# paranoid check
if [ ${#DATA_TABLES[@]} -lt 5 ]; then
Expand All @@ -178,7 +202,7 @@ mkdir -p "${DUMPDIR}"

# Read table list from database
[ "${QUIET}" == "no" ] && echo "Fetching list of existing tables..."
DB_TABLES=$($MYSQL_BATCH -e "SELECT table_name FROM information_schema.tables WHERE table_schema = '$DBNAME'" 2>&1)
DB_TABLES=$(eval ${TABLES_CMD} 2>&1)
if [ $? -ne 0 ]; then echo -e "ERROR while trying to access database:\n$DB_TABLES" 2>&1; exit 1; fi
DB_TABLES=$(echo "$DB_TABLES" | sort)
DB_TABLE_NUM=$(echo "$DB_TABLES" | wc -l)
Expand All @@ -196,8 +220,12 @@ while read table; do
else
dump_opt="--extended-insert=FALSE"
fi
mysqldump --routines --opt --single-transaction --skip-lock-tables \
$dump_opt $MYSQL_CONN --tables ${table} >>"${DUMPFILE}"
TABLE_DUMP_CMD="mysqldump ${MYSQL_CONN} --routines --opt --single-transaction --skip-lock-tables $dump_opt $DBNAME --tables ${table}"

This comment has been minimized.

Copy link
@sm4sh1k

sm4sh1k Dec 10, 2015

The line 223 is not correct! In this line you are putting database name twice. So in the result script will fail with error trying to find table named as a database (zabbix in my case). The correct line should be like that:

TABLE_DUMP_CMD="mysqldump --routines --opt --single-transaction --skip-lock-tables $dump_opt ${MYSQL_CONN} --tables ${table}"
eval ${TABLE_DUMP_CMD} >> "${DUMPFILE}"
if [[ $? != 0 ]]; then
echo "Failed dumping table ${table}. Aborting." >&2;
exit 1;
fi
if [ "${QUIET}" == "no" ]; then
# show percentage
i=$((i+1)); i_percent=$(($i * 100 / $DB_TABLE_NUM))
Expand All @@ -207,14 +235,14 @@ while read table; do
if [ $(($i_percent % 2)) -eq 0 ]; then echo -n "."; fi
fi
fi
done <<<"$DB_TABLES"
done <<<"$DB_TABLES"

if [ "${QUIET}" == "no" ]; then
echo -e "\n"
echo "For the following large tables only the schema (without data) was stored:"
for table in "${PROCESSED_DATA_TABLES[@]}"; do echo " - $table"; done

echo
echo
echo "Compressing backup file..."
fi
gzip -f "${DUMPFILE}"
Expand All @@ -225,6 +253,16 @@ fi

[ "${QUIET}" == "no" ] && echo -e "\nBackup Completed:\n${DUMPFILE}.gz"

if [ ${GENERATIONSTOKEEP} -gt 0 ]; then
[ "${QUIET}" == "no" ] && echo "Removing old backups, keeping up to ${GENERATIONSTOKEEP}"
REMOVE_OLD_CMD="cd ${DUMPDIR} && ls -t ${DUMPFILENAME_PREFIX}* | /usr/bin/awk \"NR>${GENERATIONSTOKEEP}\" | xargs rm -f "
eval ${REMOVE_OLD_CMD}
if [ $? -ne 0 ]; then
echo "ERROR: Could not rotate old backups" >&2
exit 1
fi
fi

exit 0

################################################################################
Expand Down

0 comments on commit 9d3cfcd

Please sign in to comment.