~canonical-platform-qa/ubuntu-qa-tools/practisubunit

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
#!/bin/sh
#
# Copyright (C) 2009 Canonical, Ltd.
# Author: Jamie Strandboge <jamie@canonical.com>
# License: GPLv3
#

set -e

ustconf="$HOME/.uqt-vm-tools.conf"
if [ -s "$ustconf" ]; then
    . "$ustconf"
else
    echo "Could not find '$ustconf'"
    exit 1
fi

# Find out our current directory
if [ -z "$UQT_VM_TOOLS" ]; then
    UQT_VM_TOOLS=$(dirname $(readlink -f $0))
fi

. $UQT_VM_TOOLS/libvm.sh
abort_if_root

using_snapshots() {
    cat << EOM
Once migrated, to start using snapshots:
$ vm-start -s vm1 vm2...	(creates a new snapshot)
$ vm-start vm1 vm2...		(used existing snapshot)
$ vm-stop -f vm1 vm2...		(discards snapshot)
$ vm-stop vm1 vm2...		(keeps snapshot)
$ vm-stop -u vm1 vm2...		(commits changes to pristine image)
EOM
}

help() {
    cat << EOM
Usage:	vm-use-snapshots [-f] vm1 vm2...

This will move all disk images in the specified VM from <path>/<disk>.qcow2
to <path>/<disk>.pristine.qcow2, then update the domain XML to use
cache='writeback' for all qemu qcow2 disks defined in the XML.  The libvirt
XML will still point to <path>/<disk>.qcow2, but when using 'vm-start -s',
qemu-img will create a new disk file (ie snapshot) at <path>/<disk>.qcow2
using <path>/<disk>.pristine.qcow2 as the backing store.

Specifying the '-f' will force conversion from a raw disk to a qcow2 with
snapshots without further confirmation and will delete the original raw
disk.
EOM
    using_snapshots
}

# update_disk_cache() updates all qemu disks of type 'qcow2' to use the
# 'writeback' cache method, for performance reasons. See the '-drive' option
# in 'man qemu'. This is the wame behavior as when using qemu -snapshot
# directly.
update_disk_cache() {
    tmp=`mktemp -d`
    err=
    virsh dumpxml "$1" | while read line ; do
        if echo "$line" | grep -q "<driver name='qemu' type='qcow2'" ; then
            echo "$line" | sed -e "s/cache='.*'//" -e "s#/># cache='writeback'/>#" >> "$tmp/xml"
            continue
        fi
        echo "$line" >> "$tmp/xml"
    done
    virsh define "$tmp/xml" > "$tmp/output" 2>&1 || {
        echo "Could not update domain:"
        cat "$tmp/output"
        cat "$tmp/xml"
        err="yes"
    }
    rm -rf "$tmp"
    if [ -n "$err" ]; then
        exit 1
    fi
}

convert_disk() {
    disk="$1"
    converted="$2"
    if qemu-img info "$disk" | grep -q "file format: raw" ; then
        if [ -f "$converted" ]; then
            echo "WARN: '$converted' already exists. Skipping" >&2
            return 1
        fi
        echo "INFO: Creating qcow2 from '$disk'"
        qemu-img convert -f raw "$disk" -O qcow2 "$converted"
        if qemu-img info "$converted" | grep "file format: qcow2" ; then
            echo "INFO: qcow2 created. Deleting '$disk'"
            rm -f "$disk"
        else
            echo "ERROR: could not convert disk" >&2
            return 1
        fi
    else
        echo "WARN: '$disk' is not in raw format. Skipping" >&2
        return 1
    fi
}

convert_xml() {
    machine="$1"
    disk="$2"
    converted="$3"
    tmp=`mktemp -d`
    err=
    echo "INFO: Updating xml to use '$converted' of type 'qcow2'"
    virsh dumpxml "$machine" | tr '\n' ' ' | sed "s#<driver name='qemu' type='raw'/>\( *\)<source file='$disk'/>#<driver name='qemu' type='qcow2'/>\\1<source file='$converted'/>#" > "$tmp/xml" || return 1
    virsh define "$tmp/xml" > "$tmp/output" 2>&1 || {
        echo "Could not update domain:"
        cat "$tmp/output"
        cat "$tmp/xml"
        err="yes"
    }
    rm -rf "$tmp"
    if [ -n "$err" ]; then
        return 1
    fi
}

if [ -z "$1" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ] ; then
    help
    exit 1
fi

force_conversion=
if [ "$1" = "-f" ] || [ "$1" = "--force" ] ; then
    force_conversion="yes"
    shift
fi

errors=""
performed_migration=""
for machine in "$@"; do
    disks=`get_disks $machine`
    if [ -z "$disks" ]; then
        echo "WARN: Could not find any disks. Skipping snapshot preparation." >&2
        errors="yes"
        continue
    fi

    if vm_running "$machine" ; then
        echo "WARN: '$machine' still running. Skipping snapshot preparation." >&2
        errors="yes"
        continue
    fi
    # go through once and make sure we can safely snapshot
    use_snapshot="yes"
    for d in $disks; do
        real=`readlink -f "$d"`
        dir=`dirname "$real"`
        bn=`basename "$real" .qcow2`
        pristine="$dir/$bn.pristine.qcow2"
        if [ ! -e "$real" ]; then
            echo "WARN: '$real' does not exist. Skipping snapshot preparation." >&2
            use_snapshot="no"
            break
        fi
        if ! echo "$real" | egrep -q '.qcow2$' ; then
            converted="`echo \"$real\" | sed 's/\.[a-zA-Z]\+$//'`.qcow2"
            if [ "$force_conversion" = "yes" ]; then
                if ! convert_disk "$real" "$converted" ; then
                    use_snapshot="no"
                    errors="yes"
                    break
                fi
                if ! convert_xml "$machine" "$real" "$converted" ; then
                    use_snapshot="no"
                    errors="yes"
                    break
                fi
                # update the disks for this machine, now that we have adjusted the XML
                disks=`get_disks $machine`
                if [ -z "$disks" ]; then
                    echo "WARN: Could not find any disks. Skipping snapshot preparation." >&2
                    use_snapshot="no"
                    errors="yes"
                    break
                fi
            else
                echo "WARN: File extension for '$real' is not 'qcow2'. Skipping snapshot preparation." >&2
                echo "" >&2
                echo "To convert, use something like:" >&2
                echo "$ qemu-img convert -f raw '$real' -O qcow2 '$converted'" >&2
                echo "$ qemu-img info '$converted'" >&2
                echo "$ rm -f '$real'" >&2
                echo "" >&2
                echo "Then update your domain XML accordingly." >&2
                use_snapshot="no"
                break
            fi
        fi
        if [ -e "$pristine" ]; then
            echo "WARN: '$pristine' already exists. Skipping snapshot preparation." >&2
            use_snapshot="no"
            break
        fi
    done

    if [ "$use_snapshot" = "yes" ]; then
        echo "Migrating '$machine'"
        for d in $disks; do
            real=`readlink -f "$d"`
            dir=`dirname "$real"`
            bn=`basename "$real" .qcow2`
            pristine="$dir/$bn.pristine.qcow2"
            echo "  updating disks to use '<driver name='qemu' type='qcow2' cache='writeback'/>"
            update_disk_cache "$machine"
            echo "  moving '$real' to '$pristine'"
            mv -f "$real" "$pristine"
        done
        performed_migration="yes"
    else
        errors="yes"
    fi
    echo ""
done

if [ "$performed_migration" = "yes" ]; then
    using_snapshots
fi

if [ "$errors" = "yes" ]; then
     exit 1
fi