[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH 22/22] iotests: Mirror must not attempt to create loops
From: |
Vladimir Sementsov-Ogievskiy |
Subject: |
Re: [PATCH 22/22] iotests: Mirror must not attempt to create loops |
Date: |
Thu, 26 Sep 2019 15:03:25 +0000 |
20.09.2019 18:28, Max Reitz wrote:
> Signed-off-by: Max Reitz <address@hidden>
> ---
> tests/qemu-iotests/041 | 152 +++++++++++++++++++++++++++++++++++++
> tests/qemu-iotests/041.out | 4 +-
> 2 files changed, 154 insertions(+), 2 deletions(-)
>
> diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
> index e4cc829fe2..6ea4764ae8 100755
> --- a/tests/qemu-iotests/041
> +++ b/tests/qemu-iotests/041
> @@ -1265,6 +1265,158 @@ class TestReplaces(iotests.QMPTestCase):
>
> self.vm.assert_block_path('filter0/file', 'target')
>
> + '''
> + See what happens when the @sync/@replaces configuration dictates
> + creating a loop.
> + '''
> + def test_loop(self):
> + qemu_img('create', '-f', iotests.imgfmt, test_img, str(1 * 1024 *
> 1024))
> +
> + # Dummy group so we can create a NOP filter
> + result = self.vm.qmp('object-add', qom_type='throttle-group',
> id='tg0')
> + self.assert_qmp(result, 'return', {})
> +
> + result = self.vm.qmp('blockdev-add', **{
> + 'driver': 'throttle',
> + 'node-name': 'source',
> + 'throttle-group': 'tg0',
> + 'file': {
> + 'driver': iotests.imgfmt,
> + 'node-name': 'filtered',
> + 'file': {
> + 'driver': 'file',
> + 'filename': test_img
> + }
> + }
> + })
> + self.assert_qmp(result, 'return', {})
> +
> + # Block graph is now:
> + # source[throttle] --file--> filtered[qcow2] --file--> ...
or qed, actually
> +
> + result = self.vm.qmp('drive-mirror', job_id='mirror',
> device='source',
> + target=target_img, format=iotests.imgfmt,
> + node_name='target', sync='none',
> + replaces='filtered')
> +
> + '''
> + Block graph before mirror exits would be (ignoring mirror_top):
> + source[throttle] --file--> filtered[qcow2] --file--> ...
> + target[qcow2] --file--> ...
> +
> + Then, because of sync=none and drive-mirror in absolute-paths mode,
> + the source is attached to the target:
> + source[throttle] --file--> filtered[qcow2] --file--> ...
> + ^
|
> + backing
> + |
> + target[qcow2] --file--> ...
> +
> + Replacing filtered by target would yield:
> + source[throttle] --file--> target[qcow2] --file--> ...
> + ^ |
> + +------- backing --------+
> +
> + I.e., a loop. bdrv_replace_node() detects this and simply
> + does not let source's file link point to target. However,
> + that means that target cannot really replace source.
> +
> + drive-mirror should detect this and not allow this case.
> + '''
> +
> + self.assert_qmp(result, 'error/desc',
> + "Replacing 'filtered' by 'target' with this sync " +
> \
> + "mode would result in a loop, because the former " +
> \
> + "would be a child of the latter's backing file " + \
> + "('source') after the mirror job")
> +
> + '''
> + Test what happens when there would be no loop with the pre-mirror
> + configuration, but something changes during the mirror job that asks
> + for a loop to be created during completion.
> + '''
> + def test_loop_during_mirror(self):
> + qemu_img('create', '-f', iotests.imgfmt, test_img, str(1 * 1024 *
> 1024))
> +
> + result = self.vm.qmp('blockdev-add', **{
> + 'driver': 'null-co',
> + 'node-name': 'common-base',
> + 'read-zeroes': True,
why do you need read-zeroes?
> + 'size': 1 * 1024 * 1024
> + })
> + self.assert_qmp(result, 'return', {})
> +
> + result = self.vm.qmp('blockdev-add', **{
> + 'driver': 'copy-on-read',
> + 'node-name': 'source',
> + 'file': 'common-base'
> + })
> + self.assert_qmp(result, 'return', {})
Hmm, why don't you create them both in one query?
> +
> + '''
the following is hard to read without some hint like, "We are going to ..."
> + x-blockdev-change can only add children to a quorum node that
> + have no parent yet, so we need an intermediate node between
> + target and common-base that has no parents other than target.
> + We cannot use any parent that would forward the RESIZE
> + permission (because the job takes it, but unshares it on the
> + source), so we make it a backing child of a qcow2 node.
> + Unfortunately, we cannot add backing files to Quorum nodes
> + (because of an op blocker), so we put another raw node between
> + the qcow2 node and common-base.
> + '''
> + result = self.vm.qmp('blockdev-add', **{
> + 'driver': 'qcow2',
> + 'node-name': 'base-parent',
> + 'file': {
> + 'driver': 'file',
> + 'filename': test_img
> + },
> + 'backing': {
> + 'driver': 'raw',
> + 'file': 'common-base'
> + }
> + })
> +
> + # Add a quorum node with a single child, we will add
> + # base-parent to prepare a loop later
> + result = self.vm.qmp('blockdev-add', **{
> + 'driver': 'quorum',
It would be good to skip test-cases if quorum unsupported, like other test-cases
with quorum.
> + 'node-name': 'target',
> + 'vote-threshold': 1,
> + 'children': [
> + {
> + 'driver': 'null-co',
> + 'read-zeroes': True
> + }
> + ]
> + })
> + self.assert_qmp(result, 'return', {})
It would be nice to comment out current block graph here...
> +
> + result = self.vm.qmp('blockdev-mirror', job_id='mirror',
> + device='source', target='target', sync='full',
> + replaces='common-base')
> + self.assert_qmp(result, 'return', {})
> +
> + result = self.vm.qmp('x-blockdev-change',
> + parent='target', node='base-parent')
> + self.assert_qmp(result, 'return', {})
> +
> + '''
and here, like you do in previous test-case. And here it even nicer, as this
test-case
is more complex.
> + This asks for this configuration post-mirror:
> +
> + source -> target (replaced common-base) -> base-parent
> + ^ |
> + | v
> + +----------------- [raw]
> +
> + bdrv_replace_node() would not allow such a configuration, but
> + we should not pretend we can create it, so the mirror job
> + should fail during completion.
> + '''
> +
> + self.complete_and_wait('mirror',
> + completion_error='Operation not permitted')
> +
> if __name__ == '__main__':
> iotests.main(supported_fmts=['qcow2', 'qed'],
> supported_protocols=['file'])
> diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
> index 877b76fd31..20a8158b99 100644
> --- a/tests/qemu-iotests/041.out
> +++ b/tests/qemu-iotests/041.out
> @@ -1,5 +1,5 @@
> -..............................................................................................
> +................................................................................................
> ----------------------------------------------------------------------
> -Ran 94 tests
> +Ran 96 tests
>
> OK
>
--
Best regards,
Vladimir
- Re: [PATCH 15/22] mirror: Prevent loops, (continued)
- [PATCH 16/22] iotests: Use complete_and_wait() in 155, Max Reitz, 2019/09/20
- [PATCH 18/22] iotests: Resolve TODOs in 041, Max Reitz, 2019/09/20
- [PATCH 19/22] iotests: Use self.image_len in TestRepairQuorum, Max Reitz, 2019/09/20
- [PATCH 21/22] iotests: Check that @replaces can replace filters, Max Reitz, 2019/09/20
- [PATCH 22/22] iotests: Mirror must not attempt to create loops, Max Reitz, 2019/09/20
- Re: [PATCH 22/22] iotests: Mirror must not attempt to create loops,
Vladimir Sementsov-Ogievskiy <=
- [PATCH 20/22] iotests: Add tests for invalid Quorum @replaces, Max Reitz, 2019/09/20