first commit
This commit is contained in:
commit
84f24980c7
19
License.txt
Normal file
19
License.txt
Normal file
@ -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
Normal file
3
README.md
Normal file
@ -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
Normal file
182
kvm-backup-images.sh
Normal file
@ -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
Normal file
96
kvm-backup-rbd.sh
Normal file
@ -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…
Reference in New Issue
Block a user