1*bf3e50f6SAlberto Garcia#!/usr/bin/env python 2*bf3e50f6SAlberto Garcia# 3*bf3e50f6SAlberto Garcia# Test cases for the QMP 'x-blockdev-reopen' command 4*bf3e50f6SAlberto Garcia# 5*bf3e50f6SAlberto Garcia# Copyright (C) 2018-2019 Igalia, S.L. 6*bf3e50f6SAlberto Garcia# Author: Alberto Garcia <berto@igalia.com> 7*bf3e50f6SAlberto Garcia# 8*bf3e50f6SAlberto Garcia# This program is free software; you can redistribute it and/or modify 9*bf3e50f6SAlberto Garcia# it under the terms of the GNU General Public License as published by 10*bf3e50f6SAlberto Garcia# the Free Software Foundation; either version 2 of the License, or 11*bf3e50f6SAlberto Garcia# (at your option) any later version. 12*bf3e50f6SAlberto Garcia# 13*bf3e50f6SAlberto Garcia# This program is distributed in the hope that it will be useful, 14*bf3e50f6SAlberto Garcia# but WITHOUT ANY WARRANTY; without even the implied warranty of 15*bf3e50f6SAlberto Garcia# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16*bf3e50f6SAlberto Garcia# GNU General Public License for more details. 17*bf3e50f6SAlberto Garcia# 18*bf3e50f6SAlberto Garcia# You should have received a copy of the GNU General Public License 19*bf3e50f6SAlberto Garcia# along with this program. If not, see <http://www.gnu.org/licenses/>. 20*bf3e50f6SAlberto Garcia# 21*bf3e50f6SAlberto Garcia 22*bf3e50f6SAlberto Garciaimport os 23*bf3e50f6SAlberto Garciaimport re 24*bf3e50f6SAlberto Garciaimport iotests 25*bf3e50f6SAlberto Garciaimport copy 26*bf3e50f6SAlberto Garciaimport json 27*bf3e50f6SAlberto Garciafrom iotests import qemu_img, qemu_io 28*bf3e50f6SAlberto Garcia 29*bf3e50f6SAlberto Garciahd_path = [ 30*bf3e50f6SAlberto Garcia os.path.join(iotests.test_dir, 'hd0.img'), 31*bf3e50f6SAlberto Garcia os.path.join(iotests.test_dir, 'hd1.img'), 32*bf3e50f6SAlberto Garcia os.path.join(iotests.test_dir, 'hd2.img') 33*bf3e50f6SAlberto Garcia] 34*bf3e50f6SAlberto Garcia 35*bf3e50f6SAlberto Garciadef hd_opts(idx): 36*bf3e50f6SAlberto Garcia return {'driver': iotests.imgfmt, 37*bf3e50f6SAlberto Garcia 'node-name': 'hd%d' % idx, 38*bf3e50f6SAlberto Garcia 'file': {'driver': 'file', 39*bf3e50f6SAlberto Garcia 'node-name': 'hd%d-file' % idx, 40*bf3e50f6SAlberto Garcia 'filename': hd_path[idx] } } 41*bf3e50f6SAlberto Garcia 42*bf3e50f6SAlberto Garciaclass TestBlockdevReopen(iotests.QMPTestCase): 43*bf3e50f6SAlberto Garcia total_io_cmds = 0 44*bf3e50f6SAlberto Garcia 45*bf3e50f6SAlberto Garcia def setUp(self): 46*bf3e50f6SAlberto Garcia qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M') 47*bf3e50f6SAlberto Garcia qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1]) 48*bf3e50f6SAlberto Garcia qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M') 49*bf3e50f6SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0]) 50*bf3e50f6SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1]) 51*bf3e50f6SAlberto Garcia qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2]) 52*bf3e50f6SAlberto Garcia self.vm = iotests.VM() 53*bf3e50f6SAlberto Garcia self.vm.launch() 54*bf3e50f6SAlberto Garcia 55*bf3e50f6SAlberto Garcia def tearDown(self): 56*bf3e50f6SAlberto Garcia self.vm.shutdown() 57*bf3e50f6SAlberto Garcia self.check_qemu_io_errors() 58*bf3e50f6SAlberto Garcia os.remove(hd_path[0]) 59*bf3e50f6SAlberto Garcia os.remove(hd_path[1]) 60*bf3e50f6SAlberto Garcia os.remove(hd_path[2]) 61*bf3e50f6SAlberto Garcia 62*bf3e50f6SAlberto Garcia # The output of qemu-io is not returned by vm.hmp_qemu_io() but 63*bf3e50f6SAlberto Garcia # it's stored in the log and can only be read when the VM has been 64*bf3e50f6SAlberto Garcia # shut down. This function runs qemu-io and keeps track of the 65*bf3e50f6SAlberto Garcia # number of times it's been called. 66*bf3e50f6SAlberto Garcia def run_qemu_io(self, img, cmd): 67*bf3e50f6SAlberto Garcia result = self.vm.hmp_qemu_io(img, cmd) 68*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', '') 69*bf3e50f6SAlberto Garcia self.total_io_cmds += 1 70*bf3e50f6SAlberto Garcia 71*bf3e50f6SAlberto Garcia # Once the VM is shut down we can parse the log and see if qemu-io 72*bf3e50f6SAlberto Garcia # ran without errors. 73*bf3e50f6SAlberto Garcia def check_qemu_io_errors(self): 74*bf3e50f6SAlberto Garcia self.assertFalse(self.vm.is_running()) 75*bf3e50f6SAlberto Garcia found = 0 76*bf3e50f6SAlberto Garcia log = self.vm.get_log() 77*bf3e50f6SAlberto Garcia for line in log.split("\n"): 78*bf3e50f6SAlberto Garcia if line.startswith("Pattern verification failed"): 79*bf3e50f6SAlberto Garcia raise Exception("%s (command #%d)" % (line, found)) 80*bf3e50f6SAlberto Garcia if re.match("read .*/.* bytes at offset", line): 81*bf3e50f6SAlberto Garcia found += 1 82*bf3e50f6SAlberto Garcia self.assertEqual(found, self.total_io_cmds, 83*bf3e50f6SAlberto Garcia "Expected output of %d qemu-io commands, found %d" % 84*bf3e50f6SAlberto Garcia (found, self.total_io_cmds)) 85*bf3e50f6SAlberto Garcia 86*bf3e50f6SAlberto Garcia # Run x-blockdev-reopen with 'opts' but applying 'newopts' 87*bf3e50f6SAlberto Garcia # on top of it. The original 'opts' dict is unmodified 88*bf3e50f6SAlberto Garcia def reopen(self, opts, newopts = {}, errmsg = None): 89*bf3e50f6SAlberto Garcia opts = copy.deepcopy(opts) 90*bf3e50f6SAlberto Garcia 91*bf3e50f6SAlberto Garcia # Apply the changes from 'newopts' on top of 'opts' 92*bf3e50f6SAlberto Garcia for key in newopts: 93*bf3e50f6SAlberto Garcia value = newopts[key] 94*bf3e50f6SAlberto Garcia # If key has the form "foo.bar" then we need to do 95*bf3e50f6SAlberto Garcia # opts["foo"]["bar"] = value, not opts["foo.bar"] = value 96*bf3e50f6SAlberto Garcia subdict = opts 97*bf3e50f6SAlberto Garcia while key.find('.') != -1: 98*bf3e50f6SAlberto Garcia [prefix, key] = key.split('.', 1) 99*bf3e50f6SAlberto Garcia subdict = opts[prefix] 100*bf3e50f6SAlberto Garcia subdict[key] = value 101*bf3e50f6SAlberto Garcia 102*bf3e50f6SAlberto Garcia result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts) 103*bf3e50f6SAlberto Garcia if errmsg: 104*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 105*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', errmsg) 106*bf3e50f6SAlberto Garcia else: 107*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 108*bf3e50f6SAlberto Garcia 109*bf3e50f6SAlberto Garcia 110*bf3e50f6SAlberto Garcia # Run query-named-block-nodes and return the specified entry 111*bf3e50f6SAlberto Garcia def get_node(self, node_name): 112*bf3e50f6SAlberto Garcia result = self.vm.qmp('query-named-block-nodes') 113*bf3e50f6SAlberto Garcia for node in result['return']: 114*bf3e50f6SAlberto Garcia if node['node-name'] == node_name: 115*bf3e50f6SAlberto Garcia return node 116*bf3e50f6SAlberto Garcia return None 117*bf3e50f6SAlberto Garcia 118*bf3e50f6SAlberto Garcia # Run 'query-named-block-nodes' and compare its output with the 119*bf3e50f6SAlberto Garcia # value passed by the user in 'graph' 120*bf3e50f6SAlberto Garcia def check_node_graph(self, graph): 121*bf3e50f6SAlberto Garcia result = self.vm.qmp('query-named-block-nodes') 122*bf3e50f6SAlberto Garcia self.assertEqual(json.dumps(graph, sort_keys=True), 123*bf3e50f6SAlberto Garcia json.dumps(result, sort_keys=True)) 124*bf3e50f6SAlberto Garcia 125*bf3e50f6SAlberto Garcia # This test opens one single disk image (without backing files) 126*bf3e50f6SAlberto Garcia # and tries to reopen it with illegal / incorrect parameters. 127*bf3e50f6SAlberto Garcia def test_incorrect_parameters_single_file(self): 128*bf3e50f6SAlberto Garcia # Open 'hd0' only (no backing files) 129*bf3e50f6SAlberto Garcia opts = hd_opts(0) 130*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 131*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 132*bf3e50f6SAlberto Garcia original_graph = self.vm.qmp('query-named-block-nodes') 133*bf3e50f6SAlberto Garcia 134*bf3e50f6SAlberto Garcia # We can reopen the image passing the same options 135*bf3e50f6SAlberto Garcia self.reopen(opts) 136*bf3e50f6SAlberto Garcia 137*bf3e50f6SAlberto Garcia # We can also reopen passing a child reference in 'file' 138*bf3e50f6SAlberto Garcia self.reopen(opts, {'file': 'hd0-file'}) 139*bf3e50f6SAlberto Garcia 140*bf3e50f6SAlberto Garcia # We cannot change any of these 141*bf3e50f6SAlberto Garcia self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'") 142*bf3e50f6SAlberto Garcia self.reopen(opts, {'node-name': ''}, "Cannot find node named ''") 143*bf3e50f6SAlberto Garcia self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string") 144*bf3e50f6SAlberto Garcia self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") 145*bf3e50f6SAlberto Garcia self.reopen(opts, {'driver': ''}, "Invalid parameter ''") 146*bf3e50f6SAlberto Garcia self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string") 147*bf3e50f6SAlberto Garcia self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'") 148*bf3e50f6SAlberto Garcia self.reopen(opts, {'file': ''}, "Cannot change the option 'file'") 149*bf3e50f6SAlberto Garcia self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef") 150*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'") 151*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'") 152*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'") 153*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'") 154*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") 155*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string") 156*bf3e50f6SAlberto Garcia 157*bf3e50f6SAlberto Garcia # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it 158*bf3e50f6SAlberto Garcia del opts['node-name'] 159*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Node name not specified") 160*bf3e50f6SAlberto Garcia 161*bf3e50f6SAlberto Garcia # Check that nothing has changed 162*bf3e50f6SAlberto Garcia self.check_node_graph(original_graph) 163*bf3e50f6SAlberto Garcia 164*bf3e50f6SAlberto Garcia # Remove the node 165*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 166*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 167*bf3e50f6SAlberto Garcia 168*bf3e50f6SAlberto Garcia # This test opens an image with a backing file and tries to reopen 169*bf3e50f6SAlberto Garcia # it with illegal / incorrect parameters. 170*bf3e50f6SAlberto Garcia def test_incorrect_parameters_backing_file(self): 171*bf3e50f6SAlberto Garcia # Open hd1 omitting the backing options (hd0 will be opened 172*bf3e50f6SAlberto Garcia # with the default options) 173*bf3e50f6SAlberto Garcia opts = hd_opts(1) 174*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 175*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 176*bf3e50f6SAlberto Garcia original_graph = self.vm.qmp('query-named-block-nodes') 177*bf3e50f6SAlberto Garcia 178*bf3e50f6SAlberto Garcia # We can't reopen the image passing the same options, 'backing' is mandatory 179*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd1'") 180*bf3e50f6SAlberto Garcia 181*bf3e50f6SAlberto Garcia # Everything works if we pass 'backing' using the existing node name 182*bf3e50f6SAlberto Garcia for node in original_graph['return']: 183*bf3e50f6SAlberto Garcia if node['drv'] == iotests.imgfmt and node['file'] == hd_path[0]: 184*bf3e50f6SAlberto Garcia backing_node_name = node['node-name'] 185*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': backing_node_name}) 186*bf3e50f6SAlberto Garcia 187*bf3e50f6SAlberto Garcia # We can't use a non-existing or empty (non-NULL) node as the backing image 188*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found") 189*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=") 190*bf3e50f6SAlberto Garcia 191*bf3e50f6SAlberto Garcia # We can reopen the image just fine if we specify the backing options 192*bf3e50f6SAlberto Garcia opts['backing'] = {'driver': iotests.imgfmt, 193*bf3e50f6SAlberto Garcia 'file': {'driver': 'file', 194*bf3e50f6SAlberto Garcia 'filename': hd_path[0]}} 195*bf3e50f6SAlberto Garcia self.reopen(opts) 196*bf3e50f6SAlberto Garcia 197*bf3e50f6SAlberto Garcia # We cannot change any of these options 198*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'") 199*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'") 200*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'") 201*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'") 202*bf3e50f6SAlberto Garcia 203*bf3e50f6SAlberto Garcia # Check that nothing has changed since the beginning 204*bf3e50f6SAlberto Garcia self.check_node_graph(original_graph) 205*bf3e50f6SAlberto Garcia 206*bf3e50f6SAlberto Garcia # Remove the node 207*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') 208*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 209*bf3e50f6SAlberto Garcia 210*bf3e50f6SAlberto Garcia # Reopen an image several times changing some of its options 211*bf3e50f6SAlberto Garcia def test_reopen(self): 212*bf3e50f6SAlberto Garcia # Open the hd1 image passing all backing options 213*bf3e50f6SAlberto Garcia opts = hd_opts(1) 214*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(0) 215*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 216*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 217*bf3e50f6SAlberto Garcia original_graph = self.vm.qmp('query-named-block-nodes') 218*bf3e50f6SAlberto Garcia 219*bf3e50f6SAlberto Garcia # We can reopen the image passing the same options 220*bf3e50f6SAlberto Garcia self.reopen(opts) 221*bf3e50f6SAlberto Garcia 222*bf3e50f6SAlberto Garcia # Reopen in read-only mode 223*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'ro', False) 224*bf3e50f6SAlberto Garcia 225*bf3e50f6SAlberto Garcia self.reopen(opts, {'read-only': True}) 226*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'ro', True) 227*bf3e50f6SAlberto Garcia self.reopen(opts) 228*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'ro', False) 229*bf3e50f6SAlberto Garcia 230*bf3e50f6SAlberto Garcia # Change the cache options 231*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) 232*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) 233*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) 234*bf3e50f6SAlberto Garcia self.reopen(opts, {'cache': { 'direct': True, 'no-flush': True }}) 235*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) 236*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/direct', True) 237*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True) 238*bf3e50f6SAlberto Garcia 239*bf3e50f6SAlberto Garcia # Reopen again with the original options 240*bf3e50f6SAlberto Garcia self.reopen(opts) 241*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) 242*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) 243*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) 244*bf3e50f6SAlberto Garcia 245*bf3e50f6SAlberto Garcia # Change 'detect-zeroes' and 'discard' 246*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') 247*bf3e50f6SAlberto Garcia self.reopen(opts, {'detect-zeroes': 'on'}) 248*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') 249*bf3e50f6SAlberto Garcia self.reopen(opts, {'detect-zeroes': 'unmap'}, 250*bf3e50f6SAlberto Garcia "setting detect-zeroes to unmap is not allowed " + 251*bf3e50f6SAlberto Garcia "without setting discard operation to unmap") 252*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') 253*bf3e50f6SAlberto Garcia self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'}) 254*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap') 255*bf3e50f6SAlberto Garcia self.reopen(opts) 256*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') 257*bf3e50f6SAlberto Garcia 258*bf3e50f6SAlberto Garcia # Changing 'force-share' is currently not supported 259*bf3e50f6SAlberto Garcia self.reopen(opts, {'force-share': True}, "Cannot change the option 'force-share'") 260*bf3e50f6SAlberto Garcia 261*bf3e50f6SAlberto Garcia # Change some qcow2-specific options 262*bf3e50f6SAlberto Garcia # No way to test for success other than checking the return message 263*bf3e50f6SAlberto Garcia if iotests.imgfmt == 'qcow2': 264*bf3e50f6SAlberto Garcia self.reopen(opts, {'l2-cache-entry-size': 128 * 1024}, 265*bf3e50f6SAlberto Garcia "L2 cache entry size must be a power of two "+ 266*bf3e50f6SAlberto Garcia "between 512 and the cluster size (65536)") 267*bf3e50f6SAlberto Garcia self.reopen(opts, {'l2-cache-size': 1024 * 1024, 268*bf3e50f6SAlberto Garcia 'cache-size': 512 * 1024}, 269*bf3e50f6SAlberto Garcia "l2-cache-size may not exceed cache-size") 270*bf3e50f6SAlberto Garcia self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024, 271*bf3e50f6SAlberto Garcia 'refcount-cache-size': 4 * 1024 * 1024, 272*bf3e50f6SAlberto Garcia 'l2-cache-entry-size': 32 * 1024}) 273*bf3e50f6SAlberto Garcia self.reopen(opts, {'pass-discard-request': True}) 274*bf3e50f6SAlberto Garcia 275*bf3e50f6SAlberto Garcia # Check that nothing has changed since the beginning 276*bf3e50f6SAlberto Garcia # (from the parameters that we can check) 277*bf3e50f6SAlberto Garcia self.check_node_graph(original_graph) 278*bf3e50f6SAlberto Garcia 279*bf3e50f6SAlberto Garcia # Check that the node names (other than the top-level one) are optional 280*bf3e50f6SAlberto Garcia del opts['file']['node-name'] 281*bf3e50f6SAlberto Garcia del opts['backing']['node-name'] 282*bf3e50f6SAlberto Garcia del opts['backing']['file']['node-name'] 283*bf3e50f6SAlberto Garcia self.reopen(opts) 284*bf3e50f6SAlberto Garcia self.check_node_graph(original_graph) 285*bf3e50f6SAlberto Garcia 286*bf3e50f6SAlberto Garcia # Reopen setting backing = null, this removes the backing image from the chain 287*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': None}) 288*bf3e50f6SAlberto Garcia self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image') 289*bf3e50f6SAlberto Garcia 290*bf3e50f6SAlberto Garcia # Open the 'hd0' image 291*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0)) 292*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 293*bf3e50f6SAlberto Garcia 294*bf3e50f6SAlberto Garcia # Reopen the hd1 image setting 'hd0' as its backing image 295*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd0'}) 296*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0]) 297*bf3e50f6SAlberto Garcia 298*bf3e50f6SAlberto Garcia # Check that nothing has changed since the beginning 299*bf3e50f6SAlberto Garcia self.reopen(hd_opts(0), {'read-only': True}) 300*bf3e50f6SAlberto Garcia self.check_node_graph(original_graph) 301*bf3e50f6SAlberto Garcia 302*bf3e50f6SAlberto Garcia # The backing file (hd0) is now a reference, we cannot change backing.* anymore 303*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change the option 'backing.driver'") 304*bf3e50f6SAlberto Garcia 305*bf3e50f6SAlberto Garcia # We can't remove 'hd0' while it's a backing image of 'hd1' 306*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 307*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 308*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'") 309*bf3e50f6SAlberto Garcia 310*bf3e50f6SAlberto Garcia # But we can remove both nodes if done in the proper order 311*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') 312*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 313*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 314*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 315*bf3e50f6SAlberto Garcia 316*bf3e50f6SAlberto Garcia # Reopen a raw image and see the effect of changing the 'offset' option 317*bf3e50f6SAlberto Garcia def test_reopen_raw(self): 318*bf3e50f6SAlberto Garcia opts = {'driver': 'raw', 'node-name': 'hd0', 319*bf3e50f6SAlberto Garcia 'file': { 'driver': 'file', 320*bf3e50f6SAlberto Garcia 'filename': hd_path[0], 321*bf3e50f6SAlberto Garcia 'node-name': 'hd0-file' } } 322*bf3e50f6SAlberto Garcia 323*bf3e50f6SAlberto Garcia # First we create a 2MB raw file, and fill each half with a 324*bf3e50f6SAlberto Garcia # different value 325*bf3e50f6SAlberto Garcia qemu_img('create', '-f', 'raw', hd_path[0], '2M') 326*bf3e50f6SAlberto Garcia qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0]) 327*bf3e50f6SAlberto Garcia qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0]) 328*bf3e50f6SAlberto Garcia 329*bf3e50f6SAlberto Garcia # Open the raw file with QEMU 330*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 331*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 332*bf3e50f6SAlberto Garcia 333*bf3e50f6SAlberto Garcia # Read 1MB from offset 0 334*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 335*bf3e50f6SAlberto Garcia 336*bf3e50f6SAlberto Garcia # Reopen the image with a 1MB offset. 337*bf3e50f6SAlberto Garcia # Now the results are different 338*bf3e50f6SAlberto Garcia self.reopen(opts, {'offset': 1024*1024}) 339*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa1 0 1M") 340*bf3e50f6SAlberto Garcia 341*bf3e50f6SAlberto Garcia # Reopen again with the original options. 342*bf3e50f6SAlberto Garcia # We get the original results again 343*bf3e50f6SAlberto Garcia self.reopen(opts) 344*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 345*bf3e50f6SAlberto Garcia 346*bf3e50f6SAlberto Garcia # Remove the block device 347*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 348*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 349*bf3e50f6SAlberto Garcia 350*bf3e50f6SAlberto Garcia # Omitting an option should reset it to the default value, but if 351*bf3e50f6SAlberto Garcia # an option cannot be changed it shouldn't be possible to reset it 352*bf3e50f6SAlberto Garcia # to its default value either 353*bf3e50f6SAlberto Garcia def test_reset_default_values(self): 354*bf3e50f6SAlberto Garcia opts = {'driver': 'qcow2', 'node-name': 'hd0', 355*bf3e50f6SAlberto Garcia 'file': { 'driver': 'file', 356*bf3e50f6SAlberto Garcia 'filename': hd_path[0], 357*bf3e50f6SAlberto Garcia 'x-check-cache-dropped': True, # This one can be changed 358*bf3e50f6SAlberto Garcia 'locking': 'off', # This one can NOT be changed 359*bf3e50f6SAlberto Garcia 'node-name': 'hd0-file' } } 360*bf3e50f6SAlberto Garcia 361*bf3e50f6SAlberto Garcia # Open the file with QEMU 362*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 363*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 364*bf3e50f6SAlberto Garcia 365*bf3e50f6SAlberto Garcia # file.x-check-cache-dropped can be changed... 366*bf3e50f6SAlberto Garcia self.reopen(opts, { 'file.x-check-cache-dropped': False }) 367*bf3e50f6SAlberto Garcia # ...and dropped completely (resetting to the default value) 368*bf3e50f6SAlberto Garcia del opts['file']['x-check-cache-dropped'] 369*bf3e50f6SAlberto Garcia self.reopen(opts) 370*bf3e50f6SAlberto Garcia 371*bf3e50f6SAlberto Garcia # file.locking cannot be changed nor reset to the default value 372*bf3e50f6SAlberto Garcia self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'") 373*bf3e50f6SAlberto Garcia del opts['file']['locking'] 374*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") 375*bf3e50f6SAlberto Garcia # But we can reopen it if we maintain its previous value 376*bf3e50f6SAlberto Garcia self.reopen(opts, { 'file.locking': 'off' }) 377*bf3e50f6SAlberto Garcia 378*bf3e50f6SAlberto Garcia # Remove the block device 379*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 380*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 381*bf3e50f6SAlberto Garcia 382*bf3e50f6SAlberto Garcia # This test modifies the node graph a few times by changing the 383*bf3e50f6SAlberto Garcia # 'backing' option on reopen and verifies that the guest data that 384*bf3e50f6SAlberto Garcia # is read afterwards is consistent with the graph changes. 385*bf3e50f6SAlberto Garcia def test_io_with_graph_changes(self): 386*bf3e50f6SAlberto Garcia opts = [] 387*bf3e50f6SAlberto Garcia 388*bf3e50f6SAlberto Garcia # Open hd0, hd1 and hd2 without any backing image 389*bf3e50f6SAlberto Garcia for i in range(3): 390*bf3e50f6SAlberto Garcia opts.append(hd_opts(i)) 391*bf3e50f6SAlberto Garcia opts[i]['backing'] = None 392*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) 393*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 394*bf3e50f6SAlberto Garcia 395*bf3e50f6SAlberto Garcia # hd0 396*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 397*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0 1M 1M") 398*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0 2M 1M") 399*bf3e50f6SAlberto Garcia 400*bf3e50f6SAlberto Garcia # hd1 <- hd0 401*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'hd1'}) 402*bf3e50f6SAlberto Garcia 403*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 404*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") 405*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0 2M 1M") 406*bf3e50f6SAlberto Garcia 407*bf3e50f6SAlberto Garcia # hd1 <- hd0 , hd1 <- hd2 408*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': 'hd1'}) 409*bf3e50f6SAlberto Garcia 410*bf3e50f6SAlberto Garcia self.run_qemu_io("hd2", "read -P 0 0 1M") 411*bf3e50f6SAlberto Garcia self.run_qemu_io("hd2", "read -P 0xa1 1M 1M") 412*bf3e50f6SAlberto Garcia self.run_qemu_io("hd2", "read -P 0xa2 2M 1M") 413*bf3e50f6SAlberto Garcia 414*bf3e50f6SAlberto Garcia # hd1 <- hd2 <- hd0 415*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'hd2'}) 416*bf3e50f6SAlberto Garcia 417*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 418*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") 419*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") 420*bf3e50f6SAlberto Garcia 421*bf3e50f6SAlberto Garcia # hd2 <- hd0 422*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': None}) 423*bf3e50f6SAlberto Garcia 424*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 425*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0 1M 1M") 426*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") 427*bf3e50f6SAlberto Garcia 428*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 429*bf3e50f6SAlberto Garcia self.reopen(opts[1], {'backing': 'hd2'}) 430*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'hd1'}) 431*bf3e50f6SAlberto Garcia 432*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa0 0 1M") 433*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") 434*bf3e50f6SAlberto Garcia self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") 435*bf3e50f6SAlberto Garcia 436*bf3e50f6SAlberto Garcia # Illegal operation: hd2 is a child of hd1 437*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': 'hd1'}, 438*bf3e50f6SAlberto Garcia "Making 'hd1' a backing file of 'hd2' would create a cycle") 439*bf3e50f6SAlberto Garcia 440*bf3e50f6SAlberto Garcia # hd2 <- hd0, hd2 <- hd1 441*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'hd2'}) 442*bf3e50f6SAlberto Garcia 443*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0 0 1M") 444*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") 445*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0xa2 2M 1M") 446*bf3e50f6SAlberto Garcia 447*bf3e50f6SAlberto Garcia # More illegal operations 448*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': 'hd1'}, 449*bf3e50f6SAlberto Garcia "Making 'hd1' a backing file of 'hd2' would create a cycle") 450*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'") 451*bf3e50f6SAlberto Garcia 452*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') 453*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 454*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'") 455*bf3e50f6SAlberto Garcia 456*bf3e50f6SAlberto Garcia # hd1 doesn't have a backing file now 457*bf3e50f6SAlberto Garcia self.reopen(opts[1], {'backing': None}) 458*bf3e50f6SAlberto Garcia 459*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0 0 1M") 460*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") 461*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0 2M 1M") 462*bf3e50f6SAlberto Garcia 463*bf3e50f6SAlberto Garcia # We can't remove the 'backing' option if the image has a 464*bf3e50f6SAlberto Garcia # default backing file 465*bf3e50f6SAlberto Garcia del opts[1]['backing'] 466*bf3e50f6SAlberto Garcia self.reopen(opts[1], {}, "backing is missing for 'hd1'") 467*bf3e50f6SAlberto Garcia 468*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0 0 1M") 469*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") 470*bf3e50f6SAlberto Garcia self.run_qemu_io("hd1", "read -P 0 2M 1M") 471*bf3e50f6SAlberto Garcia 472*bf3e50f6SAlberto Garcia # This test verifies that we can't change the children of a block 473*bf3e50f6SAlberto Garcia # device during a reopen operation in a way that would create 474*bf3e50f6SAlberto Garcia # cycles in the node graph 475*bf3e50f6SAlberto Garcia def test_graph_cycles(self): 476*bf3e50f6SAlberto Garcia opts = [] 477*bf3e50f6SAlberto Garcia 478*bf3e50f6SAlberto Garcia # Open all three images without backing file 479*bf3e50f6SAlberto Garcia for i in range(3): 480*bf3e50f6SAlberto Garcia opts.append(hd_opts(i)) 481*bf3e50f6SAlberto Garcia opts[i]['backing'] = None 482*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) 483*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 484*bf3e50f6SAlberto Garcia 485*bf3e50f6SAlberto Garcia # hd1 <- hd0, hd1 <- hd2 486*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'hd1'}) 487*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': 'hd1'}) 488*bf3e50f6SAlberto Garcia 489*bf3e50f6SAlberto Garcia # Illegal: hd2 is backed by hd1 490*bf3e50f6SAlberto Garcia self.reopen(opts[1], {'backing': 'hd2'}, 491*bf3e50f6SAlberto Garcia "Making 'hd2' a backing file of 'hd1' would create a cycle") 492*bf3e50f6SAlberto Garcia 493*bf3e50f6SAlberto Garcia # hd1 <- hd0 <- hd2 494*bf3e50f6SAlberto Garcia self.reopen(opts[2], {'backing': 'hd0'}) 495*bf3e50f6SAlberto Garcia 496*bf3e50f6SAlberto Garcia # Illegal: hd2 is backed by hd0, which is backed by hd1 497*bf3e50f6SAlberto Garcia self.reopen(opts[1], {'backing': 'hd2'}, 498*bf3e50f6SAlberto Garcia "Making 'hd2' a backing file of 'hd1' would create a cycle") 499*bf3e50f6SAlberto Garcia 500*bf3e50f6SAlberto Garcia # Illegal: hd1 cannot point to itself 501*bf3e50f6SAlberto Garcia self.reopen(opts[1], {'backing': 'hd1'}, 502*bf3e50f6SAlberto Garcia "Making 'hd1' a backing file of 'hd1' would create a cycle") 503*bf3e50f6SAlberto Garcia 504*bf3e50f6SAlberto Garcia # Remove all backing files 505*bf3e50f6SAlberto Garcia self.reopen(opts[0]) 506*bf3e50f6SAlberto Garcia self.reopen(opts[2]) 507*bf3e50f6SAlberto Garcia 508*bf3e50f6SAlberto Garcia ########################################## 509*bf3e50f6SAlberto Garcia # Add a blkverify node using hd0 and hd1 # 510*bf3e50f6SAlberto Garcia ########################################## 511*bf3e50f6SAlberto Garcia bvopts = {'driver': 'blkverify', 512*bf3e50f6SAlberto Garcia 'node-name': 'bv', 513*bf3e50f6SAlberto Garcia 'test': 'hd0', 514*bf3e50f6SAlberto Garcia 'raw': 'hd1'} 515*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts) 516*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 517*bf3e50f6SAlberto Garcia 518*bf3e50f6SAlberto Garcia # blkverify doesn't currently allow reopening. TODO: implement this 519*bf3e50f6SAlberto Garcia self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" + 520*bf3e50f6SAlberto Garcia " does not support reopening files") 521*bf3e50f6SAlberto Garcia 522*bf3e50f6SAlberto Garcia # Illegal: hd0 is a child of the blkverify node 523*bf3e50f6SAlberto Garcia self.reopen(opts[0], {'backing': 'bv'}, 524*bf3e50f6SAlberto Garcia "Making 'bv' a backing file of 'hd0' would create a cycle") 525*bf3e50f6SAlberto Garcia 526*bf3e50f6SAlberto Garcia # Delete the blkverify node 527*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv') 528*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 529*bf3e50f6SAlberto Garcia 530*bf3e50f6SAlberto Garcia # Misc reopen tests with different block drivers 531*bf3e50f6SAlberto Garcia def test_misc_drivers(self): 532*bf3e50f6SAlberto Garcia #################### 533*bf3e50f6SAlberto Garcia ###### quorum ###### 534*bf3e50f6SAlberto Garcia #################### 535*bf3e50f6SAlberto Garcia for i in range(3): 536*bf3e50f6SAlberto Garcia opts = hd_opts(i) 537*bf3e50f6SAlberto Garcia # Open all three images without backing file 538*bf3e50f6SAlberto Garcia opts['backing'] = None 539*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 540*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 541*bf3e50f6SAlberto Garcia 542*bf3e50f6SAlberto Garcia opts = {'driver': 'quorum', 543*bf3e50f6SAlberto Garcia 'node-name': 'quorum0', 544*bf3e50f6SAlberto Garcia 'children': ['hd0', 'hd1', 'hd2'], 545*bf3e50f6SAlberto Garcia 'vote-threshold': 2} 546*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 547*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 548*bf3e50f6SAlberto Garcia 549*bf3e50f6SAlberto Garcia # Quorum doesn't currently allow reopening. TODO: implement this 550*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" + 551*bf3e50f6SAlberto Garcia " does not support reopening files") 552*bf3e50f6SAlberto Garcia 553*bf3e50f6SAlberto Garcia # You can't make quorum0 a backing file of hd0: 554*bf3e50f6SAlberto Garcia # hd0 is already a child of quorum0. 555*bf3e50f6SAlberto Garcia self.reopen(hd_opts(0), {'backing': 'quorum0'}, 556*bf3e50f6SAlberto Garcia "Making 'quorum0' a backing file of 'hd0' would create a cycle") 557*bf3e50f6SAlberto Garcia 558*bf3e50f6SAlberto Garcia # Delete quorum0 559*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0') 560*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 561*bf3e50f6SAlberto Garcia 562*bf3e50f6SAlberto Garcia # Delete hd0, hd1 and hd2 563*bf3e50f6SAlberto Garcia for i in range(3): 564*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, 565*bf3e50f6SAlberto Garcia node_name = 'hd%d' % i) 566*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 567*bf3e50f6SAlberto Garcia 568*bf3e50f6SAlberto Garcia ###################### 569*bf3e50f6SAlberto Garcia ###### blkdebug ###### 570*bf3e50f6SAlberto Garcia ###################### 571*bf3e50f6SAlberto Garcia opts = {'driver': 'blkdebug', 572*bf3e50f6SAlberto Garcia 'node-name': 'bd', 573*bf3e50f6SAlberto Garcia 'config': '/dev/null', 574*bf3e50f6SAlberto Garcia 'image': hd_opts(0)} 575*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 576*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 577*bf3e50f6SAlberto Garcia 578*bf3e50f6SAlberto Garcia # blkdebug allows reopening if we keep the same options 579*bf3e50f6SAlberto Garcia self.reopen(opts) 580*bf3e50f6SAlberto Garcia 581*bf3e50f6SAlberto Garcia # but it currently does not allow changes 582*bf3e50f6SAlberto Garcia self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'") 583*bf3e50f6SAlberto Garcia self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'") 584*bf3e50f6SAlberto Garcia self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'") 585*bf3e50f6SAlberto Garcia del opts['config'] 586*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Option 'config' cannot be reset to its default value") 587*bf3e50f6SAlberto Garcia 588*bf3e50f6SAlberto Garcia # Delete the blkdebug node 589*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd') 590*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 591*bf3e50f6SAlberto Garcia 592*bf3e50f6SAlberto Garcia ################## 593*bf3e50f6SAlberto Garcia ###### null ###### 594*bf3e50f6SAlberto Garcia ################## 595*bf3e50f6SAlberto Garcia opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024} 596*bf3e50f6SAlberto Garcia 597*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 598*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 599*bf3e50f6SAlberto Garcia 600*bf3e50f6SAlberto Garcia # 1 << 30 is the default value, but we cannot change it explicitly 601*bf3e50f6SAlberto Garcia self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'") 602*bf3e50f6SAlberto Garcia 603*bf3e50f6SAlberto Garcia # We cannot change 'size' back to its default value either 604*bf3e50f6SAlberto Garcia del opts['size'] 605*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Option 'size' cannot be reset to its default value") 606*bf3e50f6SAlberto Garcia 607*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root') 608*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 609*bf3e50f6SAlberto Garcia 610*bf3e50f6SAlberto Garcia ################## 611*bf3e50f6SAlberto Garcia ###### file ###### 612*bf3e50f6SAlberto Garcia ################## 613*bf3e50f6SAlberto Garcia opts = hd_opts(0) 614*bf3e50f6SAlberto Garcia opts['file']['locking'] = 'on' 615*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 616*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 617*bf3e50f6SAlberto Garcia 618*bf3e50f6SAlberto Garcia # 'locking' cannot be changed 619*bf3e50f6SAlberto Garcia del opts['file']['locking'] 620*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.locking': 'on'}) 621*bf3e50f6SAlberto Garcia self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") 622*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") 623*bf3e50f6SAlberto Garcia 624*bf3e50f6SAlberto Garcia # Trying to reopen the 'file' node directly does not make a difference 625*bf3e50f6SAlberto Garcia opts = opts['file'] 626*bf3e50f6SAlberto Garcia self.reopen(opts, {'locking': 'on'}) 627*bf3e50f6SAlberto Garcia self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'") 628*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") 629*bf3e50f6SAlberto Garcia 630*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 631*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 632*bf3e50f6SAlberto Garcia 633*bf3e50f6SAlberto Garcia ###################### 634*bf3e50f6SAlberto Garcia ###### throttle ###### 635*bf3e50f6SAlberto Garcia ###################### 636*bf3e50f6SAlberto Garcia opts = { 'qom-type': 'throttle-group', 'id': 'group0', 637*bf3e50f6SAlberto Garcia 'props': { 'limits': { 'iops-total': 1000 } } } 638*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-add', conv_keys = False, **opts) 639*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 640*bf3e50f6SAlberto Garcia 641*bf3e50f6SAlberto Garcia opts = { 'qom-type': 'throttle-group', 'id': 'group1', 642*bf3e50f6SAlberto Garcia 'props': { 'limits': { 'iops-total': 2000 } } } 643*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-add', conv_keys = False, **opts) 644*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 645*bf3e50f6SAlberto Garcia 646*bf3e50f6SAlberto Garcia # Add a throttle filter with group = group0 647*bf3e50f6SAlberto Garcia opts = { 'driver': 'throttle', 'node-name': 'throttle0', 648*bf3e50f6SAlberto Garcia 'throttle-group': 'group0', 'file': hd_opts(0) } 649*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 650*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 651*bf3e50f6SAlberto Garcia 652*bf3e50f6SAlberto Garcia # We can reopen it if we keep the same options 653*bf3e50f6SAlberto Garcia self.reopen(opts) 654*bf3e50f6SAlberto Garcia 655*bf3e50f6SAlberto Garcia # We can also reopen if 'file' is a reference to the child 656*bf3e50f6SAlberto Garcia self.reopen(opts, {'file': 'hd0'}) 657*bf3e50f6SAlberto Garcia 658*bf3e50f6SAlberto Garcia # This is illegal 659*bf3e50f6SAlberto Garcia self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist") 660*bf3e50f6SAlberto Garcia 661*bf3e50f6SAlberto Garcia # But it's possible to change the group to group1 662*bf3e50f6SAlberto Garcia self.reopen(opts, {'throttle-group': 'group1'}) 663*bf3e50f6SAlberto Garcia 664*bf3e50f6SAlberto Garcia # Now group1 is in use, it cannot be deleted 665*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-del', id = 'group1') 666*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 667*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted") 668*bf3e50f6SAlberto Garcia 669*bf3e50f6SAlberto Garcia # Default options, this switches the group back to group0 670*bf3e50f6SAlberto Garcia self.reopen(opts) 671*bf3e50f6SAlberto Garcia 672*bf3e50f6SAlberto Garcia # So now we cannot delete group0 673*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-del', id = 'group0') 674*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 675*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted") 676*bf3e50f6SAlberto Garcia 677*bf3e50f6SAlberto Garcia # But group1 is free this time, and it can be deleted 678*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-del', id = 'group1') 679*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 680*bf3e50f6SAlberto Garcia 681*bf3e50f6SAlberto Garcia # Let's delete the filter node 682*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0') 683*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 684*bf3e50f6SAlberto Garcia 685*bf3e50f6SAlberto Garcia # And we can finally get rid of group0 686*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-del', id = 'group0') 687*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 688*bf3e50f6SAlberto Garcia 689*bf3e50f6SAlberto Garcia # If an image has a backing file then the 'backing' option must be 690*bf3e50f6SAlberto Garcia # passed on reopen. We don't allow leaving the option out in this 691*bf3e50f6SAlberto Garcia # case because it's unclear what the correct semantics would be. 692*bf3e50f6SAlberto Garcia def test_missing_backing_options_1(self): 693*bf3e50f6SAlberto Garcia # hd2 694*bf3e50f6SAlberto Garcia opts = hd_opts(2) 695*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 696*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 697*bf3e50f6SAlberto Garcia 698*bf3e50f6SAlberto Garcia # hd0 699*bf3e50f6SAlberto Garcia opts = hd_opts(0) 700*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 701*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 702*bf3e50f6SAlberto Garcia 703*bf3e50f6SAlberto Garcia # hd0 has no backing file: we can omit the 'backing' option 704*bf3e50f6SAlberto Garcia self.reopen(opts) 705*bf3e50f6SAlberto Garcia 706*bf3e50f6SAlberto Garcia # hd2 <- hd0 707*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd2'}) 708*bf3e50f6SAlberto Garcia 709*bf3e50f6SAlberto Garcia # hd0 has a backing file: we must set the 'backing' option 710*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd0'") 711*bf3e50f6SAlberto Garcia 712*bf3e50f6SAlberto Garcia # hd2 can't be removed because it's the backing file of hd0 713*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') 714*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/class', 'GenericError') 715*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'") 716*bf3e50f6SAlberto Garcia 717*bf3e50f6SAlberto Garcia # Detach hd2 from hd0. 718*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': None}) 719*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd0'") 720*bf3e50f6SAlberto Garcia 721*bf3e50f6SAlberto Garcia # Remove both hd0 and hd2 722*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 723*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 724*bf3e50f6SAlberto Garcia 725*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') 726*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 727*bf3e50f6SAlberto Garcia 728*bf3e50f6SAlberto Garcia # If an image has default backing file (as part of its metadata) 729*bf3e50f6SAlberto Garcia # then the 'backing' option must be passed on reopen. We don't 730*bf3e50f6SAlberto Garcia # allow leaving the option out in this case because it's unclear 731*bf3e50f6SAlberto Garcia # what the correct semantics would be. 732*bf3e50f6SAlberto Garcia def test_missing_backing_options_2(self): 733*bf3e50f6SAlberto Garcia # hd0 <- hd1 734*bf3e50f6SAlberto Garcia # (hd0 is hd1's default backing file) 735*bf3e50f6SAlberto Garcia opts = hd_opts(1) 736*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 737*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 738*bf3e50f6SAlberto Garcia 739*bf3e50f6SAlberto Garcia # hd1 has a backing file: we can't omit the 'backing' option 740*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd1'") 741*bf3e50f6SAlberto Garcia 742*bf3e50f6SAlberto Garcia # Let's detach the backing file 743*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': None}) 744*bf3e50f6SAlberto Garcia 745*bf3e50f6SAlberto Garcia # No backing file attached to hd1 now, but we still can't omit the 'backing' option 746*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd1'") 747*bf3e50f6SAlberto Garcia 748*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') 749*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 750*bf3e50f6SAlberto Garcia 751*bf3e50f6SAlberto Garcia # Test that making 'backing' a reference to an existing child 752*bf3e50f6SAlberto Garcia # keeps its current options 753*bf3e50f6SAlberto Garcia def test_backing_reference(self): 754*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 755*bf3e50f6SAlberto Garcia opts = hd_opts(0) 756*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 757*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 758*bf3e50f6SAlberto Garcia # Enable 'detect-zeroes' on all three nodes 759*bf3e50f6SAlberto Garcia opts['detect-zeroes'] = 'on' 760*bf3e50f6SAlberto Garcia opts['backing']['detect-zeroes'] = 'on' 761*bf3e50f6SAlberto Garcia opts['backing']['backing']['detect-zeroes'] = 'on' 762*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 763*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 764*bf3e50f6SAlberto Garcia 765*bf3e50f6SAlberto Garcia # Reopen the chain passing the minimum amount of required options. 766*bf3e50f6SAlberto Garcia # By making 'backing' a reference to hd1 (instead of a sub-dict) 767*bf3e50f6SAlberto Garcia # we tell QEMU to keep its current set of options. 768*bf3e50f6SAlberto Garcia opts = {'driver': iotests.imgfmt, 769*bf3e50f6SAlberto Garcia 'node-name': 'hd0', 770*bf3e50f6SAlberto Garcia 'file': 'hd0-file', 771*bf3e50f6SAlberto Garcia 'backing': 'hd1' } 772*bf3e50f6SAlberto Garcia self.reopen(opts) 773*bf3e50f6SAlberto Garcia 774*bf3e50f6SAlberto Garcia # This has reset 'detect-zeroes' on hd0, but not on hd1 and hd2. 775*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd0'), 'detect_zeroes', 'off') 776*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') 777*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd2'), 'detect_zeroes', 'on') 778*bf3e50f6SAlberto Garcia 779*bf3e50f6SAlberto Garcia # Test what happens if the graph changes due to other operations 780*bf3e50f6SAlberto Garcia # such as block-stream 781*bf3e50f6SAlberto Garcia def test_block_stream_1(self): 782*bf3e50f6SAlberto Garcia # hd1 <- hd0 783*bf3e50f6SAlberto Garcia opts = hd_opts(0) 784*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 785*bf3e50f6SAlberto Garcia opts['backing']['backing'] = None 786*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 787*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 788*bf3e50f6SAlberto Garcia 789*bf3e50f6SAlberto Garcia # Stream hd1 into hd0 and wait until it's done 790*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0') 791*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 792*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'stream0') 793*bf3e50f6SAlberto Garcia 794*bf3e50f6SAlberto Garcia # Now we have only hd0 795*bf3e50f6SAlberto Garcia self.assertEqual(self.get_node('hd1'), None) 796*bf3e50f6SAlberto Garcia 797*bf3e50f6SAlberto Garcia # We have backing.* options but there's no backing file anymore 798*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change the option 'backing.driver'") 799*bf3e50f6SAlberto Garcia 800*bf3e50f6SAlberto Garcia # If we remove the 'backing' option then we can reopen hd0 just fine 801*bf3e50f6SAlberto Garcia del opts['backing'] 802*bf3e50f6SAlberto Garcia self.reopen(opts) 803*bf3e50f6SAlberto Garcia 804*bf3e50f6SAlberto Garcia # We can also reopen hd0 if we set 'backing' to null 805*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': None}) 806*bf3e50f6SAlberto Garcia 807*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 808*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 809*bf3e50f6SAlberto Garcia 810*bf3e50f6SAlberto Garcia # Another block_stream test 811*bf3e50f6SAlberto Garcia def test_block_stream_2(self): 812*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 813*bf3e50f6SAlberto Garcia opts = hd_opts(0) 814*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 815*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 816*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 817*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 818*bf3e50f6SAlberto Garcia 819*bf3e50f6SAlberto Garcia # Stream hd1 into hd0 and wait until it's done 820*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', 821*bf3e50f6SAlberto Garcia device = 'hd0', base_node = 'hd2') 822*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 823*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'stream0') 824*bf3e50f6SAlberto Garcia 825*bf3e50f6SAlberto Garcia # The chain is hd2 <- hd0 now. hd1 is missing 826*bf3e50f6SAlberto Garcia self.assertEqual(self.get_node('hd1'), None) 827*bf3e50f6SAlberto Garcia 828*bf3e50f6SAlberto Garcia # The backing options in the dict were meant for hd1, but we cannot 829*bf3e50f6SAlberto Garcia # use them with hd2 because hd1 had a backing file while hd2 does not. 830*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change the option 'backing.driver'") 831*bf3e50f6SAlberto Garcia 832*bf3e50f6SAlberto Garcia # If we remove hd1's options from the dict then things work fine 833*bf3e50f6SAlberto Garcia opts['backing'] = opts['backing']['backing'] 834*bf3e50f6SAlberto Garcia self.reopen(opts) 835*bf3e50f6SAlberto Garcia 836*bf3e50f6SAlberto Garcia # We can also reopen hd0 if we use a reference to the backing file 837*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd2'}) 838*bf3e50f6SAlberto Garcia 839*bf3e50f6SAlberto Garcia # But we cannot leave the option out 840*bf3e50f6SAlberto Garcia del opts['backing'] 841*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "backing is missing for 'hd0'") 842*bf3e50f6SAlberto Garcia 843*bf3e50f6SAlberto Garcia # Now we can delete hd0 (and hd2) 844*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') 845*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 846*bf3e50f6SAlberto Garcia self.assertEqual(self.get_node('hd2'), None) 847*bf3e50f6SAlberto Garcia 848*bf3e50f6SAlberto Garcia # Reopen the chain during a block-stream job (from hd1 to hd0) 849*bf3e50f6SAlberto Garcia def test_block_stream_3(self): 850*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 851*bf3e50f6SAlberto Garcia opts = hd_opts(0) 852*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 853*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 854*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 855*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 856*bf3e50f6SAlberto Garcia 857*bf3e50f6SAlberto Garcia # hd2 <- hd0 858*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', 859*bf3e50f6SAlberto Garcia device = 'hd0', base_node = 'hd2', speed = 512 * 1024) 860*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 861*bf3e50f6SAlberto Garcia 862*bf3e50f6SAlberto Garcia # We can't remove hd2 while the stream job is ongoing 863*bf3e50f6SAlberto Garcia opts['backing']['backing'] = None 864*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'") 865*bf3e50f6SAlberto Garcia 866*bf3e50f6SAlberto Garcia # We can't remove hd1 while the stream job is ongoing 867*bf3e50f6SAlberto Garcia opts['backing'] = None 868*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'") 869*bf3e50f6SAlberto Garcia 870*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'stream0') 871*bf3e50f6SAlberto Garcia 872*bf3e50f6SAlberto Garcia # Reopen the chain during a block-stream job (from hd2 to hd1) 873*bf3e50f6SAlberto Garcia def test_block_stream_4(self): 874*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 875*bf3e50f6SAlberto Garcia opts = hd_opts(0) 876*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 877*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 878*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 879*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 880*bf3e50f6SAlberto Garcia 881*bf3e50f6SAlberto Garcia # hd1 <- hd0 882*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', 883*bf3e50f6SAlberto Garcia device = 'hd1', speed = 512 * 1024) 884*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 885*bf3e50f6SAlberto Garcia 886*bf3e50f6SAlberto Garcia # We can't reopen with the original options because that would 887*bf3e50f6SAlberto Garcia # make hd1 read-only and block-stream requires it to be read-write 888*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Can't set node 'hd1' to r/o with copy-on-read enabled") 889*bf3e50f6SAlberto Garcia 890*bf3e50f6SAlberto Garcia # We can't remove hd2 while the stream job is ongoing 891*bf3e50f6SAlberto Garcia opts['backing']['backing'] = None 892*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'") 893*bf3e50f6SAlberto Garcia 894*bf3e50f6SAlberto Garcia # We can detach hd1 from hd0 because it doesn't affect the stream job 895*bf3e50f6SAlberto Garcia opts['backing'] = None 896*bf3e50f6SAlberto Garcia self.reopen(opts) 897*bf3e50f6SAlberto Garcia 898*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'stream0') 899*bf3e50f6SAlberto Garcia 900*bf3e50f6SAlberto Garcia # Reopen the chain during a block-commit job (from hd0 to hd2) 901*bf3e50f6SAlberto Garcia def test_block_commit_1(self): 902*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 903*bf3e50f6SAlberto Garcia opts = hd_opts(0) 904*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 905*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 906*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 907*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 908*bf3e50f6SAlberto Garcia 909*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', 910*bf3e50f6SAlberto Garcia device = 'hd0', speed = 1024 * 1024) 911*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 912*bf3e50f6SAlberto Garcia 913*bf3e50f6SAlberto Garcia # We can't remove hd2 while the commit job is ongoing 914*bf3e50f6SAlberto Garcia opts['backing']['backing'] = None 915*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'") 916*bf3e50f6SAlberto Garcia 917*bf3e50f6SAlberto Garcia # We can't remove hd1 while the commit job is ongoing 918*bf3e50f6SAlberto Garcia opts['backing'] = None 919*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'") 920*bf3e50f6SAlberto Garcia 921*bf3e50f6SAlberto Garcia event = self.vm.event_wait(name='BLOCK_JOB_READY') 922*bf3e50f6SAlberto Garcia self.assert_qmp(event, 'data/device', 'commit0') 923*bf3e50f6SAlberto Garcia self.assert_qmp(event, 'data/type', 'commit') 924*bf3e50f6SAlberto Garcia self.assert_qmp_absent(event, 'data/error') 925*bf3e50f6SAlberto Garcia 926*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-job-complete', device='commit0') 927*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 928*bf3e50f6SAlberto Garcia 929*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'commit0') 930*bf3e50f6SAlberto Garcia 931*bf3e50f6SAlberto Garcia # Reopen the chain during a block-commit job (from hd1 to hd2) 932*bf3e50f6SAlberto Garcia def test_block_commit_2(self): 933*bf3e50f6SAlberto Garcia # hd2 <- hd1 <- hd0 934*bf3e50f6SAlberto Garcia opts = hd_opts(0) 935*bf3e50f6SAlberto Garcia opts['backing'] = hd_opts(1) 936*bf3e50f6SAlberto Garcia opts['backing']['backing'] = hd_opts(2) 937*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 938*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 939*bf3e50f6SAlberto Garcia 940*bf3e50f6SAlberto Garcia result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', 941*bf3e50f6SAlberto Garcia device = 'hd0', top_node = 'hd1', speed = 1024 * 1024) 942*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 943*bf3e50f6SAlberto Garcia 944*bf3e50f6SAlberto Garcia # We can't remove hd2 while the commit job is ongoing 945*bf3e50f6SAlberto Garcia opts['backing']['backing'] = None 946*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change the option 'backing.driver'") 947*bf3e50f6SAlberto Garcia 948*bf3e50f6SAlberto Garcia # We can't remove hd1 while the commit job is ongoing 949*bf3e50f6SAlberto Garcia opts['backing'] = None 950*bf3e50f6SAlberto Garcia self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file") 951*bf3e50f6SAlberto Garcia 952*bf3e50f6SAlberto Garcia # hd2 <- hd0 953*bf3e50f6SAlberto Garcia self.wait_until_completed(drive = 'commit0') 954*bf3e50f6SAlberto Garcia 955*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd0'), 'ro', False) 956*bf3e50f6SAlberto Garcia self.assertEqual(self.get_node('hd1'), None) 957*bf3e50f6SAlberto Garcia self.assert_qmp(self.get_node('hd2'), 'ro', True) 958*bf3e50f6SAlberto Garcia 959*bf3e50f6SAlberto Garcia # We don't allow setting a backing file that uses a different AioContext 960*bf3e50f6SAlberto Garcia def test_iothreads(self): 961*bf3e50f6SAlberto Garcia opts = hd_opts(0) 962*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) 963*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 964*bf3e50f6SAlberto Garcia 965*bf3e50f6SAlberto Garcia opts2 = hd_opts(2) 966*bf3e50f6SAlberto Garcia result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) 967*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 968*bf3e50f6SAlberto Garcia 969*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0') 970*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 971*bf3e50f6SAlberto Garcia 972*bf3e50f6SAlberto Garcia result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1') 973*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 974*bf3e50f6SAlberto Garcia 975*bf3e50f6SAlberto Garcia result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0') 976*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 977*bf3e50f6SAlberto Garcia 978*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") 979*bf3e50f6SAlberto Garcia 980*bf3e50f6SAlberto Garcia result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1') 981*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 982*bf3e50f6SAlberto Garcia 983*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") 984*bf3e50f6SAlberto Garcia 985*bf3e50f6SAlberto Garcia result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0') 986*bf3e50f6SAlberto Garcia self.assert_qmp(result, 'return', {}) 987*bf3e50f6SAlberto Garcia 988*bf3e50f6SAlberto Garcia self.reopen(opts, {'backing': 'hd2'}) 989*bf3e50f6SAlberto Garcia 990*bf3e50f6SAlberto Garciaif __name__ == '__main__': 991*bf3e50f6SAlberto Garcia iotests.main(supported_fmts=["qcow2"]) 992