mysql_ufs_snapshot.sh
#!/bin/sh
# Copyright (c) 2007 TrueStep
# Name: mysql_ufs_snapshot.sh
# Author: Rory Arms - http://www.TrueStep.com/
# CDate: 2007-11-17
# Description:
# 1. Unmounts and deletes old snapshot if found.
# 2. The mysql(1) monitor is used to flush & lock the database
# 3. Using MySQL's SYSTEM(), to generate new snapshot using mksnap_ffs(8)
# 4. Mount resulting snapshot read-only to make it available for backup
#
# Installation:
# 1. Make sure SNAP_MOUNT (see user knobs) directory exists.
# 2. Put this file in a local path, /usr/local/sbin probably.
# 3. Add a crontab(5) entry to execute this script as as often as desired.
# Example crontab entry for daily execution at 2:30:
# 30 2 * * * root /usr/local/sbin/mysql_ufs_snapshot.sh 1>/dev/null
#
# Tested with: FreeBSD 6.1, 6.2
# Deps: mksnap_ffs(8), mount(8), mdconfig(8), mysql(1), rm(1), awk(1)
# $Id: mysql_ufs_snapshot.sh,v 1.1.1.1 2007/11/26 21:35:12 rorya Exp $
# user knobs
SNAP_MOUNT="/mnt/mysql_snapshot" # where to mount read-only snapshot
MYSQL_ROOT="/var/db/mysql" # mysql root directory, where the data lives
MYSQL_PASSWORD="" # mysql password for the root user
AUTHOR="TrueStep"
NAME="mysql_ufs_snapshot"
VERSION="2"
SNAP_NAME="mysql_snap" # only change if you want a different snapshot filename
PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
print() {
echo "-> $*"
}
print_error() {
echo "Error: $*" > /dev/stderr
}
print_stderr() {
echo "$*" > /dev/stderr
}
banner() {
print "$NAME v$VERSION starting"
echo
}
prechecks() {
# Check FreeBSD version
if [ ! $(uname -r | awk -F. '{ print $1 }') -ge 5 ]; then
print_error "FreeBSD 5 or higher required"
exit 255
fi
# Make sure MySQL is installed
if [ ! -x $(which mysql) ]; then
print_error "mysql(1) monitor not found"
exit 255
fi
# Make sure this is the root user
if [ ! $(id -u) -eq 0 ]; then
print_error "You are not a superuser"
exit 255
fi
# Make sure mount point exists
if [ ! -e $SNAP_MOUNT ]; then
print_error "mount point $SNAP_MOUNT does not exist, exiting"
exit 255
fi
# Resolve partition mount and snapshot location from MYSQL_ROOT
SNAP_FS="/$(echo $MYSQL_ROOT | awk -F/ '{ print $2 }')"
SNAP_DIR="/$(echo $MYSQL_ROOT | awk -F/ '{ print $2 }')/.snap"
SNAP_PATH=${SNAP_DIR}/${SNAP_NAME}
# Make sure the .snap directory exists in the target filesystem
if [ ! -d $SNAP_DIR ]; then
print_error "$SNAP_DIR doesn't exist, are you sure this is a UFS2 partition?"
exit 255
fi
}
delete_oldsnap() {
# Check to see if there is an existing snapshot from the last time
# this was used
if [ -e $SNAP_PATH ]; then
print "Old snapshot found"
# unmount the snapshot
umount $SNAP_MOUNT
# Detach memory disk
print "Detaching memory disk"
mdconfig -d -u 4
# Delete snapshot
print "Deleting old snapshot"
rm -f $SNAP_PATH
# Check to make sure the memory disk detached successfully
if [ ! $? = 0 ]; then
print_error "memory disk did not detach, exiting"
exit 255
fi
print "Old snapshot deleted, continuing.. "
print
fi
}
create_snap() {
# Check to see if there is a MySQL password
if [ -z $MYSQL_PASSWORD ]; then
MYSQL_OPTIONS=""
else
MYSQL_OPTIONS="-p$MYSQL_PASSWORD"
fi
# Flush mysql and use SYSTEM() to call mksnap_ffs(8) to create a
# UFS2 snapshot
# This is done because the MySQL read lock is only held while the
# mysql(1) session is open.
print "Launching mysql(1) monitor to lock tables and generate snapshot... "
mysql $MYSQL_OPTIONS << SQL_MONITOR
FLUSH TABLES WITH READ LOCK;
SYSTEM mksnap_ffs /var '$SNAP_PATH';
UNLOCK TABLES;
EXIT
SQL_MONITOR
print "done"
# Check mysql(1) status code
if [ ! $? = 0 ]; then
print_error "mysql(1) failed to create snapshot, exiting"
exit 255
fi
}
mount_snap() {
# Attach snapshot file to a memory disk
print "Attaching snapshot to memory disk"
mdconfig -a -t vnode -o readonly -f $SNAP_PATH -u 4
# Check to make sure the snapshot attached
if [ ! $? = 0 ]; then
print_error "mdconfig(8) failed to attach snapshot $SNAP_PATH, exiting"
exit 255
fi
# Mount memory disk
mount -r /dev/md4 $SNAP_MOUNT
# Check to make sure the mount succeeded
if [ ! $? = 0 ]; then
print_error "mount(8) failed to mount snapshot, exiting"
exit 255
fi
print "Snapshot mounted at $SNAP_MOUNT"
}
print_usage() {
print_stderr "usage: $NAME [-h]"
print_stderr "Description: Creates UFS2 snapshot of MySQL data directory"
print_stderr "Then mounts the snapshot for further backup operations"
print_stderr "Dependencies:"
print_stderr "Deps: mksnap_ffs(8), mount(8), mdconfig(8), mysql(1), rm(1), awk(1)"
}
print_version() {
print_stderr "$NAME version $VERSION"
}
# main
# print usage if -h is specified
if [ ! -z $1 ]; then
case $1 in
-h|--help)
print_usage
;;
-v|-V|--version)
print_version
;;
esac
exit 1
fi
banner
prechecks
delete_oldsnap
create_snap
mount_snap
Generated by GNU enscript 1.6.4.