-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackup-vm
executable file
·235 lines (212 loc) · 6.59 KB
/
backup-vm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/bin/bash
VERSION=0.1
CONFIG=/etc/kvm-backup.conf
SCRIPT=$(basename "$0")
TIME_SHUTDOWN=60
VERBOSE=0
function usage {
echo "Usage: ${SCRIPT} [-t|-c|-h|-v] -n" 2>&1
echo ' -n virtual machine name'
echo ' -t time in seconds to wait virtual machine shutdown'
echo ' -c configuration file path'
echo ' -v verbose output'
echo ' -h shows this help and exit'
echo ' -V shows version and exit'
exit 1
}
if [[ ${#} -eq 0 ]]; then
usage
fi
# Define list of arguments expected in the input
optstring=":n:t:c:hvV"
while getopts ${optstring} arg; do
case "${arg}" in
n) vm="${OPTARG}" ;;
t) TIME_SHUTDOWN=${OPTARG} ;;
c) CONFIG=${OPTARG} ;;
h) usage ;;
v) VERBOSE=1 ;;
V) echo "Version: ${VERSION}"
exit 0
;;
\?)
echo "Invalid option: -${OPTARG}."
echo
usage
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
function log_message {
if [ "$1" = "error" ]; then
logger -t "${SCRIPT}" -p "local3.err" "$2"
if [ ${VERBOSE} -eq 1 ]; then
echo "$2"
fi
elif [ "$1" = "info" ]; then
logger -t "${SCRIPT}" -p "local3.info" "$2"
if [ ${VERBOSE} -eq 1 ]; then
echo "$2"
fi
fi
}
function get_qemu_version {
rpm_bin=$(/usr/bin/which rpm)
qemu_version=$(${rpm_bin} -q qemu-img |cut -d"-" -f3|cut -d"." -f1)
if [ "${qemu_version}" -ge 3 ]; then
qemu_options='-Wc -O qcow2'
log_message "info" "qemu version ${qemu_version} or higher detected"
log_message "info" "using options ${qemu_options}"
else
qemu_options='-c -O qcow2'
log_message "info" "qemu version ${qemu_version} or lower detected"
log_message "info" "using options ${qemu_options}"
fi
}
function check_vm_exists {
if ! virsh list --all | grep -q "${vm}"; then
log_message "error" "virtual machine ${vm} not found"
exit 1
else
log_message "info" "virtual machine ${vm} found"
fi
}
function load_config {
if [ ! -f "${CONFIG}" ];
then
echo "Configuration file doesn't exits"
log_message "error" "Configuration file doesn't exits ${CONFIG}"
exit 2
fi
log_message "info" "Loading configuration file ${CONFIG}"
# shellcheck source=/dev/null
source "${CONFIG}"
}
function prepare_dirs {
timestamp=$(date +%Y%m%d%H%M%S)
if [ ! -d "${NFSEXPORT}/${vm}-${timestamp}" ]; then
log_message "info" "Creating backup directory ${NFSEXPORT}/${vm}-${timestamp}"
if ! mkdir -p "${NFSEXPORT}/${vm}-${timestamp}"; then
log_message "error" "Error creating backup directory ${NFSEXPORT}/${vm}-${timestamp}"
exit 3
fi
fi
}
function make_config_backup {
log_message "info" "Creating configuration backup in ${NFSEXPORT}/${vm}-${timestamp}/${vm}.xml"
if ! virsh dumpxml "${vm}" > "${NFSEXPORT}/${vm}-${timestamp}/${vm}.xml"; then
log_message "error" "Error creating configuration backup in ${NFSEXPORT}/${vm}-${timestamp}/${vm}.xml"
exit 4
fi
}
function get_disks {
log_message "info" "Getting disks"
for disk in $(virsh domblklist --details "${vm}"|awk '/dev/ {print $4}' | xargs); do
disks+=("${disk}")
disks_names+=("${disk##*/}")
log_message "info" "Disks: ${disk} found for vm ${vm}"
done
}
function get_vgs {
log_message "info" "Getting volumes groups"
for disk in "${disks[@]}"; do
vgs+=( "$(lvdisplay -v "${disk}" 2>/dev/null|awk '/VG/ {print $3}')" )
disk_size+=( "$(lvdisplay -v "${disk}" 2>/dev/null|awk '/LV Size/ {print $3}'|awk -F"." '{print $1}')" )
done
for ((i=0; i<${#vgs[@]}; i++)); do
log_message "info" "Volume group ${vgs[$i]} found for disk ${disks[$i]} with size ${disk_size[$i]}"
done
}
function get_vm_status {
log_message "info" "Getting vm status"
vm_status=$(virsh domstate "${vm}")
}
function shutdown_vm {
get_vm_status
if [ "${vm_status}" = "shut off" ]; then
log_message "info" "Virtual machine ${vm} is ${vm_status}"
else
log_message "info" "Virtual machine ${vm} is ${vm_status}"
log_message "info" "Shutting down virtual machine ${vm}"
if ! virsh shutdown "${vm}" &>/dev/null; then
log_message "error" "Error shutting down virtual machine ${vm}"
exit 5
fi
log_message "info" "Waiting ${TIME_SHUTDOWN} seconds for virtual machine ${vm} to shutdown"
sleep "${TIME_SHUTDOWN}"
get_vm_status
fi
}
function start_vm {
log_message "info" "Starting virtual machine ${vm}"
if ! virsh start "${vm}" &>/dev/null; then
log_message "error" "Error starting virtual machine ${vm}"
exit 7
fi
}
function take_snapshot {
for ((i=0; i<${#disks[@]}; i++)); do
vg_free_space=$(vgdisplay "${vgs[$i]}" | awk '/Free/ {print $7}'|awk -F"." '{print $1}')
log_message "info" "Checking volume group ${vgs[$i]} for free space"
if [ "${vg_free_space}" -gt "${disk_size[$i]}" ]; then
log_message "info" "Volume group ${vgs[$i]} has enough free space to take snapshot"
if ! lvcreate -s -n "${disks_names[$i]}Snap" -L "${disk_size[$i]}G" "${disks[$i]}" &>/dev/null; then
log_message "error" "Error taking snapshot of virtual machine ${vm}"
exit 8
fi
else
log_message "error" "Volume group ${vgs[$i]} has not enough free space to take snapshot"
delete_snapshots
start_vm
exit 8
fi
done
snapshots=$(lvdisplay |grep Snap|awk '/Path/ {print $3}' |xargs)
start_vm
}
function delete_snapshots {
for snapshot in "${snapshots[@]}"; do
log_message "info" "Deleting snapshot ${snapshot}"
if ! lvremove -f "${snapshot}" &>/dev/null; then
log_message "error" "Error deleting snapshot ${snapshot}"
exit 9
fi
done
}
function convert_to_qcow2 {
for snap in ${snapshots}; do
snap_name=$(echo "${snap}" | awk -F"/" '{print $NF}')
log_message "info" "Converting snapshot ${snap} to qcow2"
log_message "info" "Using command: qemu-img convert ${qemu_options} ${snap} ${NFSEXPORT}/${vm}-${timestamp}/${snap_name}.qcow2"
if ! qemu-img convert "${qemu_options}" "${snap}" "${NFSEXPORT}/${vm}-${timestamp}/${snap_name}.qcow2"; then
log_message "error" "Error converting snapshot ${snap} to qcow2"
delete_snapshots
exit 10
fi
delete_snapshots
done
}
function main {
load_config
get_qemu_version
check_vm_exists
prepare_dirs
make_config_backup
get_disks
get_vgs
shutdown_vm
if [ "${vm_status}" = "shut off" ]; then
take_snapshot
convert_to_qcow2
else
log_message "info" "Virtual machine ${vm} is not shut off"
exit 6
fi
log_message "info" "Backup of virtual machine ${vm} completed"
exit 0
}
# Main Script
main