--- /dev/null
+#!/bin/bash
+#
+# bpcdump
+# Copyright (C) 2008 by Titanium Mirror, Inc.
+# Author: R. Steve McKown <smckown@titaniummirror.com>
+#
+# Dumps the BPC VEID to external storage. It must have its storage on a
+# private filesystem mounted at /var/lib/vz/private/$VEID.
+#
+# A generalized version of this script should be created later. A recovery
+# option within this script should also be created.
+
+# CONSTANTS
+
+VEID=1158
+VEDEV=/dev/vg0/ve$VEID
+VEMNT=/var/lib/vz/private/$VEID
+EXTFS=/media/esata
+unset WRITEPAR
+INFO=/dev/null
+export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH
+SCRIPT_EXT="start stop mount umount"
+CONF_DIR=/etc/vz/conf
+
+# FUNCTIONS
+
+info()
+{
+ echo "$*" | tee -a "$INFO"
+}
+
+error()
+{
+ echo "$*" | tee -a "$INFO" >&2
+}
+
+restartve()
+{
+ if [ -n "$mounted" ]; then
+ info "remounting VEID $VEID"
+ mount $VEMNT && unset mounted
+ fi
+ if [ -n "$running" ]; then
+ info "restarting VEID $VEID"
+ vzctl start $VEID && unset running
+ if [ $? = 0 ]; then
+ info "VEID $VEID has been started"
+ else
+ error "VEID $VEID failed to start; backup continues"
+ fi
+ fi
+}
+
+cleanup()
+{
+ ret="$1"
+ shift
+ msg="$*"
+ [ -z "$ret" ] && ret=-1
+ [ -z "$msg" ] && msg=undef
+ restartve
+ if [ "$ret" = "0" ]; then
+ info "$(date)"
+ info "cleanup message: $msg"
+ info "exit $ret"
+ touch "$EXTVEIDFS/good"
+ else
+ error "$(date)"
+ error "cleanup message: $msg"
+ error "exit $ret"
+ touch "$EXTVEIDFS/bad"
+ fi
+ [ -n "$backupwarn" ] && info "WARNINGS FOUND" >> "$INFO"
+ exit $ret
+}
+
+# MAIN
+
+trap "cleanup 1 \"termination by signal\"" SIGINT SIGTERM
+
+if [ $(whoami) != "root" ]; then
+ cleanup 1 "script requires super-user privileges"
+fi
+
+set $(vzctl status $VEID)
+unset exist mounted running backupwarn
+[ "$3" = "exist" ] && exist=1
+[ "$4" = "mounted" ] && mounted=1
+[ "$5" = "running" ] && running=1
+
+if ! mount | grep -q "on $EXTFS"; then
+ cleanup 1 "$EXTFS is not mounted"
+else
+ info "$EXTFS is mounted"
+fi
+
+if [ -z "$exist" ]; then
+ cleanup 1 "VEID $VEID does not exist"
+else
+ info "VEID $VEID exists"
+fi
+
+if [ ! -d "$VEMNT" ]; then
+ cleanup 1 "mount point for VEID $VEID does not exist"
+else
+ info "VEID $VEID has private mount point"
+fi
+
+if ! grep -q "$VEMNT[ ]" /etc/fstab; then
+ cleanup 1 "mount point for VEID $VEID not in /etc/fstab"
+fi
+
+if ! mount | grep -q "on $VEMNT"; then
+ cleanup 1 "$VEMNT is not mounted"
+else
+ info "VEID $VEID is mounted on $VEMNT"
+fi
+
+if [ -n "$running" ]; then
+ info "stopping VEID $VEID"
+ vzctl stop $VEID
+fi
+
+# Copy the VE's configuration file into its /etc/vzdump directory, as vzdump
+# does (for consistency)
+for file in $(echo "$SCRIPT_EXT"); do
+ if [ -f "$file" ]; then
+ destdir="${VEMNT}/etc/vzdump"
+ mkdir -p "$destdir"
+ info "Preserve config file ${VEID}.${SCRIPT_EXT}"
+ cp "${CONF_DIR}/${VEID}.${SCRIPT_EXT}" "${destdir}/vps.${SCRIPT_EXT}"
+ fi
+done
+
+# Unmount the filesystem, first getting its size
+if mount | grep -q "on $VEMNT"; then
+ mounted=1 # duplicate; vzctl status told us...
+ srcblks=$(df -P "$VEMNT" | grep "$VEMNT" | awk '{ print $2 }')
+ # Add 5% buffer
+ t=$((srcblks / 20))
+ srcblks=$((srcblks + t))
+ info "VEID fs contains $srcblks blocks"
+ info "unmount VEID $VEID"
+ umount "$VEMNT"
+else
+ cleanup 1 "VEID private fs must be mounted to determine its size"
+fi
+
+# Before we begin writing, remove old backup dirs until we have room
+dstblks=$(df -P "$EXTFS" | grep "$EXTFS" | awk '{ print $4 }')
+while [ $dstblks -le $srcblks ]; do
+ info "Only $dstblks free on $EXTFS"
+ oldest=$(cd $EXTFS && eval ls -td "ve${VEID}*" 2>/dev/null | tail -1)
+ if [ -d "$EXTFS/$oldest" ]; then
+ info "Removing old backup $oldest from $EXTFS"
+ rm -rf "$EXTFS/$oldest"
+ else
+ cleanup 1 "Structure error on $EXTFS. Correct manually."
+ fi
+ dstblks=$(df -P "$EXTFS" | grep "$EXTFS" | awk '{ print $4 }')
+done
+if [ $dstblks -le $srcblks ]; then
+ cleanup 1 "out of space: need $srcblks KB, have $dstblks KB"
+fi
+info "Archive space ok: need $srcblks KB, have $dstblks KB"
+
+EXTVEIDFS="$EXTFS/ve$VEID-$(date +'%Y%m%d')"
+if [ -d "$EXTVEIDFS" ]; then
+ rm -rf "${EXTVEIDFS}.old"
+ mv "$EXTVEIDFS" "${EXTVEIDFS}.old"
+else
+ rm -rf "$EXTVEIDFS"
+fi
+mkdir "$EXTVEIDFS"
+date > "$EXTVEIDFS/begin"
+if ! cd "$EXTVEIDFS"; then
+ cleanup 1 "cannot change into $EXTVEIDFS directory"
+fi
+
+INFO="$EXTVEIDFS/info"
+cat > "$INFO" <<+EOF+
+Date: $(date)
+VEID: $VEID
+Volume: $VEDEV
+dd_rescue log: ddrlog
+dd_rescue bad blocks: ddrbb
+image file: image
+
++EOF+
+info "copy $VEID device $VEDEV to $EXTVEIDFS/image"
+time dd_rescue -Aqy 8192 -l "$EXTVEIDFS/ddrlog" -o "$EXTVEIDFS/ddrbb" \
+ $VEDEV "$EXTVEIDFS/image"
+ret=$?
+if [ "$ret" != "0" ]; then
+ backupwarn=1
+ error "WARNING: dd_rescue returned $ret"
+fi
+info "calculate md5sum for $VEDEV (src)"
+srcmd5=$(md5sum "$VEDEV" 2>&1 | awk '{ print $1 }')
+info "calculate md5sum for image (dst)"
+
+# We're done with the partition. We can restart the VE now.
+restartve
+
+# Continue on with archive and validation
+(cd "$EXTVEIDFS" && md5sum image >image.md5sum 2>&1 | awk '{ print $1 }')
+dstmd5=$(md5sum "$EXTVEIDFS/image" 2>&1 | awk '{ print $1 }')
+echo "$dstmd5 image.md5sum" > "$EXTVEIDFS/image.md5sum"
+info "$srcmd5 source md5sum"
+info "$dstmd5 dest md5sum"
+if [ "$srcmd5" != "$dstmd5" -o -z "$srcmd5" ]; then
+ backupwarn=1
+ error "WARNING: md5 signatures do not match"
+else
+ info "md5 signatures match"
+fi
+if [ -n "$WRITEPAR" ]; then
+ (cd "$EXTVEIDFS" && time par2 c img)
+ ret=$?
+ if [ "$ret" != "0" ]; then
+ backupwarn=1
+ info "WARNING: redundancy failed: par2 returned $ret"
+ fi
+ (cd "$EXTVEIDFS" && time par2 v img)
+ ret=$?
+ if [ "$ret" != "0" ]; then
+ backupwarn=1
+ info "WARNING: redundancy inexact: par2 returned $ret"
+ fi
+fi
+
+cleanup 0 "successful"
+
+# vi: set shiftwidth=4: