Add 'x-blockdev-del' command
v3 cover Add 'x-blockdev-del' command
This version uses op blockers for the target image in the drive-mirror job, but the implementation of 'x-blockdev-del' remains the same. I copy the description from the previous series:
The semantics of 'x-blockdev-del' try to mirror the semantics of 'blockdev-add' as I discussed with Kevin in the previous thread. There's two parameters: 'id' and 'node-name' and only one can be specified.
1) 'x-blockdev-del id=foo' deletes the backend foo with its BDS, if and only if neither have more than 1 reference and the BDS has no parents and no op blockers.
2) 'x-blockdev-del node-name=foo' deletes the BDS foo, if and only if it only has one reference, no parents, no op blockers AND it is not attached to any block backend.
Regards,
Berto
v3:
- Remove the extra references added in v2 to the mirror and backup jobs, and use op blockers instead (for the mirror case only).
v2: https://lists.gnu.org/archive/html/qemu-block/2015-10/msg00828.html
- Rename it as 'x-blockdev-del' and label it as experimental.
- Use two parameters instead of just one. If you try to delete a BDS, it must not be attached to any backend.
- New test cases.
- Hold extra references during the mirror and backup block jobs.
v1: https://lists.gnu.org/archive/html/qemu-devel/2015-10/msg02999.html
- Initial implementation
Alberto Garcia (4):
mirror: block all operations on the target image during the job
block: Add blk_get_refcnt()
block: Add 'x-blockdev-del' QMP command
iotests: Add tests for the x-blockdev-del command
block/block-backend.c | 5 + block/mirror.c | 4 + blockdev.c | 66 +++++++ include/sysemu/block-backend.h | 1 + qapi/block-core.json | 32 +++- qmp-commands.hx | 46 ++++- tests/qemu-iotests/139 | 408 +++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/139.out | 5 + tests/qemu-iotests/group | 1 + 9 files changed, 564 insertions(+), 4 deletions(-) create mode 100644 tests/qemu-iotests/139 create mode 100644 tests/qemu-iotests/139.out
Signature · 1 line
2.6.1
PATCH v3 1/4 mirror: block all operations on the target image during the job
There's nothing preventing the target image from being used by other operations during the 'drive-mirror' job, so we should block them all until the job is done.
--- block/mirror.c | 4 ++++ 1 file changed, 4 insertions(+)
block/mirror.c +4 -0
@@ -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);
Signature · 1 line
2.6.1
Re: [Qemu-devel] [PATCH v3 1/4] mirror: block all operations on the target image during the job
Quoted 8 lines · Alberto Garcia
There's nothing preventing the target image from being used by other operations during the 'drive-mirror' job, so we should block them all until the job is done.
Signed-off-by Alberto Garcia <[email protected]>--- block/mirror.c | 4 ++++ 1 file changed, 4 insertions(+)
PATCH v3 2/4 block: Add blk_get_refcnt()
In reply to Alberto GarciaThis function returns the reference count of a given BlockBackend. For convenience, it returns 0 if the BlockBackend pointer is NULL.
--- block/block-backend.c | 5 +++++ include/sysemu/block-backend.h | 1 + 2 files changed, 6 insertions(+)
block/block-backend.c +5 -0
@@ -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.
include/sysemu/block-backend.h +1 -0
@@ -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);
Signature · 1 line
2.6.1
PATCH v3 3/4 block: Add 'x-blockdev-del' QMP command
In reply to Alberto GarciaThis command is still experimental, hence the name.
This is the companion to 'blockdev-add'. It allows deleting a BlockBackend with its associated BlockDriverState tree, or a BlockDriverState that is not attached to any backend.
In either case, the command fails if the reference count is greater than 1 or the BlockDriverState has any parents.
--- blockdev.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 32 +++++++++++++++++++++++-- qmp-commands.hx | 46 ++++++++++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 4 deletions(-)
blockdev.c +66 -0
@@ -3432,6 +3432,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 being used" , 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 being used 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 being used" , +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;
qapi/block-core.json +30 -2
@@ -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 #
@@ -1905,6 +1905,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 being used. +# +# 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: # # Opens a block device's tray. If there is a block driver state tree inserted as
qmp-commands.hx +44 -2
58 lines not on this page · raw message
Signature · 1 line
2.6.1
Re: [Qemu-devel] [PATCH v3 3/4] block: Add 'x-blockdev-del' QMP command
Quoted 9 lines · Alberto Garcia
This command is still experimental, hence the name.
This is the companion to 'blockdev-add'. It allows deleting a BlockBackend with its associated BlockDriverState tree, or a BlockDriverState that is not attached to any backend.
In either case, the command fails if the reference count is greater than 1 or the BlockDriverState has any parents.
Quoted 6 lines
Signed-off-by Alberto Garcia <[email protected]>--- blockdev.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ qapi/block-core.json | 32 +++++++++++++++++++++++-- qmp-commands.hx | 46 ++++++++++++++++++++++++++++++++++-- 3 files changed, 140 insertions(+), 4 deletions(-)
s/being used/in use/ might be better, and I don't think adding an associated blockdev-add to the example in qmp-commands.hx would hurt (stressing that they are intended to be paired; "virtio0" makes it look like blockdev-del is for deleting auto-generated BBs, too (which may work, but that doesn't seem to be its intended purpose)).
Anyway:
PATCH v3 4/4 iotests: Add tests for the x-blockdev-del command
In reply to Alberto Garcia--- tests/qemu-iotests/139 | 408 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/139.out | 5 + tests/qemu-iotests/group | 1 + 3 files changed, 414 insertions(+) create mode 100644 tests/qemu-iotests/139 create mode 100644 tests/qemu-iotests/139.out
tests/qemu-iotests/139 new file +408 -0
@@ -0,0 +1,408 @@ +#!/usr/bin/env python +# +# Test cases for the QMP 'x-blockdev-del' command +# +# Copyright (C) 2015 Igalia, S.L. +# Author: Alberto Garcia <[email protected]> +# +# 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 has base_img as its backing file + 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.checkBlockBackend(backend, node_after) + + # Add a BlkDebug node + 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 + 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.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 blkdebug node directly + self.delBlockDriverState('verify0') + self.checkBlockDriverState('node0', False) + self.checkBlockDriverState('node1', False) + + def testQuorum(self): + 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"])
tests/qemu-iotests/139.out new file +5 -0
@@ -0,0 +1,5 @@ +............ +---------------------------------------------------------------------- +Ran 12 tests + +OK
tests/qemu-iotests/group +1 -0
@@ -138,3 +138,4 @@ 135 rw auto 137 rw auto 138 rw auto quick +139 rw auto quick
Signature · 1 line
2.6.1
Re: [Qemu-devel] [PATCH v3 4/4] iotests: Add tests for the x-blockdev-del command
Quoted 15 lines · Alberto Garcia
Signed-off-by Alberto Garcia <[email protected]>--- tests/qemu-iotests/139 | 408 +++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/139.out | 5 + tests/qemu-iotests/group | 1 + 3 files changed, 414 insertions(+) create mode 100644 tests/qemu-iotests/139 create mode 100644 tests/qemu-iotests/139.outtests/qemu-iotests/139 new file +0
-0@@ -0,0 +1,408 @@
[...]
tests/qemu-iotests/139
Quoted 2 lines
tests/qemu-iotests/139 new file +2
-0+ + # Add a BlockDriverState that has base_img as its backing file
Not quite; while new_img does have base_img as its backing file, new_img's format BDS explicitly does not.
Quoted 12 lines
tests/qemu-iotests/139 new file +12
-0+ 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)
[...]
tests/qemu-iotests/139 line 237
Quoted 11 lines
tests/qemu-iotests/139 new file +11
-0+ # Add a BlkDebug node + 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}
Normally, blkdebug is supposed to sit between the format and the protocol BDS.
(You can either make this a test of "raw" instead of "blkdebug", amend the blkdebug placement, or simply ignore how it's "normally supposed to be".)
Quoted 9 lines
tests/qemu-iotests/139 new file +9
-0+ 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 + def addBlkVerify(self, blkverify, test, raw): + self.checkBlockDriverState(test, False) + self.checkBlockDriverState(raw, False)
tests/qemu-iotests/139 line 256
Quoted 10 lines
tests/qemu-iotests/139 new file +10
-0+ 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}}
And, well, this is normally supposed to be a protocol BDS. The use case of blkverify is not to be a dumbed-down quorum, but to test the block driver used for the test node.
(Here, your choices are either to drop this test, or again to ignore how it's normally supposed to be.)
tests/qemu-iotests/139 line 265
Quoted 9 lines
tests/qemu-iotests/139 new file +9
-0+ 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)
Assuming you choose the "ignore" option for both of the above, and you do something about the *Overlay comment or I simply accept it as "not that wrong, and it's just a comment in a test":