1#!/usr/bin/env python3
2# group: rw quick
3#
4# Test for when a backing file is considered overridden (thus, a
5# json:{} filename is generated for the overlay) and when it is not
6#
7# Copyright (C) 2018 Red Hat, Inc.
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21#
22# Creator/Owner: Max Reitz <mreitz@redhat.com>
23
24import iotests
25from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
26        filter_qmp_testfiles, filter_qmp_imgfmt
27
28# Need backing file and change-backing-file support
29iotests.script_initialize(
30    supported_fmts=['qcow2', 'qed'],
31    supported_platforms=['linux'],
32)
33
34
35def log_node_info(node):
36    log('')
37
38    log('bs->filename: ' + node['image']['filename'],
39        filters=[filter_testfiles, filter_imgfmt])
40    log('bs->backing_file: ' + node['image']['full-backing-filename'],
41        filters=[filter_testfiles, filter_imgfmt])
42
43    if 'backing-image' in node['image']:
44        log('bs->backing->bs->filename: ' +
45            node['image']['backing-image']['filename'],
46            filters=[filter_testfiles, filter_imgfmt])
47    else:
48        log('bs->backing: (none)')
49
50    log('')
51
52
53with iotests.FilePath('base.img') as base_img_path, \
54     iotests.FilePath('top.img') as top_img_path, \
55     iotests.VM() as vm:
56
57    assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
58    # Choose a funny way to describe the backing filename
59    assert qemu_img('create', '-f', iotests.imgfmt, '-b',
60                    'file:' + base_img_path, '-F', iotests.imgfmt,
61                    top_img_path) == 0
62
63    vm.launch()
64
65    log('--- Implicit backing file ---')
66    log('')
67
68    vm.qmp_log('blockdev-add',
69                node_name='node0',
70                driver=iotests.imgfmt,
71                file={
72                    'driver': 'file',
73                    'filename': top_img_path
74                },
75                filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
76
77    # Filename should be plain, and the backing node filename should
78    # not contain the "file:" prefix
79    log_node_info(vm.node_info('node0'))
80
81    vm.qmp_log('blockdev-del', node_name='node0')
82
83    log('')
84    log('--- change-backing-file ---')
85    log('')
86
87    vm.qmp_log('blockdev-add',
88               node_name='node0',
89               driver=iotests.imgfmt,
90               file={
91                   'driver': 'file',
92                   'filename': top_img_path
93               },
94               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
95
96    # Changing the backing file to a qemu-reported filename should
97    # result in qemu accepting the corresponding BDS as the implicit
98    # backing BDS (and thus not generate a json:{} filename).
99    # So, first, query the backing filename.
100
101    backing_filename = \
102        vm.node_info('node0')['image']['backing-image']['filename']
103
104    # Next, change the backing file to something different
105
106    vm.qmp_log('change-backing-file',
107               image_node_name='node0',
108               device='node0',
109               backing_file='null-co://',
110               filters=[filter_qmp_testfiles])
111
112    # Now, verify that we get a json:{} filename
113    # (Image header says "null-co://", actual backing file still is
114    # base_img_path)
115
116    log_node_info(vm.node_info('node0'))
117
118    # Change it back
119    # (To get header and backing file in sync)
120
121    vm.qmp_log('change-backing-file',
122               image_node_name='node0',
123               device='node0',
124               backing_file=backing_filename,
125               filters=[filter_qmp_testfiles])
126
127    # And verify that we get our original results
128
129    log_node_info(vm.node_info('node0'))
130
131    # Finally, try a "file:" prefix.  While this is actually what we
132    # originally had in the image header, qemu will not reopen the
133    # backing file here, so it cannot verify that this filename
134    # "resolves" to the actual backing BDS's filename and will thus
135    # consider both to be different.
136    # (This may be fixed in the future.)
137
138    vm.qmp_log('change-backing-file',
139               image_node_name='node0',
140               device='node0',
141               backing_file=('file:' + backing_filename),
142               filters=[filter_qmp_testfiles])
143
144    # So now we should get a json:{} filename
145
146    log_node_info(vm.node_info('node0'))
147
148    # Remove and re-attach so we can see that (as in our first try),
149    # opening the image anew helps qemu resolve the header backing
150    # filename.
151
152    vm.qmp_log('blockdev-del', node_name='node0')
153
154    vm.qmp_log('blockdev-add',
155               node_name='node0',
156               driver=iotests.imgfmt,
157               file={
158                   'driver': 'file',
159                   'filename': top_img_path
160               },
161               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
162
163    log_node_info(vm.node_info('node0'))
164
165    vm.qmp_log('blockdev-del', node_name='node0')
166
167    log('')
168    log('--- Override backing file ---')
169    log('')
170
171    # For this test, we need the plain filename in the image header
172    # (because qemu cannot "canonicalize"/"resolve" the backing
173    # filename unless the backing file is opened implicitly with the
174    # overlay)
175    assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path,
176                    '-F', iotests.imgfmt, top_img_path) == 0
177
178    # You can only reliably override backing options by using a node
179    # reference (or by specifying file.filename, but, well...)
180    vm.qmp_log('blockdev-add', node_name='null', driver='null-co')
181
182    vm.qmp_log('blockdev-add',
183               node_name='node0',
184               driver=iotests.imgfmt,
185               file={
186                   'driver': 'file',
187                   'filename': top_img_path
188               },
189               backing='null',
190               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
191
192    # Should get a json:{} filename (and bs->backing_file is
193    # null-co://, because that field actually has not much to do
194    # with the header backing filename (except that it is changed by
195    # change-backing-file))
196
197    log_node_info(vm.node_info('node0'))
198
199    # Detach the backing file by reopening the whole thing
200
201    vm.qmp_log('blockdev-del', node_name='node0')
202    vm.qmp_log('blockdev-del', node_name='null')
203
204    vm.qmp_log('blockdev-add',
205               node_name='node0',
206               driver=iotests.imgfmt,
207               file={
208                   'driver': 'file',
209                   'filename': top_img_path
210               },
211               backing=None,
212               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
213
214    # Should get a json:{} filename (because we overrode the backing
215    # file to not be there)
216
217    log_node_info(vm.node_info('node0'))
218
219    # Open the original backing file
220
221    vm.qmp_log('blockdev-add',
222               node_name='original-backing',
223               driver=iotests.imgfmt,
224               file={
225                   'driver': 'file',
226                   'filename': base_img_path
227               },
228               filters=[filter_qmp_testfiles, filter_qmp_imgfmt])
229
230    # Attach the original backing file to its overlay
231
232    vm.qmp_log('blockdev-snapshot',
233               node='original-backing',
234               overlay='node0')
235
236    # This should give us the original plain result
237
238    log_node_info(vm.node_info('node0'))
239
240    vm.qmp_log('blockdev-del', node_name='node0')
241    vm.qmp_log('blockdev-del', node_name='original-backing')
242
243    vm.shutdown()
244