1#!/usr/bin/env python3 2# group: rw quick 3# 4# This test covers the basic fleecing workflow, which provides a 5# point-in-time snapshot of a node that can be queried over NBD. 6# 7# Copyright (C) 2018 Red Hat, Inc. 8# John helped, too. 9# 10# This program is free software; you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation; either version 2 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program. If not, see <http://www.gnu.org/licenses/>. 22# 23# Creator/Owner: John Snow <jsnow@redhat.com> 24 25import iotests 26from iotests import log, qemu_img, qemu_io, qemu_io_silent 27 28iotests.script_initialize( 29 supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'], 30 supported_platforms=['linux'], 31 required_fmts=['copy-before-write'], 32) 33 34patterns = [('0x5d', '0', '64k'), 35 ('0xd5', '1M', '64k'), 36 ('0xdc', '32M', '64k'), 37 ('0xcd', '0x3ff0000', '64k')] # 64M - 64K 38 39overwrite = [('0xab', '0', '64k'), # Full overwrite 40 ('0xad', '0x00f8000', '64k'), # Partial-left (1M-32K) 41 ('0x1d', '0x2008000', '64k'), # Partial-right (32M+32K) 42 ('0xea', '0x3fe0000', '64k')] # Adjacent-left (64M - 128K) 43 44zeroes = [('0', '0x00f8000', '32k'), # Left-end of partial-left (1M-32K) 45 ('0', '0x2010000', '32k'), # Right-end of partial-right (32M+64K) 46 ('0', '0x3fe0000', '64k')] # overwrite[3] 47 48remainder = [('0xd5', '0x108000', '32k'), # Right-end of partial-left [1] 49 ('0xdc', '32M', '32k'), # Left-end of partial-right [2] 50 ('0xcd', '0x3ff0000', '64k')] # patterns[3] 51 52def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): 53 log('--- Setting up images ---') 54 log('') 55 56 assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 57 assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0 58 59 for p in patterns: 60 qemu_io('-f', iotests.imgfmt, 61 '-c', 'write -P%s %s %s' % p, base_img_path) 62 63 log('Done') 64 65 log('') 66 log('--- Launching VM ---') 67 log('') 68 69 src_node = 'source' 70 tmp_node = 'temp' 71 qom_path = '/machine/peripheral/sda' 72 vm.add_blockdev(f'driver={iotests.imgfmt},file.driver=file,' 73 f'file.filename={base_img_path},node-name={src_node}') 74 vm.add_device('virtio-scsi') 75 vm.add_device(f'scsi-hd,id=sda,drive={src_node}') 76 vm.launch() 77 log('Done') 78 79 log('') 80 log('--- Setting up Fleecing Graph ---') 81 log('') 82 83 84 # create tmp_node backed by src_node 85 log(vm.qmp('blockdev-add', { 86 'driver': 'qcow2', 87 'node-name': tmp_node, 88 'file': { 89 'driver': 'file', 90 'filename': fleece_img_path, 91 }, 92 'backing': src_node, 93 })) 94 95 # Establish CBW from source to fleecing node 96 if use_cbw: 97 log(vm.qmp('blockdev-add', { 98 'driver': 'copy-before-write', 99 'node-name': 'fl-cbw', 100 'file': src_node, 101 'target': tmp_node 102 })) 103 104 log(vm.qmp('qom-set', path=qom_path, property='drive', value='fl-cbw')) 105 else: 106 log(vm.qmp('blockdev-backup', 107 job_id='fleecing', 108 device=src_node, 109 target=tmp_node, 110 sync='none')) 111 112 log('') 113 log('--- Setting up NBD Export ---') 114 log('') 115 116 nbd_uri = 'nbd+unix:///%s?socket=%s' % (tmp_node, nbd_sock_path) 117 log(vm.qmp('nbd-server-start', 118 {'addr': {'type': 'unix', 119 'data': {'path': nbd_sock_path}}})) 120 121 log(vm.qmp('nbd-server-add', device=tmp_node)) 122 123 log('') 124 log('--- Sanity Check ---') 125 log('') 126 127 for p in patterns + zeroes: 128 cmd = 'read -P%s %s %s' % p 129 log(cmd) 130 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 131 132 log('') 133 log('--- Testing COW ---') 134 log('') 135 136 for p in overwrite: 137 cmd = 'write -P%s %s %s' % p 138 log(cmd) 139 log(vm.hmp_qemu_io(qom_path, cmd, qdev=True)) 140 141 log('') 142 log('--- Verifying Data ---') 143 log('') 144 145 for p in patterns + zeroes: 146 cmd = 'read -P%s %s %s' % p 147 log(cmd) 148 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 149 150 log('') 151 log('--- Cleanup ---') 152 log('') 153 154 if use_cbw: 155 log(vm.qmp('qom-set', path=qom_path, property='drive', value=src_node)) 156 log(vm.qmp('blockdev-del', node_name='fl-cbw')) 157 else: 158 log(vm.qmp('block-job-cancel', device='fleecing')) 159 e = vm.event_wait('BLOCK_JOB_CANCELLED') 160 assert e is not None 161 log(e, filters=[iotests.filter_qmp_event]) 162 163 log(vm.qmp('nbd-server-stop')) 164 log(vm.qmp('blockdev-del', node_name=tmp_node)) 165 vm.shutdown() 166 167 log('') 168 log('--- Confirming writes ---') 169 log('') 170 171 for p in overwrite + remainder: 172 cmd = 'read -P%s %s %s' % p 173 log(cmd) 174 assert qemu_io_silent(base_img_path, '-c', cmd) == 0 175 176 log('') 177 log('Done') 178 179 180def test(use_cbw): 181 with iotests.FilePath('base.img') as base_img_path, \ 182 iotests.FilePath('fleece.img') as fleece_img_path, \ 183 iotests.FilePath('nbd.sock', 184 base_dir=iotests.sock_dir) as nbd_sock_path, \ 185 iotests.VM() as vm: 186 do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm) 187 188 189log('=== Test backup(sync=none) based fleecing ===\n') 190test(False) 191 192log('=== Test filter based fleecing ===\n') 193test(True) 194