Add pid file to prevent concurrent runs, add recovery for snapshot existing prior to backup, and add progress bar.

This commit is contained in:
GRMrGecko 2023-01-05 00:37:37 -06:00
parent 5886973eed
commit 45459c1e50
2 changed files with 109 additions and 43 deletions

View File

@ -6,6 +6,23 @@
# This also works with GlusterFS so long as your # This also works with GlusterFS so long as your
# volume is mounted. # volume is mounted.
# A file to prevent overlapping runs. This allows us to make assumptions
# that we're the only backup actively running, which allows us to recover
# if a snapshot exists before backing up.
PIDFILE="/tmp/backup-image.pid"
# If the pid file exists and process is running, exit.
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if ps -p "$PID" >/dev/null; then
echo "Backup process already running, exiting."
exit 1
fi
fi
# Create a new pid file for this process.
echo $BASHPID >"$PIDFILE"
# The borg repository we're backing up to. # The borg repository we're backing up to.
export BORG_REPO='/media/Storage/Backup/kvm' export BORG_REPO='/media/Storage/Backup/kvm'
# If you have a passphrase for your repository, # If you have a passphrase for your repository,
@ -22,6 +39,34 @@ PRUNE_OPTIONS="--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
# Allows providing an argument of a domain to specifically backup. # Allows providing an argument of a domain to specifically backup.
BACKUP_DOMAIN="$1" BACKUP_DOMAIN="$1"
# Failures should remove pid file and exit with status code 1.
fail() {
echo "$1"
rm "$PIDFILE"
exit 1
}
# If the domain is running, commit the changes saved to the snapshot to the image to finish the backup.
blockCommit() {
DOMSTATUS="$1"
DOMAIN="$2"
DEV="$3"
if [[ "$DOMSTATUS" == "running" ]]; then
echo "Commit changes for $DOMAIN ($DEV)"
virsh blockcommit \
"$DOMAIN" \
"$DEV" \
--active \
--verbose \
--pivot \
--delete
if [ $? -ne 0 ]; then
fail "Could not commit changes $DOMAIN ($DEV). This may be a major issue and VM may be broken now."
fi
fi
}
# I save the status in a temporary file so I can error out and exit if a failure occurs. # I save the status in a temporary file so I can error out and exit if a failure occurs.
DOMLIST_STATUS_TMP="/tmp/backup-image-domlist-tmp" DOMLIST_STATUS_TMP="/tmp/backup-image-domlist-tmp"
while read -r line; do while read -r line; do
@ -75,17 +120,35 @@ while read -r line; do
# If status has an error, exit. # If status has an error, exit.
if [ $status -ne 0 ]; then if [ $status -ne 0 ]; then
echo "Domain block listing failed" fail "Domain block listing failed"
exit 1
fi fi
# For each image we can backup, back it up. # For each image we can backup, back it up.
for ((i = 0; i < ${#DEVS[@]}; i++)); do for ((i = 0; i < ${#DEVS[@]}; i++)); do
DEV=${DEVS[$i]} DEV=${DEVS[$i]}
IMAGE=${IMAGES[$i]} IMAGE=${IMAGES[$i]}
IMAGEEXTENSION="${IMAGE##*.}"
IMAGESNAPSHOT="${IMAGE%.*}.backup"
IMAGENAME=$(basename "$IMAGE")
# If the domain is running, we need to snapshot the disk so we can backup cleanly. # If the domain is running, we need to snapshot the disk so we can backup cleanly.
if [[ "$DOMSTATUS" == "running" ]]; then if [[ "$DOMSTATUS" == "running" ]]; then
# If the snapshot file exists, we should commit changes before performing another snapshot.
# We are assuming that we created the snapshot here, and that concurrent runs are not possible.
if [ -e "$IMAGESNAPSHOT" ]; then
# Commit any blocks.
blockCommit "$DOMSTATUS" "$DOMAIN" "$DEV"
fi
# Its possible that the image extension was changed to backup if a snapshot was previously made.
# We assume it should be qcow2, and if that does not exist we will exit.
if [[ "$IMAGEEXTENSION" == "backup" ]]; then
IMAGE="${IMAGE%.*}.qcow2"
if ! [ -f "$IMAGE" ]; then
fail "Unable to determine image name."
fi
fi
echo "Creating snapshot for $DOMAIN ($DEV)" echo "Creating snapshot for $DOMAIN ($DEV)"
virsh snapshot-create-as --domain "$DOMAIN" \ virsh snapshot-create-as --domain "$DOMAIN" \
--name backup \ --name backup \
@ -95,15 +158,13 @@ while read -r line; do
--diskspec $DEV,snapshot=external --diskspec $DEV,snapshot=external
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to create snapshot for $DOMAIN ($DEV)" fail "Failed to create snapshot for $DOMAIN ($DEV)"
exit 1
fi fi
fi fi
# Backup the image. # Backup the image.
echo "Creating backup for $DOMAIN ($DEV)" echo "Creating backup for $DOMAIN ($DEV [$IMAGE])"
IMAGENAME=$(basename "$IMAGE") pv "$IMAGE" | borg create \
cat "$IMAGE" | pv | borg create \
--verbose \ --verbose \
--stats \ --stats \
--show-rc \ --show-rc \
@ -111,8 +172,9 @@ while read -r line; do
"::$DOMAIN-$DEV-{now}" - "::$DOMAIN-$DEV-{now}" -
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN ($DEV)" # Commit any blocks.
exit 1 blockCommit "$DOMSTATUS" "$DOMAIN" "$DEV"
fail "Failed to backup $DOMAIN ($DEV)"
fi fi
# Prune if options are configured. # Prune if options are configured.
@ -124,27 +186,14 @@ while read -r line; do
$PRUNE_OPTIONS $PRUNE_OPTIONS
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN ($DEV)" # Commit any blocks.
exit 1 blockCommit "$DOMSTATUS" "$DOMAIN" "$DEV"
fail "Failed to prune $DOMAIN ($DEV)"
fi fi
fi fi
# If the domain is running, commit the changes saved to the snapshot to the image to finish the backup. # Commit any blocks.
if [[ "$DOMSTATUS" == "running" ]]; then blockCommit "$DOMSTATUS" "$DOMAIN" "$DEV"
echo "Commit changes for $DOMAIN ($DEV)"
virsh blockcommit \
"$DOMAIN" \
"$DEV" \
--active \
--verbose \
--pivot \
--delete
if [ $? -ne 0 ]; then
echo "Could not commit changes $DOMAIN ($DEV). This may be a major issue and VM may be broken now."
exit 1
fi
fi
done done
# Backup the domain info. # Backup the domain info.
@ -156,8 +205,7 @@ while read -r line; do
"::$DOMAIN-xml-{now}" - "::$DOMAIN-xml-{now}" -
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN" fail "Failed to backup $DOMAIN"
exit 1
fi fi
# Prune if options are configured. # Prune if options are configured.
@ -169,8 +217,7 @@ while read -r line; do
$PRUNE_OPTIONS $PRUNE_OPTIONS
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN" fail "Failed to prune $DOMAIN"
exit 1
fi fi
fi fi
done < <( done < <(
@ -187,9 +234,10 @@ fi
# If status has an error, exit. # If status has an error, exit.
if [ $status -ne 0 ]; then if [ $status -ne 0 ]; then
echo "Domain listing failed" fail "Domain listing failed"
exit 1
fi fi
# Shrink repo. # Shrink repo.
borg compact borg compact
rm "$PIDFILE"

View File

@ -3,6 +3,21 @@
# This is for backing up Rados Block Device (Ceph) storage. # This is for backing up Rados Block Device (Ceph) storage.
# A file to prevent overlapping runs.
PIDFILE="/tmp/backup-image.pid"
# If the pid file exists and process is running, exit.
if [ -f "$PIDFILE" ]; then
PID=$(cat "$PIDFILE")
if ps -p "$PID" >/dev/null; then
echo "Backup process already running, exiting."
exit 1
fi
fi
# Create a new pid file for this process.
echo $BASHPID >"$PIDFILE"
# The pool in Ceph that you would like to backup. # The pool in Ceph that you would like to backup.
POOL="libvirt" POOL="libvirt"
# Pull images in pull from rbd driver. # Pull images in pull from rbd driver.
@ -20,6 +35,12 @@ export BORG_DELETE_I_KNOW_WHAT_I_AM_DOING=NO
# Set to empty string to disable pruning. # Set to empty string to disable pruning.
PRUNE_OPTIONS="--keep-daily 7 --keep-weekly 4 --keep-monthly 6" PRUNE_OPTIONS="--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
# Failures should remove pid file and exit with status code 1.
fail() {
echo "$1"
rm "$PIDFILE"
exit 1
}
for IMAGE in $IMAGES; do for IMAGE in $IMAGES; do
# Export volume to borg backup. # Export volume to borg backup.
@ -31,8 +52,7 @@ for IMAGE in $IMAGES; do
"::$IMAGE-{now}" - "::$IMAGE-{now}" -
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to backup $IMAGE" fail "Failed to backup $IMAGE"
exit 1
fi fi
# Prune if options are configured. # Prune if options are configured.
@ -44,8 +64,7 @@ for IMAGE in $IMAGES; do
$PRUNE_OPTIONS $PRUNE_OPTIONS
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN" fail "Failed to prune $DOMAIN"
exit 1
fi fi
fi fi
done done
@ -65,8 +84,7 @@ while read -r line; do
"::$DOMAIN-xml-{now}" - "::$DOMAIN-xml-{now}" -
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN" fail "Failed to backup $DOMAIN"
exit 1
fi fi
# Prune if options are configured. # Prune if options are configured.
@ -78,8 +96,7 @@ while read -r line; do
$PRUNE_OPTIONS $PRUNE_OPTIONS
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN" fail "Failed to prune $DOMAIN"
exit 1
fi fi
fi fi
done < <( done < <(
@ -96,9 +113,10 @@ fi
# If status has an error, exit. # If status has an error, exit.
if [ $status -ne 0 ]; then if [ $status -ne 0 ]; then
echo "Domain listing failed" fail "Domain listing failed"
exit 1
fi fi
# Shrink repo. # Shrink repo.
borg compact borg compact
rm "$PIDFILE"