1#!/usr/bin/env python
2#
3# Test qcow2 and file image creation
4#
5# Copyright (C) 2018 Red Hat, Inc.
6#
7# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
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
23import iotests
24from iotests import imgfmt
25
26iotests.verify_image_format(supported_fmts=['qcow2'])
27
28def blockdev_create(vm, options):
29    result = vm.qmp_log('blockdev-create',
30                        filters=[iotests.filter_qmp_testfiles],
31                        job_id='job0', options=options)
32
33    if 'return' in result:
34        assert result['return'] == {}
35        vm.run_job('job0')
36    iotests.log("")
37
38with iotests.FilePath('t.qcow2') as disk_path, \
39     iotests.FilePath('t.qcow2.base') as backing_path, \
40     iotests.VM() as vm:
41
42    vm.add_object('secret,id=keysec0,data=foo')
43
44    #
45    # Successful image creation (defaults)
46    #
47    iotests.log("=== Successful image creation (defaults) ===")
48    iotests.log("")
49
50    size = 128 * 1024 * 1024
51
52    vm.launch()
53    blockdev_create(vm, { 'driver': 'file',
54                          'filename': disk_path,
55                          'size': 0 })
56
57    vm.qmp_log('blockdev-add',
58               filters=[iotests.filter_qmp_testfiles],
59               driver='file', filename=disk_path,
60               node_name='imgfile')
61
62    blockdev_create(vm, { 'driver': imgfmt,
63                          'file': 'imgfile',
64                          'size': size })
65    vm.shutdown()
66
67    iotests.img_info_log(disk_path)
68
69    #
70    # Successful image creation (inline blockdev-add, explicit defaults)
71    #
72    iotests.log("=== Successful image creation (inline blockdev-add, explicit defaults) ===")
73    iotests.log("")
74
75    # Choose a different size to show that we got a new image
76    size = 64 * 1024 * 1024
77
78    vm.launch()
79    blockdev_create(vm, { 'driver': 'file',
80                          'filename': disk_path,
81                          'size': 0,
82                          'preallocation': 'off',
83                          'nocow': False })
84
85    blockdev_create(vm, { 'driver': imgfmt,
86                          'file': {
87                              'driver': 'file',
88                              'filename': disk_path,
89                          },
90                          'size': size,
91                          'version': 'v3',
92                          'cluster-size': 65536,
93                          'preallocation': 'off',
94                          'lazy-refcounts': False,
95                          'refcount-bits': 16 })
96    vm.shutdown()
97
98    iotests.img_info_log(disk_path)
99
100    #
101    # Successful image creation (v3 non-default options)
102    #
103    iotests.log("=== Successful image creation (v3 non-default options) ===")
104    iotests.log("")
105
106    # Choose a different size to show that we got a new image
107    size = 32 * 1024 * 1024
108
109    vm.launch()
110    blockdev_create(vm, { 'driver': 'file',
111                          'filename': disk_path,
112                          'size': 0,
113                          'preallocation': 'falloc',
114                          'nocow': True })
115
116    blockdev_create(vm, { 'driver': imgfmt,
117                          'file': {
118                              'driver': 'file',
119                              'filename': disk_path,
120                          },
121                          'size': size,
122                          'version': 'v3',
123                          'cluster-size': 2097152,
124                          'preallocation': 'metadata',
125                          'lazy-refcounts': True,
126                          'refcount-bits': 1 })
127    vm.shutdown()
128
129    iotests.img_info_log(disk_path)
130
131    #
132    # Successful image creation (v2 non-default options)
133    #
134    iotests.log("=== Successful image creation (v2 non-default options) ===")
135    iotests.log("")
136
137    vm.launch()
138    blockdev_create(vm, { 'driver': 'file',
139                          'filename': disk_path,
140                          'size': 0 })
141
142    blockdev_create(vm, { 'driver': imgfmt,
143                          'file': {
144                              'driver': 'file',
145                              'filename': disk_path,
146                          },
147                          'size': size,
148                          'backing-file': backing_path,
149                          'backing-fmt': 'qcow2',
150                          'version': 'v2',
151                          'cluster-size': 512 })
152    vm.shutdown()
153
154    iotests.img_info_log(disk_path)
155
156    #
157    # Successful image creation (encrypted)
158    #
159    iotests.log("=== Successful image creation (encrypted) ===")
160    iotests.log("")
161
162    vm.launch()
163    blockdev_create(vm, { 'driver': imgfmt,
164                          'file': {
165                              'driver': 'file',
166                              'filename': disk_path,
167                          },
168                          'size': size,
169                          'encrypt': {
170                              'format': 'luks',
171                              'key-secret': 'keysec0',
172                              'cipher-alg': 'twofish-128',
173                              'cipher-mode': 'ctr',
174                              'ivgen-alg': 'plain64',
175                              'ivgen-hash-alg': 'md5',
176                              'hash-alg': 'sha1',
177                              'iter-time': 10,
178                          }})
179    vm.shutdown()
180
181    iotests.img_info_log(disk_path)
182
183    #
184    # Invalid BlockdevRef
185    #
186    iotests.log("=== Invalid BlockdevRef ===")
187    iotests.log("")
188
189    vm.launch()
190    blockdev_create(vm, { 'driver': imgfmt,
191                          'file': "this doesn't exist",
192                          'size': size })
193    vm.shutdown()
194
195    #
196    # Invalid sizes
197    #
198    iotests.log("=== Invalid sizes ===")
199
200    # TODO Negative image sizes aren't handled correctly, but this is a problem
201    # with QAPI's implementation of the 'size' type and affects other commands
202    # as well. Once this is fixed, we may want to add a test case here.
203    #
204    # 1. Misaligned image size
205    # 2. 2^64 - 512
206    # 3. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
207    # 4. 2^63 - 512 (generally valid, but qcow2 can't handle images this size)
208
209    vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path))
210
211    vm.launch()
212    for size in [ 1234, 18446744073709551104, 9223372036854775808,
213                  9223372036854775296 ]:
214        blockdev_create(vm, { 'driver': imgfmt,
215                              'file': 'node0',
216                              'size': size })
217    vm.shutdown()
218
219    #
220    # Invalid version
221    #
222    iotests.log("=== Invalid version ===")
223
224    vm.launch()
225    blockdev_create(vm, { 'driver': imgfmt,
226                          'file': 'node0',
227                          'size': 67108864,
228                          'version': 'v1' })
229    blockdev_create(vm, { 'driver': imgfmt,
230                          'file': 'node0',
231                          'size': 67108864,
232                          'version': 'v2',
233                          'lazy-refcounts': True })
234    blockdev_create(vm, { 'driver': imgfmt,
235                          'file': 'node0',
236                          'size': 67108864,
237                          'version': 'v2',
238                          'refcount-bits': 8 })
239    vm.shutdown()
240
241    #
242    # Invalid backing file options
243    #
244    iotests.log("=== Invalid backing file options ===")
245
246    vm.launch()
247    blockdev_create(vm, { 'driver': imgfmt,
248                          'file': 'node0',
249                          'size': 67108864,
250                          'backing-file': '/dev/null',
251                          'preallocation': 'full' })
252    blockdev_create(vm, { 'driver': imgfmt,
253                          'file': 'node0',
254                          'size': 67108864,
255                          'backing-fmt': imgfmt })
256    vm.shutdown()
257
258    #
259    # Invalid cluster size
260    #
261    iotests.log("=== Invalid cluster size ===")
262
263    vm.launch()
264    for csize in [ 1234, 128, 4194304, 0 ]:
265        blockdev_create(vm, { 'driver': imgfmt,
266                              'file': 'node0',
267                              'size': 67108864,
268                              'cluster-size': csize })
269    blockdev_create(vm, { 'driver': imgfmt,
270                          'file': 'node0',
271                          'size': 281474976710656,
272                          'cluster-size': 512 })
273    vm.shutdown()
274
275    #
276    # Invalid refcount width
277    #
278    iotests.log("=== Invalid refcount width ===")
279
280    vm.launch()
281    for refcount_bits in [ 128, 0, 7 ]:
282        blockdev_create(vm, { 'driver': imgfmt,
283                              'file': 'node0',
284                              'size': 67108864,
285                              'refcount-bits': refcount_bits })
286    vm.shutdown()
287