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 51with iotests.FilePath('base.img') as base_img_path, \ 52 iotests.FilePath('fleece.img') as fleece_img_path, \ 53 iotests.FilePath('nbd.sock', 54 base_dir=iotests.sock_dir) as nbd_sock_path, \ 55 iotests.VM() as vm: 56 57 log('--- Setting up images ---') 58 log('') 59 60 assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 61 assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0 62 63 for p in patterns: 64 qemu_io('-f', iotests.imgfmt, 65 '-c', 'write -P%s %s %s' % p, base_img_path) 66 67 log('Done') 68 69 log('') 70 log('--- Launching VM ---') 71 log('') 72 73 vm.add_drive(base_img_path) 74 vm.launch() 75 log('Done') 76 77 log('') 78 log('--- Setting up Fleecing Graph ---') 79 log('') 80 81 src_node = 'drive0' 82 tgt_node = 'fleeceNode' 83 84 # create tgt_node backed by src_node 85 log(vm.qmp('blockdev-add', { 86 'driver': 'qcow2', 87 'node-name': tgt_node, 88 'file': { 89 'driver': 'file', 90 'filename': fleece_img_path, 91 }, 92 'backing': src_node, 93 })) 94 95 # Establish COW from source to fleecing node 96 log(vm.qmp('blockdev-backup', 97 device=src_node, 98 target=tgt_node, 99 sync='none')) 100 101 log('') 102 log('--- Setting up NBD Export ---') 103 log('') 104 105 nbd_uri = 'nbd+unix:///%s?socket=%s' % (tgt_node, nbd_sock_path) 106 log(vm.qmp('nbd-server-start', 107 {'addr': { 'type': 'unix', 108 'data': { 'path': nbd_sock_path } } })) 109 110 log(vm.qmp('nbd-server-add', device=tgt_node)) 111 112 log('') 113 log('--- Sanity Check ---') 114 log('') 115 116 for p in patterns + zeroes: 117 cmd = 'read -P%s %s %s' % p 118 log(cmd) 119 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 120 121 log('') 122 log('--- Testing COW ---') 123 log('') 124 125 for p in overwrite: 126 cmd = 'write -P%s %s %s' % p 127 log(cmd) 128 log(vm.hmp_qemu_io(src_node, cmd)) 129 130 log('') 131 log('--- Verifying Data ---') 132 log('') 133 134 for p in patterns + zeroes: 135 cmd = 'read -P%s %s %s' % p 136 log(cmd) 137 assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 138 139 log('') 140 log('--- Cleanup ---') 141 log('') 142 143 log(vm.qmp('block-job-cancel', device=src_node)) 144 e = vm.event_wait('BLOCK_JOB_CANCELLED') 145 assert e is not None 146 log(e, filters=[iotests.filter_qmp_event]) 147 log(vm.qmp('nbd-server-stop')) 148 log(vm.qmp('blockdev-del', node_name=tgt_node)) 149 vm.shutdown() 150 151 log('') 152 log('--- Confirming writes ---') 153 log('') 154 155 for p in overwrite + remainder: 156 cmd = 'read -P%s %s %s' % p 157 log(cmd) 158 assert qemu_io_silent(base_img_path, '-c', cmd) == 0 159 160 log('') 161 log('Done') 162