GRMrGecko
1 year ago
commit
84f24980c7
4 changed files with 300 additions and 0 deletions
-
19License.txt
-
3README.md
-
182kvm-backup-images.sh
-
96kvm-backup-rbd.sh
@ -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. |
@ -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. |
@ -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 |
@ -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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue