xref: /qemu/tests/qemu-iotests/245 (revision bf3e50f6)
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