提交 4d07c720 编写于 作者: K Kevin Wolf

Merge remote-tracking branch 'mreitz/tags/pull-block-for-kevin-2015-11-11' into queue-block

Block patches from 2015-10-26 until 2015-11-11.

# gpg: Signature made Wed Nov 11 17:00:50 2015 CET using RSA key ID E838ACAD
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>"

* mreitz/tags/pull-block-for-kevin-2015-11-11:
  iotests: Check for quorum support in test 139
  qcow2: Fix qcow2_get_cluster_offset() for zero clusters
  iotests: Add tests for the x-blockdev-del command
  block: Add 'x-blockdev-del' QMP command
  block: Add blk_get_refcnt()
  mirror: block all operations on the target image during the job
  qemu-iotests: fix -valgrind option for check
  qemu-iotests: fix cleanup of background processes
Signed-off-by: NKevin Wolf <kwolf@redhat.com>
......@@ -189,6 +189,11 @@ static void drive_info_del(DriveInfo *dinfo)
g_free(dinfo);
}
int blk_get_refcnt(BlockBackend *blk)
{
return blk ? blk->refcnt : 0;
}
/*
* Increment @blk's reference count.
* @blk must not be null.
......
......@@ -384,6 +384,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
aio_context_release(replace_aio_context);
}
g_free(s->replaces);
bdrv_op_unblock_all(s->target, s->common.blocker);
bdrv_unref(s->target);
block_job_completed(&s->common, data->ret);
g_free(data);
......@@ -744,6 +745,9 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
block_job_release(bs);
return;
}
bdrv_op_block_all(s->target, s->common.blocker);
bdrv_set_enable_write_cache(s->target, true);
if (s->target->blk) {
blk_set_on_error(s->target->blk, on_target_error, on_target_error);
......
......@@ -312,7 +312,7 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
if (!offset)
return 0;
assert(qcow2_get_cluster_type(first_entry) != QCOW2_CLUSTER_COMPRESSED);
assert(qcow2_get_cluster_type(first_entry) == QCOW2_CLUSTER_NORMAL);
for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
......@@ -324,14 +324,16 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size,
return i;
}
static int count_contiguous_free_clusters(int nb_clusters, uint64_t *l2_table)
static int count_contiguous_clusters_by_type(int nb_clusters,
uint64_t *l2_table,
int wanted_type)
{
int i;
for (i = 0; i < nb_clusters; i++) {
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
if (type != QCOW2_CLUSTER_UNALLOCATED) {
if (type != wanted_type) {
break;
}
}
......@@ -554,13 +556,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
ret = -EIO;
goto fail;
}
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], QCOW_OFLAG_ZERO);
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
QCOW2_CLUSTER_ZERO);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_UNALLOCATED:
/* how many empty clusters ? */
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
QCOW2_CLUSTER_UNALLOCATED);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_NORMAL:
......
......@@ -3479,6 +3479,72 @@ fail:
qmp_output_visitor_cleanup(ov);
}
void qmp_x_blockdev_del(bool has_id, const char *id,
bool has_node_name, const char *node_name, Error **errp)
{
AioContext *aio_context;
BlockBackend *blk;
BlockDriverState *bs;
if (has_id && has_node_name) {
error_setg(errp, "Only one of id and node-name must be specified");
return;
} else if (!has_id && !has_node_name) {
error_setg(errp, "No block device specified");
return;
}
if (has_id) {
blk = blk_by_name(id);
if (!blk) {
error_setg(errp, "Cannot find block backend %s", id);
return;
}
if (blk_get_refcnt(blk) > 1) {
error_setg(errp, "Block backend %s is in use", id);
return;
}
bs = blk_bs(blk);
aio_context = blk_get_aio_context(blk);
} else {
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Cannot find node %s", node_name);
return;
}
blk = bs->blk;
if (blk) {
error_setg(errp, "Node %s is in use by %s",
node_name, blk_name(blk));
return;
}
aio_context = bdrv_get_aio_context(bs);
}
aio_context_acquire(aio_context);
if (bs) {
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) {
goto out;
}
if (bs->refcnt > 1 || !QLIST_EMPTY(&bs->parents)) {
error_setg(errp, "Block device %s is in use",
bdrv_get_device_or_node_name(bs));
goto out;
}
}
if (blk) {
blk_unref(blk);
} else {
bdrv_unref(bs);
}
out:
aio_context_release(aio_context);
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{
BlockJobInfoList *head = NULL, **p_next = &head;
......
......@@ -65,6 +65,7 @@ BlockBackend *blk_new_with_bs(const char *name, Error **errp);
BlockBackend *blk_new_open(const char *name, const char *filename,
const char *reference, QDict *options, int flags,
Error **errp);
int blk_get_refcnt(BlockBackend *blk);
void blk_ref(BlockBackend *blk);
void blk_unref(BlockBackend *blk);
const char *blk_name(BlockBackend *blk);
......
......@@ -1895,8 +1895,8 @@
# level and no BlockBackend will be created.
#
# This command is still a work in progress. It doesn't support all
# block drivers, it lacks a matching blockdev-del, and more. Stay
# away from it unless you want to help with its development.
# block drivers among other things. Stay away from it unless you want
# to help with its development.
#
# @options: block device options for the new device
#
......@@ -1904,6 +1904,34 @@
##
{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
##
# @x-blockdev-del:
#
# Deletes a block device that has been added using blockdev-add.
# The selected device can be either a block backend or a graph node.
#
# In the former case the backend will be destroyed, along with its
# inserted medium if there's any. The command will fail if the backend
# or its medium are in use.
#
# In the latter case the node will be destroyed. The command will fail
# if the node is attached to a block backend or is otherwise being
# used.
#
# One of @id or @node-name must be specified, but not both.
#
# This command is still a work in progress and is considered
# experimental. Stay away from it unless you want to help with its
# development.
#
# @id: #optional Name of the block backend device to delete.
#
# @node-name: #optional Name of the graph node to delete.
#
# Since: 2.5
##
{ 'command': 'x-blockdev-del', 'data': { '*id': 'str', '*node-name': 'str' } }
##
# @blockdev-open-tray:
#
......
......@@ -3946,8 +3946,8 @@ blockdev-add
Add a block device.
This command is still a work in progress. It doesn't support all
block drivers, it lacks a matching blockdev-del, and more. Stay away
from it unless you want to help with its development.
block drivers among other things. Stay away from it unless you want
to help with its development.
Arguments:
......@@ -3990,6 +3990,63 @@ Example (2):
<- { "return": {} }
EQMP
{
.name = "x-blockdev-del",
.args_type = "id:s?,node-name:s?",
.mhandler.cmd_new = qmp_marshal_x_blockdev_del,
},
SQMP
x-blockdev-del
------------
Since 2.5
Deletes a block device thas has been added using blockdev-add.
The selected device can be either a block backend or a graph node.
In the former case the backend will be destroyed, along with its
inserted medium if there's any. The command will fail if the backend
or its medium are in use.
In the latter case the node will be destroyed. The command will fail
if the node is attached to a block backend or is otherwise being
used.
One of "id" or "node-name" must be specified, but not both.
This command is still a work in progress and is considered
experimental. Stay away from it unless you want to help with its
development.
Arguments:
- "id": Name of the block backend device to delete (json-string, optional)
- "node-name": Name of the graph node to delete (json-string, optional)
Example:
-> { "execute": "blockdev-add",
"arguments": {
"options": {
"driver": "qcow2",
"id": "drive0",
"file": {
"driver": "file",
"filename": "test.qcow2"
}
}
}
}
<- { "return": {} }
-> { "execute": "x-blockdev-del",
"arguments": { "id": "drive0" }
}
<- { "return": {} }
EQMP
{
......
......@@ -11,7 +11,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
......@@ -46,7 +50,11 @@ read 512/512 bytes at offset 0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
Rebuilding refcount structure
......@@ -60,7 +68,11 @@ incompatible_features 0x0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x0
No errors were found on the image.
......@@ -79,7 +91,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x1
ERROR cluster 5 refcount=0 reference=1
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
......@@ -89,7 +105,11 @@ Data may be corrupted, or further writes to the image may corrupt it.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x0
No errors were found on the image.
*** done
......@@ -32,11 +32,17 @@ status=1 # failure is the default!
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
nbd_snapshot_img="nbd:unix:$nbd_unix_socket"
rm -f "${TEST_DIR}/qemu-nbd.pid"
_cleanup_nbd()
{
if [ -n "$NBD_SNAPSHOT_PID" ]; then
kill "$NBD_SNAPSHOT_PID"
local NBD_SNAPSHOT_PID
if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then
read NBD_SNAPSHOT_PID < "${TEST_DIR}/qemu-nbd.pid"
rm -f "${TEST_DIR}/qemu-nbd.pid"
if [ -n "$NBD_SNAPSHOT_PID" ]; then
kill "$NBD_SNAPSHOT_PID"
fi
fi
rm -f "$nbd_unix_socket"
}
......@@ -60,7 +66,6 @@ _export_nbd_snapshot()
{
_cleanup_nbd
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 &
NBD_SNAPSHOT_PID=$!
_wait_for_nbd
}
......@@ -68,7 +73,6 @@ _export_nbd_snapshot1()
{
_cleanup_nbd
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 &
NBD_SNAPSHOT_PID=$!
_wait_for_nbd
}
......
......@@ -57,7 +57,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
magic 0x514649fb
version 3
backing_file_offset 0x0
......@@ -215,7 +219,11 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
magic 0x514649fb
version 3
backing_file_offset 0x0
......
......@@ -31,7 +31,11 @@ Cache clean interval too big
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all
wrote 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
./common.config: Killed ( if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@";
fi )
incompatible_features 0x0
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
......
#!/usr/bin/env python
#
# Test cases for the QMP 'x-blockdev-del' command
#
# Copyright (C) 2015 Igalia, S.L.
# Author: Alberto Garcia <berto@igalia.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
import os
import iotests
import time
base_img = os.path.join(iotests.test_dir, 'base.img')
new_img = os.path.join(iotests.test_dir, 'new.img')
class TestBlockdevDel(iotests.QMPTestCase):
def setUp(self):
iotests.qemu_img('create', '-f', iotests.imgfmt, base_img, '1M')
self.vm = iotests.VM()
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(base_img)
if os.path.isfile(new_img):
os.remove(new_img)
# Check whether a BlockBackend exists
def checkBlockBackend(self, backend, node, must_exist = True):
result = self.vm.qmp('query-block')
backends = filter(lambda x: x['device'] == backend, result['return'])
self.assertLessEqual(len(backends), 1)
self.assertEqual(must_exist, len(backends) == 1)
if must_exist:
if node:
self.assertEqual(backends[0]['inserted']['node-name'], node)
else:
self.assertFalse(backends[0].has_key('inserted'))
# Check whether a BlockDriverState exists
def checkBlockDriverState(self, node, must_exist = True):
result = self.vm.qmp('query-named-block-nodes')
nodes = filter(lambda x: x['node-name'] == node, result['return'])
self.assertLessEqual(len(nodes), 1)
self.assertEqual(must_exist, len(nodes) == 1)
# Add a new BlockBackend (with its attached BlockDriverState)
def addBlockBackend(self, backend, node):
file_node = '%s_file' % node
self.checkBlockBackend(backend, node, False)
self.checkBlockDriverState(node, False)
self.checkBlockDriverState(file_node, False)
opts = {'driver': iotests.imgfmt,
'id': backend,
'node-name': node,
'file': {'driver': 'file',
'node-name': file_node,
'filename': base_img}}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockBackend(backend, node)
self.checkBlockDriverState(node)
self.checkBlockDriverState(file_node)
# Add a BlockDriverState without a BlockBackend
def addBlockDriverState(self, node):
file_node = '%s_file' % node
self.checkBlockDriverState(node, False)
self.checkBlockDriverState(file_node, False)
opts = {'driver': iotests.imgfmt,
'node-name': node,
'file': {'driver': 'file',
'node-name': file_node,
'filename': base_img}}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(file_node)
# Add a BlockDriverState that will be used as overlay for the base_img BDS
def addBlockDriverStateOverlay(self, node):
self.checkBlockDriverState(node, False)
iotests.qemu_img('create', '-f', iotests.imgfmt,
'-b', base_img, new_img, '1M')
opts = {'driver': iotests.imgfmt,
'node-name': node,
'backing': '',
'file': {'driver': 'file',
'filename': new_img}}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
# Delete a BlockBackend
def delBlockBackend(self, backend, node, expect_error = False,
destroys_media = True):
self.checkBlockBackend(backend, node)
if node:
self.checkBlockDriverState(node)
result = self.vm.qmp('x-blockdev-del', id = backend)
if expect_error:
self.assert_qmp(result, 'error/class', 'GenericError')
if node:
self.checkBlockDriverState(node)
else:
self.assert_qmp(result, 'return', {})
if node:
self.checkBlockDriverState(node, not destroys_media)
self.checkBlockBackend(backend, node, must_exist = expect_error)
# Delete a BlockDriverState
def delBlockDriverState(self, node, expect_error = False):
self.checkBlockDriverState(node)
result = self.vm.qmp('x-blockdev-del', node_name = node)
if expect_error:
self.assert_qmp(result, 'error/class', 'GenericError')
else:
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node, expect_error)
# Add a device model
def addDeviceModel(self, device, backend):
result = self.vm.qmp('device_add', id = device,
driver = 'virtio-blk-pci', drive = backend)
self.assert_qmp(result, 'return', {})
# Delete a device model
def delDeviceModel(self, device):
result = self.vm.qmp('device_del', id = device)
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('system_reset')
self.assert_qmp(result, 'return', {})
device_path = '/machine/peripheral/%s/virtio-backend' % device
event = self.vm.event_wait(name="DEVICE_DELETED",
match={'data': {'path': device_path}})
self.assertNotEqual(event, None)
event = self.vm.event_wait(name="DEVICE_DELETED",
match={'data': {'device': device}})
self.assertNotEqual(event, None)
# Remove a BlockDriverState
def ejectDrive(self, backend, node, expect_error = False,
destroys_media = True):
self.checkBlockBackend(backend, node)
self.checkBlockDriverState(node)
result = self.vm.qmp('eject', device = backend)
if expect_error:
self.assert_qmp(result, 'error/class', 'GenericError')
self.checkBlockDriverState(node)
self.checkBlockBackend(backend, node)
else:
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node, not destroys_media)
self.checkBlockBackend(backend, None)
# Insert a BlockDriverState
def insertDrive(self, backend, node):
self.checkBlockBackend(backend, None)
self.checkBlockDriverState(node)
result = self.vm.qmp('blockdev-insert-medium',
device = backend, node_name = node)
self.assert_qmp(result, 'return', {})
self.checkBlockBackend(backend, node)
self.checkBlockDriverState(node)
# Create a snapshot using 'blockdev-snapshot-sync'
def createSnapshotSync(self, node, overlay):
self.checkBlockDriverState(node)
self.checkBlockDriverState(overlay, False)
opts = {'node-name': node,
'snapshot-file': new_img,
'snapshot-node-name': overlay,
'format': iotests.imgfmt}
result = self.vm.qmp('blockdev-snapshot-sync', conv_keys=False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(overlay)
# Create a snapshot using 'blockdev-snapshot'
def createSnapshot(self, node, overlay):
self.checkBlockDriverState(node)
self.checkBlockDriverState(overlay)
result = self.vm.qmp('blockdev-snapshot',
node = node, overlay = overlay)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(overlay)
# Create a mirror
def createMirror(self, backend, node, new_node):
self.checkBlockBackend(backend, node)
self.checkBlockDriverState(new_node, False)
opts = {'device': backend,
'target': new_img,
'node-name': new_node,
'sync': 'top',
'format': iotests.imgfmt}
result = self.vm.qmp('drive-mirror', conv_keys=False, **opts)
self.assert_qmp(result, 'return', {})
self.checkBlockBackend(backend, node)
self.checkBlockDriverState(new_node)
# Complete an existing block job
def completeBlockJob(self, backend, node_before, node_after):
self.checkBlockBackend(backend, node_before)
result = self.vm.qmp('block-job-complete', device=backend)
self.assert_qmp(result, 'return', {})
self.wait_until_completed(backend)
self.checkBlockBackend(backend, node_after)
# Add a BlkDebug node
# Note that the purpose of this is to test the x-blockdev-del
# sanity checks, not to create a usable blkdebug drive
def addBlkDebug(self, debug, node):
self.checkBlockDriverState(node, False)
self.checkBlockDriverState(debug, False)
image = {'driver': iotests.imgfmt,
'node-name': node,
'file': {'driver': 'file',
'filename': base_img}}
opts = {'driver': 'blkdebug',
'node-name': debug,
'image': image}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(node)
self.checkBlockDriverState(debug)
# Add a BlkVerify node
# Note that the purpose of this is to test the x-blockdev-del
# sanity checks, not to create a usable blkverify drive
def addBlkVerify(self, blkverify, test, raw):
self.checkBlockDriverState(test, False)
self.checkBlockDriverState(raw, False)
self.checkBlockDriverState(blkverify, False)
iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
node_0 = {'driver': iotests.imgfmt,
'node-name': test,
'file': {'driver': 'file',
'filename': base_img}}
node_1 = {'driver': iotests.imgfmt,
'node-name': raw,
'file': {'driver': 'file',
'filename': new_img}}
opts = {'driver': 'blkverify',
'node-name': blkverify,
'test': node_0,
'raw': node_1}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(test)
self.checkBlockDriverState(raw)
self.checkBlockDriverState(blkverify)
# Add a Quorum node
def addQuorum(self, quorum, child0, child1):
self.checkBlockDriverState(child0, False)
self.checkBlockDriverState(child1, False)
self.checkBlockDriverState(quorum, False)
iotests.qemu_img('create', '-f', iotests.imgfmt, new_img, '1M')
child_0 = {'driver': iotests.imgfmt,
'node-name': child0,
'file': {'driver': 'file',
'filename': base_img}}
child_1 = {'driver': iotests.imgfmt,
'node-name': child1,
'file': {'driver': 'file',
'filename': new_img}}
opts = {'driver': 'quorum',
'node-name': quorum,
'vote-threshold': 1,
'children': [ child_0, child_1 ]}
result = self.vm.qmp('blockdev-add', conv_keys = False, options = opts)
self.assert_qmp(result, 'return', {})
self.checkBlockDriverState(child0)
self.checkBlockDriverState(child1)
self.checkBlockDriverState(quorum)
########################
# The tests start here #
########################
def testWrongParameters(self):
self.addBlockBackend('drive0', 'node0')
result = self.vm.qmp('x-blockdev-del')
self.assert_qmp(result, 'error/class', 'GenericError')
result = self.vm.qmp('x-blockdev-del', id='drive0', node_name='node0')
self.assert_qmp(result, 'error/class', 'GenericError')
self.delBlockBackend('drive0', 'node0')
def testBlockBackend(self):
self.addBlockBackend('drive0', 'node0')
# You cannot delete a BDS that is attached to a backend
self.delBlockDriverState('node0', expect_error = True)
self.delBlockBackend('drive0', 'node0')
def testBlockDriverState(self):
self.addBlockDriverState('node0')
# You cannot delete a file BDS directly
self.delBlockDriverState('node0_file', expect_error = True)
self.delBlockDriverState('node0')
def testEject(self):
self.addBlockBackend('drive0', 'node0')
self.ejectDrive('drive0', 'node0')
self.delBlockBackend('drive0', None)
def testDeviceModel(self):
self.addBlockBackend('drive0', 'node0')
self.addDeviceModel('device0', 'drive0')
self.ejectDrive('drive0', 'node0', expect_error = True)
self.delBlockBackend('drive0', 'node0', expect_error = True)
self.delDeviceModel('device0')
self.delBlockBackend('drive0', 'node0')
def testAttachMedia(self):
# This creates a BlockBackend and removes its media
self.addBlockBackend('drive0', 'node0')
self.ejectDrive('drive0', 'node0')
# This creates a new BlockDriverState and inserts it into the backend
self.addBlockDriverState('node1')
self.insertDrive('drive0', 'node1')
# The backend can't be removed: the new BDS has an extra reference
self.delBlockBackend('drive0', 'node1', expect_error = True)
self.delBlockDriverState('node1', expect_error = True)
# The BDS still exists after being ejected, but now it can be removed
self.ejectDrive('drive0', 'node1', destroys_media = False)
self.delBlockDriverState('node1')
self.delBlockBackend('drive0', None)
def testSnapshotSync(self):
self.addBlockBackend('drive0', 'node0')
self.createSnapshotSync('node0', 'overlay0')
# This fails because node0 is now being used as a backing image
self.delBlockDriverState('node0', expect_error = True)
# This succeeds because overlay0 only has the backend reference
self.delBlockBackend('drive0', 'overlay0')
self.checkBlockDriverState('node0', False)
def testSnapshot(self):
self.addBlockBackend('drive0', 'node0')
self.addBlockDriverStateOverlay('overlay0')
self.createSnapshot('node0', 'overlay0')
self.delBlockBackend('drive0', 'overlay0', expect_error = True)
self.delBlockDriverState('node0', expect_error = True)
self.delBlockDriverState('overlay0', expect_error = True)
self.ejectDrive('drive0', 'overlay0', destroys_media = False)
self.delBlockBackend('drive0', None)
self.delBlockDriverState('node0', expect_error = True)
self.delBlockDriverState('overlay0')
self.checkBlockDriverState('node0', False)
def testMirror(self):
self.addBlockBackend('drive0', 'node0')
self.createMirror('drive0', 'node0', 'mirror0')
# The block job prevents removing the device
self.delBlockBackend('drive0', 'node0', expect_error = True)
self.delBlockDriverState('node0', expect_error = True)
self.delBlockDriverState('mirror0', expect_error = True)
self.wait_ready('drive0')
self.completeBlockJob('drive0', 'node0', 'mirror0')
self.assert_no_active_block_jobs()
self.checkBlockDriverState('node0', False)
# This succeeds because the backend now points to mirror0
self.delBlockBackend('drive0', 'mirror0')
def testBlkDebug(self):
self.addBlkDebug('debug0', 'node0')
# 'node0' is used by the blkdebug node
self.delBlockDriverState('node0', expect_error = True)
# But we can remove the blkdebug node directly
self.delBlockDriverState('debug0')
self.checkBlockDriverState('node0', False)
def testBlkVerify(self):
self.addBlkVerify('verify0', 'node0', 'node1')
# We cannot remove the children of a blkverify device
self.delBlockDriverState('node0', expect_error = True)
self.delBlockDriverState('node1', expect_error = True)
# But we can remove the blkverify node directly
self.delBlockDriverState('verify0')
self.checkBlockDriverState('node0', False)
self.checkBlockDriverState('node1', False)
def testQuorum(self):
if not 'quorum' in iotests.qemu_img_pipe('--help'):
return
self.addQuorum('quorum0', 'node0', 'node1')
# We cannot remove the children of a Quorum device
self.delBlockDriverState('node0', expect_error = True)
self.delBlockDriverState('node1', expect_error = True)
# But we can remove the Quorum node directly
self.delBlockDriverState('quorum0')
self.checkBlockDriverState('node0', False)
self.checkBlockDriverState('node1', False)
if __name__ == '__main__':
iotests.main(supported_fmts=["qcow2"])
............
----------------------------------------------------------------------
Ran 12 tests
OK
......@@ -41,7 +41,6 @@ sortme=false
expunge=true
have_test_arg=false
randomize=false
valgrind=false
cachemode=false
rm -f $tmp.list $tmp.tmp $tmp.sed
......@@ -53,6 +52,7 @@ export CACHEMODE="writeback"
export QEMU_IO_OPTIONS=""
export CACHEMODE_IS_DEFAULT=true
export QEMU_OPTIONS="-nodefaults"
export VALGRIND_QEMU=
for r
do
......@@ -278,7 +278,7 @@ testlist options
;;
-valgrind)
valgrind=true
VALGRIND_QEMU='y'
xpand=false
;;
......@@ -436,8 +436,3 @@ fi
if [ "$IMGPROTO" = "nbd" ] ; then
[ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found"
fi
if $valgrind; then
export REAL_QEMU_IO="$QEMU_IO_PROG"
export QEMU_IO_PROG=valgrind_qemu_io
fi
......@@ -44,6 +44,8 @@ export HOST_OPTIONS=${HOST_OPTIONS:=local.config}
export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"}
export PWD=`pwd`
export _QEMU_HANDLE=0
# $1 = prog to look for, $2* = default pathnames if not found in $PATH
set_prog_path()
{
......@@ -105,7 +107,12 @@ fi
_qemu_wrapper()
{
(exec "$QEMU_PROG" $QEMU_OPTIONS "$@")
(
if [ -n "${QEMU_NEED_PID}" ]; then
echo $BASHPID > "${TEST_DIR}/qemu-${_QEMU_HANDLE}.pid"
fi
exec "$QEMU_PROG" $QEMU_OPTIONS "$@"
)
}
_qemu_img_wrapper()
......@@ -115,12 +122,31 @@ _qemu_img_wrapper()
_qemu_io_wrapper()
{
(exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@")
local VALGRIND_LOGFILE=/tmp/$$.valgrind
local RETVAL
(
if [ "${VALGRIND_QEMU}" == "y" ]; then
exec valgrind --log-file="${VALGRIND_LOGFILE}" --error-exitcode=99 "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"
else
exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@"
fi
)
RETVAL=$?
if [ "${VALGRIND_QEMU}" == "y" ]; then
if [ $RETVAL == 99 ]; then
cat "${VALGRIND_LOGFILE}"
fi
rm -f "${VALGRIND_LOGFILE}"
fi
(exit $RETVAL)
}
_qemu_nbd_wrapper()
{
(exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@")
(
echo $BASHPID > "${TEST_DIR}/qemu-nbd.pid"
exec "$QEMU_NBD_PROG" $QEMU_NBD_OPTIONS "$@"
)
}
export QEMU=_qemu_wrapper
......
......@@ -30,8 +30,6 @@ QEMU_COMM_TIMEOUT=10
QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$"
QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$"
QEMU_PID=
_QEMU_HANDLE=0
QEMU_HANDLE=0
# If bash version is >= 4.1, these will be overwritten and dynamic
......@@ -153,11 +151,11 @@ function _launch_qemu()
mkfifo "${fifo_out}"
mkfifo "${fifo_in}"
QEMU_NEED_PID='y'\
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
>"${fifo_out}" \
2>&1 \
<"${fifo_in}" &
QEMU_PID[${_QEMU_HANDLE}]=$!
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]
......@@ -196,10 +194,18 @@ function _cleanup_qemu()
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
for i in "${!QEMU_OUT[@]}"
do
if [ -z "${wait}" ]; then
kill -KILL ${QEMU_PID[$i]} 2>/dev/null
local QEMU_PID
if [ -f "${TEST_DIR}/qemu-${i}.pid" ]; then
read QEMU_PID < "${TEST_DIR}/qemu-${i}.pid"
rm -f "${TEST_DIR}/qemu-${i}.pid"
if [ -z "${wait}" ] && [ -n "${QEMU_PID}" ]; then
kill -KILL ${QEMU_PID} 2>/dev/null
fi
if [ -n "${QEMU_PID}" ]; then
wait ${QEMU_PID} 2>/dev/null # silent kill
fi
fi
wait ${QEMU_PID[$i]} 2>/dev/null # silent kill
if [ -n "${wait}" ]; then
cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \
| _filter_qemu_io | _filter_qmp
......
......@@ -70,16 +70,6 @@ else
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
fi
function valgrind_qemu_io()
{
valgrind --log-file=/tmp/$$.valgrind --error-exitcode=99 $REAL_QEMU_IO "$@"
if [ $? != 0 ]; then
cat /tmp/$$.valgrind
fi
rm -f /tmp/$$.valgrind
}
_optstr_add()
{
if [ -n "$1" ]; then
......@@ -154,7 +144,6 @@ _make_test_img()
# Start an NBD server on the image file, which is what we'll be talking to
if [ $IMGPROTO = "nbd" ]; then
eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &"
QEMU_NBD_PID=$!
sleep 1 # FIXME: qemu-nbd needs to be listening before we continue
fi
}
......@@ -175,8 +164,11 @@ _cleanup_test_img()
case "$IMGPROTO" in
nbd)
if [ -n "$QEMU_NBD_PID" ]; then
kill $QEMU_NBD_PID
if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then
local QEMU_NBD_PID
read QEMU_NBD_PID < "${TEST_DIR}/qemu-nbd.pid"
kill ${QEMU_NBD_PID}
rm -f "${TEST_DIR}/qemu-nbd.pid"
fi
rm -f "$TEST_IMG_FILE"
;;
......
......@@ -138,3 +138,4 @@
135 rw auto
137 rw auto
138 rw auto quick
139 rw auto quick
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册