Browse Source

first commit

master
GRMrGecko 1 year ago
commit
84f24980c7
  1. 19
      License.txt
  2. 3
      README.md
  3. 182
      kvm-backup-images.sh
  4. 96
      kvm-backup-rbd.sh

19
License.txt

@ -0,0 +1,19 @@
Copyright (c) 2022 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
README.md

@ -0,0 +1,3 @@
# KVM Backup Scripts
These are scripts I wrote to perform backup of KVM images in my homelab environment. I am publishing them here for the case it is useful to someone. These scripts backups to a (borg backup)[https://borgbackup.readthedocs.io/en/stable/index.html] archive, as that is my favorite backup software.

182
kvm-backup-images.sh

@ -0,0 +1,182 @@
#!/bin/bash
# Copyright (c) 2022 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
# This is for backing up block devices in virsh
# which use image files such as qcow2.
# This also works with GlusterFS so long as your
# volume is mounted.
# The borg repository we're backing up to.
export BORG_REPO='/media/Storage/Backup/kvm'
# If you have a passphrase for your repository,
# set it here or you can use bash to retrieve it.
# export BORG_PASSPHRASE=''
# Set to empty string to disable pruning.
PRUNE_OPTIONS="--keep-daily 7 --keep-weekly 4 --keep-monthly 6"
# 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"
while read -r line; do
# Extract the domain name and status from the line.
DOMAIN=$(echo $line | awk '{print $2}')
DOMSTATUS=$(echo $line | awk '{for (i=3; i<NF; i++) printf $i " "; if (NF>=3) print $NF}')
# If the domain is empty, its not needed.
if [ -z "$DOMAIN" ]; then
continue
fi
# Get the images that need backing up.
DEVS=()
IMAGES=()
BLKLIST_STATUS_TMP="/tmp/backup-image-blklist-tmp"
while read -r line; do
# Extract the device and image from the line.
DEV=$(echo $line | awk '{print $1}')
IMAGE=$(echo $line | awk '{for (i=2; i<NF; i++) printf $i " "; if (NF>=2) print $NF}')
# Ignore empty line or no image.
if [ -z "$IMAGE" ] || [[ "$IMAGE" == "-" ]]; then
continue
fi
# Ignore iso files.
if [[ "$IMAGE" =~ \.iso$ ]]; then
continue
fi
# This image needs backing up.
DEVS+=("$DEV")
IMAGES+=("$IMAGE")
done < <(
virsh domblklist $DOMAIN | tail -n +3
echo $? >$BLKLIST_STATUS_TMP
)
# Get status from the block listing.
status=1
if [ -f $BLKLIST_STATUS_TMP ]; then
status=$(cat $BLKLIST_STATUS_TMP)
rm $BLKLIST_STATUS_TMP
fi
# If status has an error, exit.
if [ $status -ne 0 ]; then
echo "Domain block listing failed"
exit 1
fi
# For each image we can backup, back it up.
for ((i = 0; i < ${#DEVS[@]}; i++)); do
DEV=${DEVS[$i]}
IMAGE=${IMAGES[$i]}
# If the domain is running, we need to snapshot the disk so we can backup cleanly.
if [[ "$DOMSTATUS" == "running" ]]; then
echo "Creating snapshot for $DOMAIN ($DEV)"
virsh snapshot-create-as --domain "$DOMAIN" \
--name backup \
--no-metadata \
--atomic \
--disk-only \
--diskspec $DEV,snapshot=external
if [ $? -ne 0 ]; then
echo "Failed to create snapshot for $DOMAIN ($DEV)"
exit 1
fi
fi
# Backup the image.
echo "Creating backup for $DOMAIN ($DEV)"
IMAGENAME=$(basename "$IMAGE")
cat "$IMAGE" | pv | borg create \
--verbose \
--stats \
--show-rc \
--stdin-name "$IMAGENAME" \
"::$DOMAIN-$DEV-{now}" -
if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN ($DEV)"
exit 1
fi
# Prune if options are configured.
if [ -n "$PRUNE_OPTIONS" ]; then
echo "Pruning backups for $DOMAIN ($DEV)"
borg prune --list \
--show-rc \
--glob-archives "$DOMAIN-$DEV-*" \
$PRUNE_OPTIONS
if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN ($DEV)"
exit 1
fi
fi
# If the domain is running, commit the changes saved to the snapshot to the image to finish the backup.
if [[ "$DOMSTATUS" == "running" ]]; then
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
# Backup the domain info.
echo "Backing up $DOMAIN xml"
virsh dumpxml "$DOMAIN" | borg create \
--verbose \
--stats \
--show-rc \
"::$DOMAIN-xml-{now}" -
if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN"
exit 1
fi
# Prune if options are configured.
if [ -n "$PRUNE_OPTIONS" ]; then
echo "Pruning backups for $IMAGE"
borg prune --list \
--show-rc \
--glob-archives "$DOMAIN-xml-*" \
$PRUNE_OPTIONS
if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN"
exit 1
fi
fi
done < <(
virsh list --all | tail -n +3
echo $? >$DOMLIST_STATUS_TMP
)
# Get status from the domain listing.
status=1
if [ -f $DOMLIST_STATUS_TMP ]; then
status=$(cat $DOMLIST_STATUS_TMP)
rm $DOMLIST_STATUS_TMP
fi
# If status has an error, exit.
if [ $status -ne 0 ]; then
echo "Domain listing failed"
exit 1
fi
# Shrink repo.
borg compact

96
kvm-backup-rbd.sh

@ -0,0 +1,96 @@
#!/bin/bash
# Copyright (c) 2022 Mr. Gecko's Media (James Coleman). http://mrgeckosmedia.com/
# This is for backing up Rados Block Device (Ceph) storage.
# The pool in Ceph that you would like to backup.
POOL="libvirt"
# Pull images in pull from rbd driver.
IMAGES=$(rbd -p $POOL ls)
# The borg repository we're backing up to.
export BORG_REPO='/media/Storage/Backup/kvm'
# If you have a passphrase for your repository,
# set it here or you can use bash to retrieve it.
# export BORG_PASSPHRASE=''
for IMAGE in $IMAGES; do
# Export volume to borg backup.
echo "Creating backup for $IMAGE"
rbd export $POOL/$IMAGE - | pv | borg create \
--verbose \
--stats \
--show-rc \
"::$IMAGE-{now}" -
if [ $? -ne 0 ]; then
echo "Failed to backup $IMAGE"
exit 1
fi
# If you want to prune backups, you can set the prune options.
echo "Pruning backups for $IMAGE"
borg prune --list \
--show-rc \
--glob-archives "$IMAGE-*" \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 6
if [ $? -ne 0 ]; then
echo "Failed prune $IMAGE"
exit 1
fi
done
# I save the status in a temporary file so I can error out and exit if a failure occurs.
DOMLIST_STATUS_TMP="/tmp/backup-rbd-domlist-tmp"
while read -r line; do
# Extract the domain name from the line.
DOMAIN=$(echo $line | awk '{print $2}')
# Backup the domain info.
echo "Backing up $DOMAIN xml"
virsh dumpxml "$DOMAIN" | borg create \
--verbose \
--stats \
--show-rc \
"::$DOMAIN-xml-{now}" -
if [ $? -ne 0 ]; then
echo "Failed to backup $DOMAIN"
exit 1
fi
# Prune if options are configured.
if [ -n "$PRUNE_OPTIONS" ]; then
echo "Pruning backups for $IMAGE"
borg prune --list \
--show-rc \
--glob-archives "$DOMAIN-xml-*" \
$PRUNE_OPTIONS
if [ $? -ne 0 ]; then
echo "Failed to prune $DOMAIN"
exit 1
fi
fi
done < <(
virsh list --all | tail -n +3
echo $? >$DOMLIST_STATUS_TMP
)
# Get status from the domain listing.
status=1
if [ -f $DOMLIST_STATUS_TMP ]; then
status=$(cat $DOMLIST_STATUS_TMP)
rm $DOMLIST_STATUS_TMP
fi
# If status has an error, exit.
if [ $status -ne 0 ]; then
echo "Domain listing failed"
exit 1
fi
# Shrink repo.
borg compact
Loading…
Cancel
Save