1*298c6009SMax Reitz#!/usr/bin/env python 2*298c6009SMax Reitz# 3*298c6009SMax Reitz# Test whether the backing BDSs are correct after completion of a 4*298c6009SMax Reitz# mirror block job; in "existing" modes (drive-mirror with 5*298c6009SMax Reitz# mode=existing and blockdev-mirror) the backing chain should not be 6*298c6009SMax Reitz# overridden. 7*298c6009SMax Reitz# 8*298c6009SMax Reitz# Copyright (C) 2016 Red Hat, Inc. 9*298c6009SMax Reitz# 10*298c6009SMax Reitz# This program is free software; you can redistribute it and/or modify 11*298c6009SMax Reitz# it under the terms of the GNU General Public License as published by 12*298c6009SMax Reitz# the Free Software Foundation; either version 2 of the License, or 13*298c6009SMax Reitz# (at your option) any later version. 14*298c6009SMax Reitz# 15*298c6009SMax Reitz# This program is distributed in the hope that it will be useful, 16*298c6009SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 17*298c6009SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*298c6009SMax Reitz# GNU General Public License for more details. 19*298c6009SMax Reitz# 20*298c6009SMax Reitz# You should have received a copy of the GNU General Public License 21*298c6009SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 22*298c6009SMax Reitz# 23*298c6009SMax Reitz 24*298c6009SMax Reitzimport os 25*298c6009SMax Reitzimport iotests 26*298c6009SMax Reitzfrom iotests import qemu_img 27*298c6009SMax Reitz 28*298c6009SMax Reitzback0_img = os.path.join(iotests.test_dir, 'back0.' + iotests.imgfmt) 29*298c6009SMax Reitzback1_img = os.path.join(iotests.test_dir, 'back1.' + iotests.imgfmt) 30*298c6009SMax Reitzback2_img = os.path.join(iotests.test_dir, 'back2.' + iotests.imgfmt) 31*298c6009SMax Reitzsource_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) 32*298c6009SMax Reitztarget_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) 33*298c6009SMax Reitz 34*298c6009SMax Reitz 35*298c6009SMax Reitz# Class variables for controlling its behavior: 36*298c6009SMax Reitz# 37*298c6009SMax Reitz# existing: If True, explicitly create the target image and blockdev-add it 38*298c6009SMax Reitz# target_backing: If existing is True: Use this filename as the backing file 39*298c6009SMax Reitz# of the target image 40*298c6009SMax Reitz# (None: no backing file) 41*298c6009SMax Reitz# target_blockdev_backing: If existing is True: Pass this dict as "backing" 42*298c6009SMax Reitz# for the blockdev-add command 43*298c6009SMax Reitz# (None: do not pass "backing") 44*298c6009SMax Reitz# target_real_backing: If existing is True: The real filename of the backing 45*298c6009SMax Reitz# image during runtime, only makes sense if 46*298c6009SMax Reitz# target_blockdev_backing is not None 47*298c6009SMax Reitz# (None: same as target_backing) 48*298c6009SMax Reitz 49*298c6009SMax Reitzclass BaseClass(iotests.QMPTestCase): 50*298c6009SMax Reitz target_blockdev_backing = None 51*298c6009SMax Reitz target_real_backing = None 52*298c6009SMax Reitz 53*298c6009SMax Reitz def setUp(self): 54*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, back0_img, '1M') 55*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img, back1_img) 56*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img, back2_img) 57*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img, source_img) 58*298c6009SMax Reitz 59*298c6009SMax Reitz self.vm = iotests.VM() 60*298c6009SMax Reitz self.vm.add_drive(None, '', 'none') 61*298c6009SMax Reitz self.vm.launch() 62*298c6009SMax Reitz 63*298c6009SMax Reitz # Add the BDS via blockdev-add so it stays around after the mirror block 64*298c6009SMax Reitz # job has been completed 65*298c6009SMax Reitz result = self.vm.qmp('blockdev-add', 66*298c6009SMax Reitz options={'node-name': 'source', 67*298c6009SMax Reitz 'driver': iotests.imgfmt, 68*298c6009SMax Reitz 'file': {'driver': 'file', 69*298c6009SMax Reitz 'filename': source_img}}) 70*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 71*298c6009SMax Reitz 72*298c6009SMax Reitz result = self.vm.qmp('x-blockdev-insert-medium', 73*298c6009SMax Reitz device='drive0', node_name='source') 74*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 75*298c6009SMax Reitz 76*298c6009SMax Reitz self.assertIntactSourceBackingChain() 77*298c6009SMax Reitz 78*298c6009SMax Reitz if self.existing: 79*298c6009SMax Reitz if self.target_backing: 80*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, 81*298c6009SMax Reitz '-b', self.target_backing, target_img, '1M') 82*298c6009SMax Reitz else: 83*298c6009SMax Reitz qemu_img('create', '-f', iotests.imgfmt, target_img, '1M') 84*298c6009SMax Reitz 85*298c6009SMax Reitz if self.cmd == 'blockdev-mirror': 86*298c6009SMax Reitz options = { 'node-name': 'target', 87*298c6009SMax Reitz 'driver': iotests.imgfmt, 88*298c6009SMax Reitz 'file': { 'driver': 'file', 89*298c6009SMax Reitz 'filename': target_img } } 90*298c6009SMax Reitz if self.target_blockdev_backing: 91*298c6009SMax Reitz options['backing'] = self.target_blockdev_backing 92*298c6009SMax Reitz 93*298c6009SMax Reitz result = self.vm.qmp('blockdev-add', options=options) 94*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 95*298c6009SMax Reitz 96*298c6009SMax Reitz def tearDown(self): 97*298c6009SMax Reitz self.vm.shutdown() 98*298c6009SMax Reitz os.remove(source_img) 99*298c6009SMax Reitz os.remove(back2_img) 100*298c6009SMax Reitz os.remove(back1_img) 101*298c6009SMax Reitz os.remove(back0_img) 102*298c6009SMax Reitz try: 103*298c6009SMax Reitz os.remove(target_img) 104*298c6009SMax Reitz except OSError: 105*298c6009SMax Reitz pass 106*298c6009SMax Reitz 107*298c6009SMax Reitz def findBlockNode(self, node_name, id=None): 108*298c6009SMax Reitz if id: 109*298c6009SMax Reitz result = self.vm.qmp('query-block') 110*298c6009SMax Reitz for device in result['return']: 111*298c6009SMax Reitz if device['device'] == id: 112*298c6009SMax Reitz if node_name: 113*298c6009SMax Reitz self.assert_qmp(device, 'inserted/node-name', node_name) 114*298c6009SMax Reitz return device['inserted'] 115*298c6009SMax Reitz else: 116*298c6009SMax Reitz result = self.vm.qmp('query-named-block-nodes') 117*298c6009SMax Reitz for node in result['return']: 118*298c6009SMax Reitz if node['node-name'] == node_name: 119*298c6009SMax Reitz return node 120*298c6009SMax Reitz 121*298c6009SMax Reitz self.fail('Cannot find node %s/%s' % (id, node_name)) 122*298c6009SMax Reitz 123*298c6009SMax Reitz def assertIntactSourceBackingChain(self): 124*298c6009SMax Reitz node = self.findBlockNode('source') 125*298c6009SMax Reitz 126*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', 127*298c6009SMax Reitz source_img) 128*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', 129*298c6009SMax Reitz back2_img) 130*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 2 + '/filename', 131*298c6009SMax Reitz back1_img) 132*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 3 + '/filename', 133*298c6009SMax Reitz back0_img) 134*298c6009SMax Reitz self.assert_qmp_absent(node, 'image' + '/backing-image' * 4) 135*298c6009SMax Reitz 136*298c6009SMax Reitz def assertCorrectBackingImage(self, node, default_image): 137*298c6009SMax Reitz if self.existing: 138*298c6009SMax Reitz if self.target_real_backing: 139*298c6009SMax Reitz image = self.target_real_backing 140*298c6009SMax Reitz else: 141*298c6009SMax Reitz image = self.target_backing 142*298c6009SMax Reitz else: 143*298c6009SMax Reitz image = default_image 144*298c6009SMax Reitz 145*298c6009SMax Reitz if image: 146*298c6009SMax Reitz self.assert_qmp(node, 'image/backing-image/filename', image) 147*298c6009SMax Reitz else: 148*298c6009SMax Reitz self.assert_qmp_absent(node, 'image/backing-image') 149*298c6009SMax Reitz 150*298c6009SMax Reitz 151*298c6009SMax Reitz# Class variables for controlling its behavior: 152*298c6009SMax Reitz# 153*298c6009SMax Reitz# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror 154*298c6009SMax Reitz 155*298c6009SMax Reitzclass MirrorBaseClass(BaseClass): 156*298c6009SMax Reitz def runMirror(self, sync): 157*298c6009SMax Reitz if self.cmd == 'blockdev-mirror': 158*298c6009SMax Reitz result = self.vm.qmp(self.cmd, device='drive0', sync=sync, 159*298c6009SMax Reitz target='target') 160*298c6009SMax Reitz else: 161*298c6009SMax Reitz if self.existing: 162*298c6009SMax Reitz mode = 'existing' 163*298c6009SMax Reitz else: 164*298c6009SMax Reitz mode = 'absolute-paths' 165*298c6009SMax Reitz result = self.vm.qmp(self.cmd, device='drive0', sync=sync, 166*298c6009SMax Reitz target=target_img, format=iotests.imgfmt, 167*298c6009SMax Reitz mode=mode, node_name='target') 168*298c6009SMax Reitz 169*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 170*298c6009SMax Reitz 171*298c6009SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 172*298c6009SMax Reitz 173*298c6009SMax Reitz result = self.vm.qmp('block-job-complete', device='drive0') 174*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 175*298c6009SMax Reitz 176*298c6009SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 177*298c6009SMax Reitz 178*298c6009SMax Reitz def testFull(self): 179*298c6009SMax Reitz self.runMirror('full') 180*298c6009SMax Reitz 181*298c6009SMax Reitz node = self.findBlockNode('target', 'drive0') 182*298c6009SMax Reitz self.assertCorrectBackingImage(node, None) 183*298c6009SMax Reitz self.assertIntactSourceBackingChain() 184*298c6009SMax Reitz 185*298c6009SMax Reitz def testTop(self): 186*298c6009SMax Reitz self.runMirror('top') 187*298c6009SMax Reitz 188*298c6009SMax Reitz node = self.findBlockNode('target', 'drive0') 189*298c6009SMax Reitz self.assertCorrectBackingImage(node, back2_img) 190*298c6009SMax Reitz self.assertIntactSourceBackingChain() 191*298c6009SMax Reitz 192*298c6009SMax Reitz def testNone(self): 193*298c6009SMax Reitz self.runMirror('none') 194*298c6009SMax Reitz 195*298c6009SMax Reitz node = self.findBlockNode('target', 'drive0') 196*298c6009SMax Reitz self.assertCorrectBackingImage(node, source_img) 197*298c6009SMax Reitz self.assertIntactSourceBackingChain() 198*298c6009SMax Reitz 199*298c6009SMax Reitz 200*298c6009SMax Reitzclass TestDriveMirrorAbsolutePaths(MirrorBaseClass): 201*298c6009SMax Reitz cmd = 'drive-mirror' 202*298c6009SMax Reitz existing = False 203*298c6009SMax Reitz 204*298c6009SMax Reitzclass TestDriveMirrorExistingNoBacking(MirrorBaseClass): 205*298c6009SMax Reitz cmd = 'drive-mirror' 206*298c6009SMax Reitz existing = True 207*298c6009SMax Reitz target_backing = None 208*298c6009SMax Reitz 209*298c6009SMax Reitzclass TestDriveMirrorExistingBacking(MirrorBaseClass): 210*298c6009SMax Reitz cmd = 'drive-mirror' 211*298c6009SMax Reitz existing = True 212*298c6009SMax Reitz target_backing = 'null-co://' 213*298c6009SMax Reitz 214*298c6009SMax Reitzclass TestBlockdevMirrorNoBacking(MirrorBaseClass): 215*298c6009SMax Reitz cmd = 'blockdev-mirror' 216*298c6009SMax Reitz existing = True 217*298c6009SMax Reitz target_backing = None 218*298c6009SMax Reitz 219*298c6009SMax Reitzclass TestBlockdevMirrorBacking(MirrorBaseClass): 220*298c6009SMax Reitz cmd = 'blockdev-mirror' 221*298c6009SMax Reitz existing = True 222*298c6009SMax Reitz target_backing = 'null-co://' 223*298c6009SMax Reitz 224*298c6009SMax Reitzclass TestBlockdevMirrorForcedBacking(MirrorBaseClass): 225*298c6009SMax Reitz cmd = 'blockdev-mirror' 226*298c6009SMax Reitz existing = True 227*298c6009SMax Reitz target_backing = None 228*298c6009SMax Reitz target_blockdev_backing = { 'driver': 'null-co' } 229*298c6009SMax Reitz target_real_backing = 'null-co://' 230*298c6009SMax Reitz 231*298c6009SMax Reitz 232*298c6009SMax Reitzclass TestCommit(BaseClass): 233*298c6009SMax Reitz existing = False 234*298c6009SMax Reitz 235*298c6009SMax Reitz def testCommit(self): 236*298c6009SMax Reitz result = self.vm.qmp('block-commit', device='drive0', base=back1_img) 237*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 238*298c6009SMax Reitz 239*298c6009SMax Reitz self.vm.event_wait('BLOCK_JOB_READY') 240*298c6009SMax Reitz 241*298c6009SMax Reitz result = self.vm.qmp('block-job-complete', device='drive0') 242*298c6009SMax Reitz self.assert_qmp(result, 'return', {}) 243*298c6009SMax Reitz 244*298c6009SMax Reitz self.vm.event_wait('BLOCK_JOB_COMPLETED') 245*298c6009SMax Reitz 246*298c6009SMax Reitz node = self.findBlockNode(None, 'drive0') 247*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename', 248*298c6009SMax Reitz back1_img) 249*298c6009SMax Reitz self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename', 250*298c6009SMax Reitz back0_img) 251*298c6009SMax Reitz self.assert_qmp_absent(node, 'image' + '/backing-image' * 2 + 252*298c6009SMax Reitz '/filename') 253*298c6009SMax Reitz 254*298c6009SMax Reitz self.assertIntactSourceBackingChain() 255*298c6009SMax Reitz 256*298c6009SMax Reitz 257*298c6009SMax ReitzBaseClass = None 258*298c6009SMax ReitzMirrorBaseClass = None 259*298c6009SMax Reitz 260*298c6009SMax Reitzif __name__ == '__main__': 261*298c6009SMax Reitz iotests.main(supported_fmts=['qcow2']) 262