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) 32 33patterns = [('0x5d', '0', '64k'), 34 ('0xd5', '1M', '64k'), 35 ('0xdc', '32M', '64k'), 36 ('0xcd', '0x3ff0000', '64k')] # 64M - 64K 37 38overwrite = [('0xab', '0', '64k'), # Full overwrite 39 ('0xad', '0x00f8000', '64k'), # Partial-left (1M-32K) 40 ('0x1d', '0x2008000', '64k'), # Partial-right (32M+32K) 41 ('0xea', '0x3fe0000', '64k')] # Adjacent-left (64M - 128K) 42 43zeroes = [('0', '0x00f8000', '32k'), # Left-end of partial-left (1M-32K) 44 ('0', '0x2010000', '32k'), # Right-end of partial-right (32M+64K) 45 ('0', '0x3fe0000', '64k')] # overwrite[3] 46 47remainder = [('0xd5', '0x108000', '32k'), # Right-end of partial-left [1] 48 ('0xdc', '32M', '32k'), # Left-end of partial-right [2] 49 ('0xcd', '0x3ff0000', '64k')] # patterns[3] 50 51def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm): 52 log('--- Setting up images ---') 53 log('') 54 55 assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 56 assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0 57 58 for p in patterns: 59 qemu_io('-f', iotests.imgfmt, 60 '-c', 'write -P%s %s %s' % p, base_img_path) 61 62 log('Done') 63 64 log('') 65 log('--- Launching VM ---') 66 log('') 67 68 src_node = 'source' 69 tmp_node = 'temp' 70 qom_path = '/machine/peripheral/sda' 71 vm.add_blockdev(f'driver={iotests.imgfmt},file.driver=file,' 72 f'file.filename={base_img_path},node-name={src_node}') 73 vm.add_device('virtio-scsi') 74 vm.add_device(f'scsi-hd,id=sda,drive={src_node}') 75 vm.launch() 76 log('Done') 77 78 log('') 79 log('--- Setting up Fleecing Graph ---') 80 log('') 81 82 83 # create tmp_node backed by src_node 84 log(vm.qmp('blockdev-add', { 85 'driver': 'qcow2', 86 'node-name': tmp_node, 87 'file': { 88 'driver': 'file', 89 'filename': fleece_img_path, 90 }, 91 'backing': src_node, 92 })) 93 94 # Establish CBW from source to fleecing node 95 if use_cbw: 96 log(vm.qmp('blockdev-add', { 97 'driver': 'copy-before-write', 98 'node-name': 'fl-cbw', 99 'file': src_node, 100 'target': tmp_node 101 })) 102 103 log(vm.qmp('qom-set', path=qom_path, property='drive', value='fl-cbw')) 104 else: 105 log(vm.qmp('blockdev-backup', 106 job_id='fleecing', 107 device=src_node, 108 target=tmp_node, 109 sync='none')) 110 111 log('') 112 log('--- Setting up NBD Export ---') 113 log('') 114 115 nbd_uri = 'nbd+unix:///%s?socket=%s' % (tmp_node, nbd_sock_path) 116 log(vm.qmp('nbd-server-start', 117 {'addr': { 'type': 'unix', 118 'data': { 'path': nbd_sock_path } } })) 119 120 log(vm.qmp('nbd-server-add', device=tmp_node)) 121 122 log('') 123 log('--- Sanity Check ---') 124 log('') 125 126 for p in patterns + zeroes: 127 cmd = 'read -P%s %s %s' % p 128 log(cmd) 129 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 130 131 log('') 132 log('--- Testing COW ---') 133 log('') 134 135 for p in overwrite: 136 cmd = 'write -P%s %s %s' % p 137 log(cmd) 138 log(vm.hmp_qemu_io(qom_path, cmd, qdev=True)) 139 140 log('') 141 log('--- Verifying Data ---') 142 log('') 143 144 for p in patterns + zeroes: 145 cmd = 'read -P%s %s %s' % p 146 log(cmd) 147 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 148 149 log('') 150 log('--- Cleanup ---') 151 log('') 152 153 if use_cbw: 154 log(vm.qmp('qom-set', path=qom_path, property='drive', value=src_node)) 155 log(vm.qmp('blockdev-del', node_name='fl-cbw')) 156 else: 157 log(vm.qmp('block-job-cancel', device='fleecing')) 158 e = vm.event_wait('BLOCK_JOB_CANCELLED') 159 assert e is not None 160 log(e, filters=[iotests.filter_qmp_event]) 161 162 log(vm.qmp('nbd-server-stop')) 163 log(vm.qmp('blockdev-del', node_name=tmp_node)) 164 vm.shutdown() 165 166 log('') 167 log('--- Confirming writes ---') 168 log('') 169 170 for p in overwrite + remainder: 171 cmd = 'read -P%s %s %s' % p 172 log(cmd) 173 assert qemu_io_silent(base_img_path, '-c', cmd) == 0 174 175 log('') 176 log('Done') 177 178 179def test(use_cbw): 180 with iotests.FilePath('base.img') as base_img_path, \ 181 iotests.FilePath('fleece.img') as fleece_img_path, \ 182 iotests.FilePath('nbd.sock', 183 base_dir=iotests.sock_dir) as nbd_sock_path, \ 184 iotests.VM() as vm: 185 do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm) 186 187 188log('=== Test backup(sync=none) based fleecing ===\n') 189test(False) 190 191log('=== Test filter based fleecing ===\n') 192test(True) 193