Module: tools
Branch: master
Commit: c83123416c0f3e9d103bf9c3757c6b9a62e9479e
URL: https://source.winehq.org/git/tools.git/?a=commit;h=c83123416c0f3e9d103bf9c…
Author: Francois Gouget <fgouget(a)codeweavers.com>
Date: Mon Apr 1 03:44:12 2019 +0200
testbot/RestoreVM: A script to restore a Libirt/QEmu VM.
This allows restoring a VM from either a borg repository (potentially
remote), a set of compressed tar files or a directory created by the
--extract-only option.
RestoreVM automatically waits for the VM to not be in use before
restoring it, and minimises the time it takes to perform the final step.
RestoreVM --disk-only verifies that none of the Libvirt configuration
files changed and then only replaces the disks. This can be useful to
remove accumulated cruft from the qcow2 image files.
The VM can be renamed during restoration, in which case its uuid and
MAC address are also modified to avoid conflicts (but the guest
configuration such as hostname and IP address must be changed
manually).
With --extract-only RestoreVM will just extract the files to a
temporary directory and show the commands to use to manually put them
into place.
The --extract-only option can be combined with renaming the VM and the
directory will contain both sets of Libvirt configuration files which
allows comparing and verifying them.
In case of an error (such as if the VM is running and does not stop
within the imparted time) RestoreVM automatically switches to the
--extract-only mode. This allows continuing the restoration by passing
that directory as an argument once the issue has been resolved.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
testbot/scripts/RestoreVM | 711 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 711 insertions(+)
Diff: https://source.winehq.org/git/tools.git/?a=commitdiff;h=c83123416c0f3e9d103…
Module: tools
Branch: master
Commit: 4061512746c8829a5eaaf62c85e604906704714f
URL: https://source.winehq.org/git/tools.git/?a=commit;h=4061512746c8829a5eaaf62…
Author: Francois Gouget <fgouget(a)codeweavers.com>
Date: Mon Apr 1 03:43:53 2019 +0200
testbot/BackupVM: A script for backing up a Libvirt/QEmu VM.
The script supports backing up to either a borg repository, possibly
remote, the deduplication makes this ideal for backups; or tar files,
which is more suited for archival.
By default the script will refuse to perform the backup if it foundi
no snapshots. If that's expected pass the --no-snapshot option.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
---
testbot/scripts/BackupVM | 362 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 362 insertions(+)
diff --git a/testbot/scripts/BackupVM b/testbot/scripts/BackupVM
new file mode 100755
index 0000000..2cf3320
--- /dev/null
+++ b/testbot/scripts/BackupVM
@@ -0,0 +1,362 @@
+#!/bin/sh
+#
+# Creates a backup of a QEMU/LibVirt VM.
+#
+# Copyright 2013-2019 Francois Gouget
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+name0=`basename "$0"`
+
+etcdir="etc/libvirt/qemu"
+snapdir="var/lib/libvirt/qemu/snapshot"
+
+
+#
+# Generic helpers
+#
+
+error()
+{
+ echo "$name0:error:" "$@" >&2
+}
+
+warning()
+{
+ echo "$name0:warning:" "$@" >&2
+}
+
+opt_dry_run=""
+opt_verbose=""
+dry_run()
+{
+ [ -n "$opt_verbose$opt_dry_run" ] && echo "$@"
+ if [ -z "$opt_dry_run" ]
+ then
+ "$@"
+ fi
+}
+
+
+#
+# Process the command line
+#
+
+check_opt_val()
+{
+ option="$1"
+ var="$2"
+ argc="$3"
+
+ if [ -n "$var" ]
+ then
+ error "$option can only be specified once"
+ usage=2 # but continue processing options
+ fi
+ if [ $argc -eq 0 ]
+ then
+ error "missing value for $option"
+ usage=2
+ return 1
+ fi
+ return 0
+}
+
+opt_vm=""
+opt_suffix=""
+opt_snapshot="1"
+opt_borg=""
+opt_connect=""
+usage=""
+while [ $# -gt 0 ]
+do
+ arg="$1"
+ shift
+ case "$arg" in
+ --borg)
+ if check_opt_val "$arg" "$opt_borg" $#
+ then
+ opt_borg="$1"
+ shift
+ fi
+ ;;
+ --snapshot)
+ opt_snapshot="1"
+ ;;
+ --no-snapshot)
+ opt_snapshot=""
+ ;;
+ --connect)
+ if check_opt_val "$arg" "$opt_connect" $#
+ then
+ opt_connect="$1"
+ shift
+ fi
+ ;;
+ --verbose)
+ opt_verbose="1"
+ ;;
+ --dry-run)
+ opt_dry_run="1"
+ ;;
+ -\?|-h|--help)
+ usage=0
+ ;;
+ -*)
+ error "unknown option '$arg'"
+ usage=2
+ break
+ ;;
+ *)
+ if [ -z "$opt_vm" ]
+ then
+ opt_vm="$arg"
+ elif [ -z "$opt_suffix" ]
+ then
+ opt_suffix="$arg"
+ else
+ error "only one VM and suffix can be specified."
+ usage=2
+ fi
+ ;;
+ esac
+done
+
+if [ -z "$usage" ]
+then
+ if [ -z "$opt_vm" ]
+ then
+ error "you must specify the name of the VM to backup."
+ usage=2
+ fi
+ [ -n "$opt_connect" ] || opt_connect="qemu:///system"
+fi
+
+if [ -n "$usage" ]
+then
+ if [ "$usage" != "0" ]
+ then
+ error "try '$name0 --help' for more information"
+ exit $usage
+ fi
+ cat <<EOF
+Usage: $name0 [--no-snapshot] [--verbose] [--dry-run] [--help] [--borg REPO]
+ [--connect URI] VMNAME [SUFFIX]
+
+Creates a backup of the specified VM.
+This must be run as root on the VM host.
+
+Where:
+ VMNAME The name of the VM to backup.
+ SUFFIX A (short) string to add to the backup name. This can help
+ provide an indication of what features were added to this
+ backup.
+ --borg REPO The borg repository to backup to.
+ --no-snapshot It is ok for the VM to have no snapshot.
+ --connect URI Connect to the specified Libvirt server.
+ --verbose Show all the commands as they are being run.
+ --dry-run Show what would happen but do not extract or change anything.
+ --help, -h Shows this help message.
+EOF
+ exit 0
+fi
+
+
+#
+# Prepare the backup
+#
+
+get_vm_disk_images()
+{
+ _root="$1"
+ _vm="$2"
+ sed -e "s~^ *<source file='\([^']*\)' */> *\$~\\1~" -e t -e d "$_root$etcdir/$_vm.xml" "$_root$snapdir/$_vm"/*.xml | sort | uniq
+}
+
+get_symlink_closure()
+{
+ while read file
+ do
+ echo "$file"
+ while [ -h "$file" ]
+ do
+ target=`readlink "$file"`
+ case "$target" in
+ /*) file="$target" ;;
+ *) file=`dirname "$file"`"/$target" ;;
+ esac
+ echo "$file"
+ done
+ done
+}
+
+if [ ! -r "/$etcdir/$opt_vm.xml" ]
+then
+ error "the '/$etcdir/$opt_vm.xml' configuration file is not readable"
+ exit 2
+fi
+
+snapshots=`echo "/$snapdir/$opt_vm"/*.xml`
+if [ -n "$opt_snapshot" -a "$snapshots" = "/$snapdir/$opt_vm/*.xml" ]
+then
+ error "the '$opt_vm' VM does not seem to have snapshots! Use --no-snapshot if this is expected."
+ exit 2
+fi
+
+# Backup the symbolic links but also their targets!
+disks=`get_vm_disk_images "/" "$opt_vm" 2>/dev/null | get_symlink_closure | sort | uniq`
+if [ -z "$disks" ]
+then
+ error "could not find the disk images."
+ exit 2
+fi
+
+confs=`for file in "/$etcdir/$opt_vm.xml" $snapshots; do echo $file; done | get_symlink_closure`
+
+errors=""
+all_paths=""
+echo "$opt_vm uses the following files and directories:"
+for file in $confs $disks
+do
+ all_paths="$all_paths $file"
+ echo " $file"
+ if [ ! -r "$file" ]
+ then
+ error "'$file' is not readable."
+ errors=1
+ fi
+done
+[ -z "$errors" ] || exit 1
+echo
+
+
+#
+# Wait for the VM
+#
+
+fatal()
+{
+ error "$@"
+ exit 1
+}
+
+wait_for_vm()
+{
+ _vm="$1"
+ _timeout="$2"
+
+ while true
+ do
+ _state=`(virsh --connect "$opt_connect" list --all 2>/dev/null || echo " $_vm error") | sed -e "s/^.* $_vm *//" -e t -e d`
+ [ -z "$opt_dry_run" ] || _state="shut off"
+ case "$_state" in
+ "error")
+ echo "nolibvirt"
+ return
+ ;;
+ "")
+ echo "novm"
+ return
+ ;;
+ "shut off")
+ echo "off"
+ return
+ ;;
+ esac
+
+ echo "$_vm is not powered off ($_state). Waiting for up to ${_timeout}s..." >&2
+ _timeout=`expr $_timeout - 10`
+ if [ $_timeout -le 0 ]
+ then
+ echo "running"
+ return
+ fi
+ sleep 10
+ done
+}
+
+# Note that we don't actually care whether libvirt knows about the VM since
+# we know which files to backup anyway.
+case `wait_for_vm "$opt_vm" 120` in
+nolibvirt)
+ warning "Could not connect to libvirt. Assuming $opt_vm is not running."
+ ;;
+running)
+ fatal "$opt_vm is running. Try again when it has been powered off."
+ ;;
+esac
+
+
+#
+# Do the backup
+#
+
+nice=""
+which ionice >/dev/null && nice="ionice"
+which nice >/dev/null && nice="nice $nice"
+
+backup="libvirt-$opt_vm-`date +%Y%m%d`"
+[ -n "$opt_suffix" ] && backup="$backup-$opt_suffix"
+
+if [ -n "$opt_borg" ]
+then
+ borg_opts=""
+ [ -n "$opt_verbose" ] && borg_opts="$borg_opts --verbose"
+ dry_run $nice borg create --progress --stats -C lzma,9 \
+ "$opt_borg::$backup" $all_paths
+ rc_borg=$?
+ if [ $rc_borg -ne 0 ]
+ then
+ fatal "an error occurred while saving the VM (borg=$rc_borg)"
+ fi
+ echo "Saved $opt_vm to '$opt_borg::$backup'"
+
+else
+ zipcmd=pbzip2
+ which pbzip2 >/dev/null || zipcmd=bzip2
+
+ tar_opts=""
+ [ -n "$opt_verbose" ] && tar_opts="-v"
+ if [ -n "$opt_verbose$opt_dry_run" ]
+ then
+ echo "$nice tar cf - $tar_opts $all_paths | $nice $zipcmd -9 | $nice split -d -b 2146435072 - '$backup.tar.bz2.'"
+ fi
+
+ if [ -n "$opt_dry_run" ]
+ then
+ rc_tar=0
+ rc_zip=0
+ rc_split=0
+ else
+ ($nice tar cf - $tar_opts $all_paths; echo $? >"rc_tar" ) | \
+ ($nice $zipcmd -9; echo $? >"rc_zip") | \
+ $nice split -d -b 2146435072 - "$backup.tar.bz2."
+ rc_split=$?
+ rc_zip=`cat "rc_zip"`
+ rc_tar=`cat "rc_tar"`
+ rm -f "rc_tar" "rc_zip"
+ fi
+ if [ "$rc_tar" != "0" -o "$rc_zip" != "0" -o "$rc_split" != "0" ]
+ then
+ fatal "an error occurred while saving the VM (tar=$rc_tar zip=$rc_zip split=$rc_split)"
+ fi
+ echo "Saved $opt_vm to '$backup.tar.bz2.*'"
+fi
+
+case `wait_for_vm "$opt_vm" 0` in
+running)
+ fatal "$opt_vm is running. The backup may be bad."
+ ;;
+esac