Block patches from 2015-10-26 until 2015-11-11.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJWQ2YyAAoJEDuxQgLoOKytN14IAKGIVOI1CfPNpWphrtc55Q0n NEHxu9AeectWxbxHrVbhHqB7wEfIoPdymuiIg8WO3GPrEII2EUHJntBy0LyIxJUC wj3waAsIs4bJPdkGBxFoqMkmXo+wKUFivh1aVx7pZwd0b3yjfgTZ117i3aHCBIEl hvAoLdTp/tqRQcBvPu+/jnl2mSquf6IJg8Yhg409HFUrNcQ4bg2+gOQgrSuAKle8 1Jp73umA2509/FOB3J1QT0GZnX9+rbjoWxXbGyPucb26x6ep3KDEbMy5lMAv0+W2 Zoi4YfjPiPFTswkuNvqu2zpzJz1q7WNCP3V5/Yn/2q9KXNicTfsWQWs+T/hoByw= =FKUB -----END PGP SIGNATURE----- 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: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
commit
4d07c720f4
@ -189,6 +189,11 @@ static void drive_info_del(DriveInfo *dinfo)
|
|||||||
g_free(dinfo);
|
g_free(dinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int blk_get_refcnt(BlockBackend *blk)
|
||||||
|
{
|
||||||
|
return blk ? blk->refcnt : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Increment @blk's reference count.
|
* Increment @blk's reference count.
|
||||||
* @blk must not be null.
|
* @blk must not be null.
|
||||||
|
@ -384,6 +384,7 @@ static void mirror_exit(BlockJob *job, void *opaque)
|
|||||||
aio_context_release(replace_aio_context);
|
aio_context_release(replace_aio_context);
|
||||||
}
|
}
|
||||||
g_free(s->replaces);
|
g_free(s->replaces);
|
||||||
|
bdrv_op_unblock_all(s->target, s->common.blocker);
|
||||||
bdrv_unref(s->target);
|
bdrv_unref(s->target);
|
||||||
block_job_completed(&s->common, data->ret);
|
block_job_completed(&s->common, data->ret);
|
||||||
g_free(data);
|
g_free(data);
|
||||||
@ -744,6 +745,9 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
block_job_release(bs);
|
block_job_release(bs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bdrv_op_block_all(s->target, s->common.blocker);
|
||||||
|
|
||||||
bdrv_set_enable_write_cache(s->target, true);
|
bdrv_set_enable_write_cache(s->target, true);
|
||||||
if (s->target->blk) {
|
if (s->target->blk) {
|
||||||
blk_set_on_error(s->target->blk, on_target_error, on_target_error);
|
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)
|
if (!offset)
|
||||||
return 0;
|
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++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
|
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;
|
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;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
|
||||||
|
|
||||||
if (type != QCOW2_CLUSTER_UNALLOCATED) {
|
if (type != wanted_type) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -554,13 +556,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
|||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
|
c = count_contiguous_clusters_by_type(nb_clusters, &l2_table[l2_index],
|
||||||
&l2_table[l2_index], QCOW_OFLAG_ZERO);
|
QCOW2_CLUSTER_ZERO);
|
||||||
*cluster_offset = 0;
|
*cluster_offset = 0;
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_UNALLOCATED:
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
/* how many empty clusters ? */
|
/* 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;
|
*cluster_offset = 0;
|
||||||
break;
|
break;
|
||||||
case QCOW2_CLUSTER_NORMAL:
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
|
66
blockdev.c
66
blockdev.c
@ -3479,6 +3479,72 @@ fail:
|
|||||||
qmp_output_visitor_cleanup(ov);
|
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 *qmp_query_block_jobs(Error **errp)
|
||||||
{
|
{
|
||||||
BlockJobInfoList *head = NULL, **p_next = &head;
|
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,
|
BlockBackend *blk_new_open(const char *name, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
const char *reference, QDict *options, int flags,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
int blk_get_refcnt(BlockBackend *blk);
|
||||||
void blk_ref(BlockBackend *blk);
|
void blk_ref(BlockBackend *blk);
|
||||||
void blk_unref(BlockBackend *blk);
|
void blk_unref(BlockBackend *blk);
|
||||||
const char *blk_name(BlockBackend *blk);
|
const char *blk_name(BlockBackend *blk);
|
||||||
|
@ -1895,8 +1895,8 @@
|
|||||||
# level and no BlockBackend will be created.
|
# level and no BlockBackend will be created.
|
||||||
#
|
#
|
||||||
# This command is still a work in progress. It doesn't support all
|
# This command is still a work in progress. It doesn't support all
|
||||||
# block drivers, it lacks a matching blockdev-del, and more. Stay
|
# block drivers among other things. Stay away from it unless you want
|
||||||
# away from it unless you want to help with its development.
|
# to help with its development.
|
||||||
#
|
#
|
||||||
# @options: block device options for the new device
|
# @options: block device options for the new device
|
||||||
#
|
#
|
||||||
@ -1904,6 +1904,34 @@
|
|||||||
##
|
##
|
||||||
{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
|
{ '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:
|
# @blockdev-open-tray:
|
||||||
#
|
#
|
||||||
|
@ -3946,8 +3946,8 @@ blockdev-add
|
|||||||
Add a block device.
|
Add a block device.
|
||||||
|
|
||||||
This command is still a work in progress. It doesn't support all
|
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
|
block drivers among other things. Stay away from it unless you want
|
||||||
from it unless you want to help with its development.
|
to help with its development.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
@ -3990,6 +3990,63 @@ Example (2):
|
|||||||
|
|
||||||
<- { "return": {} }
|
<- { "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
|
EQMP
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,11 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x1
|
||||||
ERROR cluster 5 refcount=0 reference=1
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
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
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x1
|
||||||
ERROR cluster 5 refcount=0 reference=1
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
Rebuilding refcount structure
|
Rebuilding refcount structure
|
||||||
@ -60,7 +68,11 @@ incompatible_features 0x0
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x0
|
||||||
No errors were found on the image.
|
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
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x1
|
||||||
ERROR cluster 5 refcount=0 reference=1
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
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
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x0
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
*** done
|
*** done
|
||||||
|
@ -32,12 +32,18 @@ status=1 # failure is the default!
|
|||||||
|
|
||||||
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
|
nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket
|
||||||
nbd_snapshot_img="nbd:unix:$nbd_unix_socket"
|
nbd_snapshot_img="nbd:unix:$nbd_unix_socket"
|
||||||
|
rm -f "${TEST_DIR}/qemu-nbd.pid"
|
||||||
|
|
||||||
_cleanup_nbd()
|
_cleanup_nbd()
|
||||||
{
|
{
|
||||||
|
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
|
if [ -n "$NBD_SNAPSHOT_PID" ]; then
|
||||||
kill "$NBD_SNAPSHOT_PID"
|
kill "$NBD_SNAPSHOT_PID"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
rm -f "$nbd_unix_socket"
|
rm -f "$nbd_unix_socket"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +66,6 @@ _export_nbd_snapshot()
|
|||||||
{
|
{
|
||||||
_cleanup_nbd
|
_cleanup_nbd
|
||||||
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 &
|
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 &
|
||||||
NBD_SNAPSHOT_PID=$!
|
|
||||||
_wait_for_nbd
|
_wait_for_nbd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +73,6 @@ _export_nbd_snapshot1()
|
|||||||
{
|
{
|
||||||
_cleanup_nbd
|
_cleanup_nbd
|
||||||
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 &
|
$QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 &
|
||||||
NBD_SNAPSHOT_PID=$!
|
|
||||||
_wait_for_nbd
|
_wait_for_nbd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,11 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
wrote 131072/131072 bytes at offset 0
|
wrote 131072/131072 bytes at offset 0
|
||||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
magic 0x514649fb
|
||||||
version 3
|
version 3
|
||||||
backing_file_offset 0x0
|
backing_file_offset 0x0
|
||||||
@ -215,7 +219,11 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
wrote 131072/131072 bytes at offset 0
|
wrote 131072/131072 bytes at offset 0
|
||||||
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
magic 0x514649fb
|
||||||
version 3
|
version 3
|
||||||
backing_file_offset 0x0
|
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
|
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
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
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
|
incompatible_features 0x0
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
wrote 65536/65536 bytes at offset 0
|
wrote 65536/65536 bytes at offset 0
|
||||||
|
416
tests/qemu-iotests/139
Normal file
416
tests/qemu-iotests/139
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
#!/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"])
|
5
tests/qemu-iotests/139.out
Normal file
5
tests/qemu-iotests/139.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
............
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
Ran 12 tests
|
||||||
|
|
||||||
|
OK
|
@ -41,7 +41,6 @@ sortme=false
|
|||||||
expunge=true
|
expunge=true
|
||||||
have_test_arg=false
|
have_test_arg=false
|
||||||
randomize=false
|
randomize=false
|
||||||
valgrind=false
|
|
||||||
cachemode=false
|
cachemode=false
|
||||||
rm -f $tmp.list $tmp.tmp $tmp.sed
|
rm -f $tmp.list $tmp.tmp $tmp.sed
|
||||||
|
|
||||||
@ -53,6 +52,7 @@ export CACHEMODE="writeback"
|
|||||||
export QEMU_IO_OPTIONS=""
|
export QEMU_IO_OPTIONS=""
|
||||||
export CACHEMODE_IS_DEFAULT=true
|
export CACHEMODE_IS_DEFAULT=true
|
||||||
export QEMU_OPTIONS="-nodefaults"
|
export QEMU_OPTIONS="-nodefaults"
|
||||||
|
export VALGRIND_QEMU=
|
||||||
|
|
||||||
for r
|
for r
|
||||||
do
|
do
|
||||||
@ -278,7 +278,7 @@ testlist options
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
-valgrind)
|
-valgrind)
|
||||||
valgrind=true
|
VALGRIND_QEMU='y'
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -436,8 +436,3 @@ fi
|
|||||||
if [ "$IMGPROTO" = "nbd" ] ; then
|
if [ "$IMGPROTO" = "nbd" ] ; then
|
||||||
[ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found"
|
[ "$QEMU_NBD" = "" ] && _fatal "qemu-nbd not found"
|
||||||
fi
|
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 CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"}
|
||||||
export PWD=`pwd`
|
export PWD=`pwd`
|
||||||
|
|
||||||
|
export _QEMU_HANDLE=0
|
||||||
|
|
||||||
# $1 = prog to look for, $2* = default pathnames if not found in $PATH
|
# $1 = prog to look for, $2* = default pathnames if not found in $PATH
|
||||||
set_prog_path()
|
set_prog_path()
|
||||||
{
|
{
|
||||||
@ -105,7 +107,12 @@ fi
|
|||||||
|
|
||||||
_qemu_wrapper()
|
_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()
|
_qemu_img_wrapper()
|
||||||
@ -115,12 +122,31 @@ _qemu_img_wrapper()
|
|||||||
|
|
||||||
_qemu_io_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()
|
_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
|
export QEMU=_qemu_wrapper
|
||||||
|
@ -30,8 +30,6 @@ QEMU_COMM_TIMEOUT=10
|
|||||||
QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$"
|
QEMU_FIFO_IN="${TEST_DIR}/qmp-in-$$"
|
||||||
QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$"
|
QEMU_FIFO_OUT="${TEST_DIR}/qmp-out-$$"
|
||||||
|
|
||||||
QEMU_PID=
|
|
||||||
_QEMU_HANDLE=0
|
|
||||||
QEMU_HANDLE=0
|
QEMU_HANDLE=0
|
||||||
|
|
||||||
# If bash version is >= 4.1, these will be overwritten and dynamic
|
# If bash version is >= 4.1, these will be overwritten and dynamic
|
||||||
@ -153,11 +151,11 @@ function _launch_qemu()
|
|||||||
mkfifo "${fifo_out}"
|
mkfifo "${fifo_out}"
|
||||||
mkfifo "${fifo_in}"
|
mkfifo "${fifo_in}"
|
||||||
|
|
||||||
|
QEMU_NEED_PID='y'\
|
||||||
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
|
${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \
|
||||||
>"${fifo_out}" \
|
>"${fifo_out}" \
|
||||||
2>&1 \
|
2>&1 \
|
||||||
<"${fifo_in}" &
|
<"${fifo_in}" &
|
||||||
QEMU_PID[${_QEMU_HANDLE}]=$!
|
|
||||||
|
|
||||||
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
|
if [[ "${BASH_VERSINFO[0]}" -ge "5" ||
|
||||||
("${BASH_VERSINFO[0]}" -ge "4" && "${BASH_VERSINFO[1]}" -ge "1") ]]
|
("${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
|
# QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices
|
||||||
for i in "${!QEMU_OUT[@]}"
|
for i in "${!QEMU_OUT[@]}"
|
||||||
do
|
do
|
||||||
if [ -z "${wait}" ]; then
|
local QEMU_PID
|
||||||
kill -KILL ${QEMU_PID[$i]} 2>/dev/null
|
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
|
fi
|
||||||
wait ${QEMU_PID[$i]} 2>/dev/null # silent kill
|
if [ -n "${QEMU_PID}" ]; then
|
||||||
|
wait ${QEMU_PID} 2>/dev/null # silent kill
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -n "${wait}" ]; then
|
if [ -n "${wait}" ]; then
|
||||||
cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \
|
cat <&${QEMU_OUT[$i]} | _filter_testdir | _filter_qemu \
|
||||||
| _filter_qemu_io | _filter_qmp
|
| _filter_qemu_io | _filter_qmp
|
||||||
|
@ -70,16 +70,6 @@ else
|
|||||||
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
|
TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT
|
||||||
fi
|
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()
|
_optstr_add()
|
||||||
{
|
{
|
||||||
if [ -n "$1" ]; then
|
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
|
# Start an NBD server on the image file, which is what we'll be talking to
|
||||||
if [ $IMGPROTO = "nbd" ]; then
|
if [ $IMGPROTO = "nbd" ]; then
|
||||||
eval "$QEMU_NBD -v -t -b 127.0.0.1 -p 10810 -f $IMGFMT $TEST_IMG_FILE &"
|
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
|
sleep 1 # FIXME: qemu-nbd needs to be listening before we continue
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -175,8 +164,11 @@ _cleanup_test_img()
|
|||||||
case "$IMGPROTO" in
|
case "$IMGPROTO" in
|
||||||
|
|
||||||
nbd)
|
nbd)
|
||||||
if [ -n "$QEMU_NBD_PID" ]; then
|
if [ -f "${TEST_DIR}/qemu-nbd.pid" ]; then
|
||||||
kill $QEMU_NBD_PID
|
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
|
fi
|
||||||
rm -f "$TEST_IMG_FILE"
|
rm -f "$TEST_IMG_FILE"
|
||||||
;;
|
;;
|
||||||
|
@ -138,3 +138,4 @@
|
|||||||
135 rw auto
|
135 rw auto
|
||||||
137 rw auto
|
137 rw auto
|
||||||
138 rw auto quick
|
138 rw auto quick
|
||||||
|
139 rw auto quick
|
||||||
|
Loading…
x
Reference in New Issue
Block a user