Skip to content

Commit 6c6e540

Browse files
committed
backup for Libvirt/KVM VMs
1 parent c517054 commit 6c6e540

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

libvirt/vm-backup

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Author: Andrei Buzoianu <andrei@buzoianu.info>
4+
# 2019-04-10 Version 0.1
5+
#
6+
7+
# backup UID
8+
USER=backup
9+
10+
# backup GID
11+
GROUP=backup
12+
13+
# State file location
14+
STATEDIR=/tmp
15+
16+
# Prefix for mysqldump program
17+
VIRSH_PREFIX=/bin
18+
19+
# The DATE-parameter tells date in format YYYY.MM.DD.
20+
DATE=`date +20%y.%m.%d`
21+
22+
# The TIME-parameter tells time in format HHMMSS.
23+
TIME=`date +%H%M%S`
24+
25+
# Arguments
26+
if [ $# -ne 3 ]; then
27+
echo "Usage: ./vm-backup <backup-folder> <domain> <host>"
28+
exit 1
29+
fi
30+
31+
BACKUPDIR="$1"
32+
DOMAIN="$2"
33+
HOST="$3"
34+
35+
case "$HOST" in
36+
"localhost"|"127.0.0.1")
37+
LOCAL=true
38+
VIRSH_URI="qemu:///system"
39+
;;
40+
41+
*)
42+
LOCAL=false
43+
VIRSH_URI="qemu+ssh://$USER@$HOST/system"
44+
esac
45+
46+
#
47+
# Test if QEMU guest agent is connected
48+
#
49+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" qemu-agent-command "$DOMAIN" '{"execute":"guest-ping"}' > /dev/null 2>&1
50+
if [ $? -ne 0 ]; then
51+
echo "Guest agent is not responding!"
52+
echo
53+
USE_QEMU_AGENT=false
54+
else
55+
USE_QEMU_AGENT=true
56+
fi
57+
58+
#
59+
# Start the backup
60+
#
61+
echo "Beginning backup for $DOMAIN"
62+
echo
63+
echo "Starting backup of VM $DOMAIN to $BACKUPDIR/$DOMAIN/$DATE.$TIME"
64+
65+
if [ ! -f "$VIRSH_PREFIX/virsh" ]
66+
then
67+
echo "ERROR Couldn\'t locate the virsh command under $VIRSH_PREFIX"
68+
exit 1
69+
fi
70+
71+
if [ ! -d "$BACKUPDIR" ]
72+
then
73+
echo "Creating backup directory $BACKUPDIR"
74+
mkdir -p $BACKUPDIR
75+
fi
76+
77+
if [ ! -d "$BACKUPDIR/$DOMAIN/$DATE.$TIME" ]
78+
then
79+
echo "Creating backup subdirectories $BACKUPDIR/$DOMAIN/$DATE.$TIME"
80+
mkdir -p $BACKUPDIR/$DOMAIN/$DATE.$TIME
81+
fi
82+
83+
#
84+
# Get the list of targets (disks) and the image paths
85+
#
86+
declare -A disks
87+
eval $("$VIRSH_PREFIX"/virsh -c "$VIRSH_URI" domblklist "$DOMAIN" --details | awk '/^[[:space:]]*file[[:space:]]+disk/ {print "disks["$3"]="$4}')
88+
89+
#
90+
# Create the snapshot
91+
#
92+
if [ "$USE_QEMU_AGENT" = true ]
93+
then
94+
#
95+
# If dynamic_ownership is set to 0 in qemu.conf (Opennebula does that by default), we need to touch the snapshot file before creating the external snapshot,
96+
# and chown it to user and group defined in qemu.conf. Known workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1363634
97+
#
98+
for disk in ${!disks[@]}; do
99+
if [[ -f "${disks[$disk]}.backup" ]]; then
100+
echo "${disks[$disk]}.backup from $DOMAIN already exists"
101+
exit 1
102+
fi
103+
touch "${disks[$disk]}.backup"
104+
chown $USER:$GROUP "${disks[$disk]}.backup"
105+
done
106+
107+
DISKSPEC=""
108+
for disk in ${!disks[@]}; do
109+
DISKSPEC="$DISKSPEC --diskspec $disk,snapshot=external,file=${disks[$disk]}.backup"
110+
done
111+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" snapshot-create-as --domain "$DOMAIN" --disk-only --atomic --no-metadata --quiesce $DISKSPEC > /dev/null 2>&1
112+
if [ $? -ne 0 ]; then
113+
echo "Failed to create snapshot for $DOMAIN"
114+
exit 1
115+
fi
116+
117+
#
118+
# Copy disk images
119+
#
120+
for disk in ${!disks[@]}; do
121+
NAME=$(basename "${disks[$disk]}")
122+
rsync -ah --progress ${disks[$disk]} $BACKUPDIR/$DOMAIN/$DATE.$TIME/$NAME
123+
done
124+
125+
#
126+
# Merge changes back
127+
#
128+
for disk in ${!disks[@]}; do
129+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" blockcommit "$DOMAIN" "${disks[$disk]}.backup" --active --wait --pivot > /dev/null 2>&1
130+
if [ $? -ne 0 ]; then
131+
echo "Could not merge changes for disk ${disks[$disk]} of $DOMAIN. VM may be in invalid state."
132+
exit 1
133+
fi
134+
done
135+
136+
#
137+
# Cleanup left over backup images
138+
#
139+
for disk in ${!disks[@]}; do
140+
rm -f "${disks[$disk]}.backup"
141+
done
142+
143+
else
144+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" save "$DOMAIN" $STATEDIR/$DOMAIN-$DATE-$TIME-state-file > /dev/null 2>&1
145+
146+
#
147+
# Copy disk images
148+
#
149+
for disk in ${!disks[@]}; do
150+
NAME=$(basename "${disks[$disk]}")
151+
rsync -ah --progress ${disks[$disk]} $BACKUPDIR/$DOMAIN/$DATE.$TIME/$NAME
152+
done
153+
154+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" restore $STATEDIR/$DOMAIN-$DATE-$TIME-state-file > /dev/null 2>&1
155+
156+
#
157+
# Cleanup left over state-files
158+
#
159+
if [ "$LOCAL" = true ]
160+
then
161+
rm -rf $STATEDIR/$DOMAIN-$DATE-$TIME-state-file
162+
else
163+
ssh -q $USER@$HOST "rm -rf $STATEDIR/$DOMAIN-$DATE-$TIME-state-file"
164+
fi
165+
fi
166+
167+
#
168+
# Dump the configuration information
169+
#
170+
$VIRSH_PREFIX/virsh -c "$VIRSH_URI" dumpxml "$DOMAIN" >"$BACKUPDIR/$DOMAIN/$DATE.$TIME/$DOMAIN.xml" > /dev/null 2>&1
171+
chown $USER:$GROUP "$BACKUPDIR/$DOMAIN/$DATE.$TIME/$DOMAIN.xml"
172+
173+
echo "Finished backup"
174+
echo

0 commit comments

Comments
 (0)