1#!/usr/bin/env python3 2# group: auto quick 3# 4# Test for copy-before-write filter permission conflict 5# 6# Copyright (c) 2019 Virtuozzo International GmbH. 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 2 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program. If not, see <http://www.gnu.org/licenses/>. 20# 21 22import iotests 23 24# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs 25iotests.script_initialize( 26 supported_fmts=['qcow2'], 27) 28 29size = 1024 * 1024 30 31""" Test description 32 33When performing a backup, all writes on the source subtree must go through the 34copy-before-write filter so it can copy all data to the target before it is 35changed. copy-before-write filter is appended above source node, to achieve 36this thing, so all parents of source node are handled. A configuration with 37side parents of source sub-tree with write permission is unsupported (we'd have 38append several copy-before-write filter like nodes to handle such parents). The 39test create an example of such configuration and checks that a backup is then 40not allowed (blockdev-backup command should fail). 41 42The configuration: 43 44 ┌────────┐ target ┌─────────────┐ 45 │ target │ ◀─────── │ backup_top │ 46 └────────┘ └─────────────┘ 47 │ 48 │ backing 49 ▼ 50 ┌─────────────┐ 51 │ source │ 52 └─────────────┘ 53 │ 54 │ file 55 ▼ 56 ┌─────────────┐ write perm ┌───────┐ 57 │ base │ ◀──────────── │ other │ 58 └─────────────┘ └───────┘ 59 60copy-before-write filter wants to unshare write permission on its source child. 61Write unsharing will be propagated to the "source->base" link and will conflict 62with other node write permission. So permission update will fail and backup job 63will not be started. 64 65Note, that the only thing which prevents backup of running on such 66configuration is default permission propagation scheme. It may be altered by 67different block drivers, so backup will run in invalid configuration. But 68something is better than nothing. Also, before the previous commit (commit 69preceding this test creation), starting backup on such configuration led to 70crash, so current "something" is a lot better, and this test actual goal is 71to check that crash is fixed :) 72""" 73 74vm = iotests.VM() 75vm.launch() 76 77vm.qmp_log('blockdev-add', **{ 78 'node-name': 'target', 79 'driver': 'null-co', 80 'size': size, 81}) 82 83vm.qmp_log('blockdev-add', **{ 84 'node-name': 'source', 85 'driver': 'blkdebug', 86 'image': {'node-name': 'base', 'driver': 'null-co', 'size': size} 87}) 88 89vm.qmp_log('blockdev-add', **{ 90 'node-name': 'other', 91 'driver': 'blkdebug', 92 'image': 'base', 93 'take-child-perms': ['write'] 94}) 95 96vm.qmp_log('blockdev-backup', sync='full', device='source', target='target', 97 job_id="backup0") 98 99vm.shutdown() 100 101 102print('\n=== copy-before-write filter should be gone after job-finalize ===\n') 103 104# Check that the copy-before-write node is gone after job-finalize. 105 106vm = iotests.VM() 107vm.launch() 108 109vm.qmp_log('blockdev-add', **{ 110 'node-name': 'source', 111 'driver': 'null-co', 112}) 113 114vm.qmp_log('blockdev-add', **{ 115 'node-name': 'target', 116 'driver': 'null-co', 117}) 118 119vm.qmp_log('blockdev-backup', 120 job_id='backup', 121 device='source', 122 target='target', 123 sync='full', 124 filter_node_name='backup-filter', 125 auto_finalize=False, 126 auto_dismiss=False) 127 128vm.event_wait('BLOCK_JOB_PENDING', 5.0) 129 130# The copy-before-write filter should still be present prior to finalization 131assert vm.node_info('backup-filter') is not None 132 133vm.qmp_log('job-finalize', id='backup') 134vm.event_wait('BLOCK_JOB_COMPLETED', 5.0) 135 136# The filter should be gone now. Check that by trying to access it 137# with qemu-io (which will most likely crash qemu if it is still 138# there.). 139vm.qmp_log('human-monitor-command', 140 command_line='qemu-io backup-filter "write 0 1M"') 141 142# (Also, do an explicit check.) 143assert vm.node_info('backup-filter') is None 144 145vm.qmp_log('job-dismiss', id='backup') 146vm.event_wait('JOB_STATUS_CHANGE', 5.0, {'data': {'status': 'null'}}) 147 148vm.shutdown() 149