]>
Commit | Line | Data |
---|---|---|
fefac70d PB |
1 | #!/usr/bin/env python |
2 | # | |
3 | # Tests for shrinking images | |
4 | # | |
5 | # Copyright (c) 2016-2017 Parallels International GmbH | |
6 | # | |
7 | # This program is free software; you can redistribute it and/or modify | |
8 | # it under the terms of the GNU General Public License as published by | |
9 | # the Free Software Foundation; either version 2 of the License, or | |
10 | # (at your option) any later version. | |
11 | # | |
12 | # This program is distributed in the hope that it will be useful, | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | # GNU General Public License for more details. | |
16 | # | |
17 | # You should have received a copy of the GNU General Public License | |
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | # | |
20 | ||
21 | import os, random, iotests, struct, qcow2 | |
22 | from iotests import qemu_img, qemu_io, image_size | |
23 | ||
24 | test_img = os.path.join(iotests.test_dir, 'test.img') | |
25 | check_img = os.path.join(iotests.test_dir, 'check.img') | |
26 | ||
27 | def size_to_int(str): | |
28 | suff = ['B', 'K', 'M', 'G', 'T'] | |
29 | return int(str[:-1]) * 1024**suff.index(str[-1:]) | |
30 | ||
31 | class ShrinkBaseClass(iotests.QMPTestCase): | |
32 | image_len = '128M' | |
33 | shrink_size = '10M' | |
34 | chunk_size = '16M' | |
35 | refcount_bits = '16' | |
36 | ||
37 | def __qcow2_check(self, filename): | |
38 | entry_bits = 3 | |
39 | entry_size = 1 << entry_bits | |
40 | l1_mask = 0x00fffffffffffe00 | |
41 | div_roundup = lambda n, d: (n + d - 1) / d | |
42 | ||
43 | def split_by_n(data, n): | |
44 | for x in xrange(0, len(data), n): | |
45 | yield struct.unpack('>Q', data[x:x + n])[0] & l1_mask | |
46 | ||
47 | def check_l1_table(h, l1_data): | |
48 | l1_list = list(split_by_n(l1_data, entry_size)) | |
49 | real_l1_size = div_roundup(h.size, | |
50 | 1 << (h.cluster_bits*2 - entry_size)) | |
51 | used, unused = l1_list[:real_l1_size], l1_list[real_l1_size:] | |
52 | ||
53 | self.assertTrue(len(used) != 0, "Verifying l1 table content") | |
54 | self.assertFalse(any(unused), "Verifying l1 table content") | |
55 | ||
56 | def check_reftable(fd, h, reftable): | |
57 | for offset in split_by_n(reftable, entry_size): | |
58 | if offset != 0: | |
59 | fd.seek(offset) | |
60 | cluster = fd.read(1 << h.cluster_bits) | |
61 | self.assertTrue(any(cluster), "Verifying reftable content") | |
62 | ||
63 | with open(filename, "rb") as fd: | |
64 | h = qcow2.QcowHeader(fd) | |
65 | ||
66 | fd.seek(h.l1_table_offset) | |
67 | l1_table = fd.read(h.l1_size << entry_bits) | |
68 | ||
69 | fd.seek(h.refcount_table_offset) | |
70 | reftable = fd.read(h.refcount_table_clusters << h.cluster_bits) | |
71 | ||
72 | check_l1_table(h, l1_table) | |
73 | check_reftable(fd, h, reftable) | |
74 | ||
75 | def __raw_check(self, filename): | |
76 | pass | |
77 | ||
78 | image_check = { | |
79 | 'qcow2' : __qcow2_check, | |
80 | 'raw' : __raw_check | |
81 | } | |
82 | ||
83 | def setUp(self): | |
84 | if iotests.imgfmt == 'raw': | |
85 | qemu_img('create', '-f', iotests.imgfmt, test_img, self.image_len) | |
86 | qemu_img('create', '-f', iotests.imgfmt, check_img, | |
87 | self.shrink_size) | |
88 | else: | |
89 | qemu_img('create', '-f', iotests.imgfmt, | |
90 | '-o', 'cluster_size=' + self.cluster_size + | |
91 | ',refcount_bits=' + self.refcount_bits, | |
92 | test_img, self.image_len) | |
93 | qemu_img('create', '-f', iotests.imgfmt, | |
94 | '-o', 'cluster_size=%s'% self.cluster_size, | |
95 | check_img, self.shrink_size) | |
96 | qemu_io('-c', 'write -P 0xff 0 ' + self.shrink_size, check_img) | |
97 | ||
98 | def tearDown(self): | |
99 | os.remove(test_img) | |
100 | os.remove(check_img) | |
101 | ||
102 | def image_verify(self): | |
103 | self.assertEqual(image_size(test_img), image_size(check_img), | |
104 | "Verifying image size") | |
105 | self.image_check[iotests.imgfmt](self, test_img) | |
106 | ||
107 | if iotests.imgfmt == 'raw': | |
108 | return | |
109 | self.assertEqual(qemu_img('check', test_img), 0, | |
110 | "Verifying image corruption") | |
111 | ||
112 | def test_empty_image(self): | |
113 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
114 | self.shrink_size) | |
115 | ||
116 | self.assertEqual( | |
117 | qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, test_img), | |
118 | qemu_io('-c', 'read -P 0x00 %s'%self.shrink_size, check_img), | |
119 | "Verifying image content") | |
120 | ||
121 | self.image_verify() | |
122 | ||
123 | def test_sequential_write(self): | |
124 | for offs in range(0, size_to_int(self.image_len), | |
125 | size_to_int(self.chunk_size)): | |
126 | qemu_io('-c', 'write -P 0xff %d %s' % (offs, self.chunk_size), | |
127 | test_img) | |
128 | ||
129 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
130 | self.shrink_size) | |
131 | ||
132 | self.assertEqual(qemu_img("compare", test_img, check_img), 0, | |
133 | "Verifying image content") | |
134 | ||
135 | self.image_verify() | |
136 | ||
137 | def test_random_write(self): | |
138 | offs_list = range(0, size_to_int(self.image_len), | |
139 | size_to_int(self.chunk_size)) | |
140 | random.shuffle(offs_list) | |
141 | for offs in offs_list: | |
142 | qemu_io('-c', 'write -P 0xff %d %s' % (offs, self.chunk_size), | |
143 | test_img) | |
144 | ||
145 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
146 | self.shrink_size) | |
147 | ||
148 | self.assertEqual(qemu_img("compare", test_img, check_img), 0, | |
149 | "Verifying image content") | |
150 | ||
151 | self.image_verify() | |
152 | ||
153 | class TestShrink512(ShrinkBaseClass): | |
154 | image_len = '3M' | |
155 | shrink_size = '1M' | |
156 | chunk_size = '256K' | |
157 | cluster_size = '512' | |
158 | refcount_bits = '64' | |
159 | ||
160 | class TestShrink64K(ShrinkBaseClass): | |
161 | cluster_size = '64K' | |
162 | ||
163 | class TestShrink1M(ShrinkBaseClass): | |
164 | cluster_size = '1M' | |
165 | refcount_bits = '1' | |
166 | ||
167 | ShrinkBaseClass = None | |
168 | ||
169 | if __name__ == '__main__': | |
170 | iotests.main(supported_fmts=['raw', 'qcow2']) |