diff --git a/block/snapshot.c b/block/snapshot.c index f2f48f926a8f..8081616ae91b 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -31,6 +31,7 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qstring.h" #include "qemu/option.h" +#include "sysemu/block-backend.h" QemuOptsList internal_snapshot_opts = { .name = "snapshot", @@ -384,6 +385,16 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, return ret; } +static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs) +{ + if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { + return false; + } + + /* Include all nodes that are either in use by a BlockBackend, or that + * aren't attached to any node, but owned by the monitor. */ + return bdrv_has_blk(bs) || QLIST_EMPTY(&bs->parents); +} /* Group operations. All block drivers are involved. * These functions will properly handle dataplane (take aio_context_acquire @@ -399,7 +410,7 @@ bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs) AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - if (bdrv_is_inserted(bs) && !bdrv_is_read_only(bs)) { + if (bdrv_all_snapshots_includes_bs(bs)) { ok = bdrv_can_snapshot(bs); } aio_context_release(ctx); @@ -426,8 +437,9 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs, AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs) && - bdrv_snapshot_find(bs, snapshot, name) >= 0) { + if (bdrv_all_snapshots_includes_bs(bs) && + bdrv_snapshot_find(bs, snapshot, name) >= 0) + { ret = bdrv_snapshot_delete(bs, snapshot->id_str, snapshot->name, err); } @@ -455,7 +467,7 @@ int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs, AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs)) { + if (bdrv_all_snapshots_includes_bs(bs)) { ret = bdrv_snapshot_goto(bs, name, errp); } aio_context_release(ctx); @@ -481,7 +493,7 @@ int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs) AioContext *ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); - if (bdrv_can_snapshot(bs)) { + if (bdrv_all_snapshots_includes_bs(bs)) { err = bdrv_snapshot_find(bs, &sn, name); } aio_context_release(ctx); @@ -512,7 +524,7 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, if (bs == vm_state_bs) { sn->vm_state_size = vm_state_size; err = bdrv_snapshot_create(bs, sn); - } else if (bdrv_can_snapshot(bs)) { + } else if (bdrv_all_snapshots_includes_bs(bs)) { sn->vm_state_size = 0; err = bdrv_snapshot_create(bs, sn); } @@ -538,7 +550,7 @@ BlockDriverState *bdrv_all_find_vmstate_bs(void) bool found; aio_context_acquire(ctx); - found = bdrv_can_snapshot(bs); + found = bdrv_all_snapshots_includes_bs(bs) && bdrv_can_snapshot(bs); aio_context_release(ctx); if (found) { diff --git a/tests/qemu-iotests/044 b/tests/qemu-iotests/044 index 05ea1f49c5a4..8b2afa2a11e9 100755 --- a/tests/qemu-iotests/044 +++ b/tests/qemu-iotests/044 @@ -28,9 +28,6 @@ import struct import subprocess import sys -if sys.version_info.major == 2: - range = xrange - test_img = os.path.join(iotests.test_dir, 'test.img') class TestRefcountTableGrowth(iotests.QMPTestCase): diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index 081ccc8ac1d4..d94728e0804d 100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 @@ -21,9 +21,6 @@ import os, random, iotests, struct, qcow2, sys from iotests import qemu_img, qemu_io, image_size -if sys.version_info.major == 2: - range = xrange - test_img = os.path.join(iotests.test_dir, 'test.img') check_img = os.path.join(iotests.test_dir, 'check.img') diff --git a/tests/qemu-iotests/267 b/tests/qemu-iotests/267 new file mode 100755 index 000000000000..d37a67c01295 --- /dev/null +++ b/tests/qemu-iotests/267 @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# +# Test which nodes are involved in internal snapshots +# +# Copyright (C) 2019 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# Internal snapshots are (currently) impossible with refcount_bits=1 +_unsupported_imgopts 'refcount_bits=1[^0-9]' + +do_run_qemu() +{ + echo Testing: "$@" + ( + if ! test -t 0; then + while read cmd; do + echo $cmd + done + fi + echo quit + ) | $QEMU -nographic -monitor stdio -nodefaults "$@" + echo +} + +run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp | + _filter_generated_node_ids | _filter_imgfmt | _filter_vmstate_size +} + +size=128M + +run_test() +{ + _make_test_img $size + printf "savevm snap0\ninfo snapshots\nloadvm snap0\n" | run_qemu "$@" | _filter_date +} + + +echo +echo "=== No block devices at all ===" +echo + +run_test + +echo +echo "=== -drive if=none ===" +echo + +run_test -drive driver=file,file="$TEST_IMG",if=none +run_test -drive driver=$IMGFMT,file="$TEST_IMG",if=none +run_test -drive driver=$IMGFMT,file="$TEST_IMG",if=none -device virtio-blk,drive=none0 + +echo +echo "=== -drive if=virtio ===" +echo + +run_test -drive driver=file,file="$TEST_IMG",if=virtio +run_test -drive driver=$IMGFMT,file="$TEST_IMG",if=virtio + +echo +echo "=== Simple -blockdev ===" +echo + +run_test -blockdev driver=file,filename="$TEST_IMG",node-name=file +run_test -blockdev driver=file,filename="$TEST_IMG",node-name=file \ + -blockdev driver=$IMGFMT,file=file,node-name=fmt +run_test -blockdev driver=file,filename="$TEST_IMG",node-name=file \ + -blockdev driver=raw,file=file,node-name=raw \ + -blockdev driver=$IMGFMT,file=raw,node-name=fmt + +echo +echo "=== -blockdev with a filter on top ===" +echo + +run_test -blockdev driver=file,filename="$TEST_IMG",node-name=file \ + -blockdev driver=$IMGFMT,file=file,node-name=fmt \ + -blockdev driver=copy-on-read,file=fmt,node-name=filter + +echo +echo "=== -blockdev with a backing file ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img $size + +IMGOPTS="backing_file=$TEST_IMG.base" \ +run_test -blockdev driver=file,filename="$TEST_IMG.base",node-name=backing-file \ + -blockdev driver=file,filename="$TEST_IMG",node-name=file \ + -blockdev driver=$IMGFMT,file=file,backing=backing-file,node-name=fmt + +IMGOPTS="backing_file=$TEST_IMG.base" \ +run_test -blockdev driver=file,filename="$TEST_IMG.base",node-name=backing-file \ + -blockdev driver=$IMGFMT,file=backing-file,node-name=backing-fmt \ + -blockdev driver=file,filename="$TEST_IMG",node-name=file \ + -blockdev driver=$IMGFMT,file=file,backing=backing-fmt,node-name=fmt + +# A snapshot should be present on the overlay, but not the backing file +echo Internal snapshots on overlay: +$QEMU_IMG snapshot -l "$TEST_IMG" | _filter_date | _filter_vmstate_size + +echo Internal snapshots on backing file: +$QEMU_IMG snapshot -l "$TEST_IMG.base" | _filter_date | _filter_vmstate_size + +echo +echo "=== -blockdev with NBD server on the backing file ===" +echo + +IMGOPTS="backing_file=$TEST_IMG.base" _make_test_img $size +cat <= (3,6) else 1)' +then + python_usable=true +fi + default_machine=$($QEMU_PROG -machine help | sed -n '/(default)/ s/ .*//p') default_alias_machine=$($QEMU_PROG -machine help | \ sed -n "/(alias of $default_machine)/ { s/ .*//p; q; }") @@ -809,7 +815,12 @@ do start=$(_wallclock) if [ "$(head -n 1 "$source_iotests/$seq")" == "#!/usr/bin/env python" ]; then - run_command="$PYTHON $seq" + if $python_usable; then + run_command="$PYTHON $seq" + else + run_command="false" + echo "Unsupported Python version" > $seq.notrun + fi else run_command="./$seq" fi diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 445a1c23e0c1..9f418b4881a1 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -19,12 +19,15 @@ # standard filters # -# ctime(3) dates -# _filter_date() { - $SED \ - -e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/' + $SED -re 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/yyyy-mm-dd hh:mm:ss/' +} + +_filter_vmstate_size() +{ + $SED -r -e 's/[0-9. ]{5} [KMGT]iB/ SIZE/' \ + -e 's/[0-9. ]{5} B/ SIZE/' } _filter_generated_node_ids() diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 5d3da937e4f5..5805a79d9e86 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -277,3 +277,4 @@ 263 rw quick 265 rw auto quick 266 rw quick +267 rw auto quick snapshot diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b26271187c84..9fb5181c3dc4 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -35,6 +35,7 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) from qemu import qtest +assert sys.version_info >= (3,6) # This will not work if arguments contain spaces but is necessary if we # want to support the override options that ./check supports. @@ -250,10 +251,7 @@ def image_size(img): return json.loads(r)['virtual-size'] def is_str(val): - if sys.version_info.major >= 3: - return isinstance(val, str) - else: - return isinstance(val, str) or isinstance(val, unicode) + return isinstance(val, str) test_dir_re = re.compile(r"%s" % test_dir) def filter_test_dir(msg): @@ -935,12 +933,7 @@ def execute_test(test_function=None, else: # We need to filter out the time taken from the output so that # qemu-iotest can reliably diff the results against master output. - if sys.version_info.major >= 3: - output = io.StringIO() - else: - # io.StringIO is for unicode strings, which is not what - # 2.x's test runner emits. - output = io.BytesIO() + output = io.StringIO() logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))