Francois Gouget : testbot/BackupVM: A script for backing up a Libvirt/ QEmu VM.

Alexandre Julliard julliard at winehq.org
Mon Apr 1 14:58:15 CDT 2019


Module: tools
Branch: master
Commit: 4061512746c8829a5eaaf62c85e604906704714f
URL:    https://source.winehq.org/git/tools.git/?a=commit;h=4061512746c8829a5eaaf62c85e604906704714f

Author: Francois Gouget <fgouget at 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 at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at 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




More information about the wine-cvs mailing list