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