]>
Commit | Line | Data |
---|---|---|
903cb1bf | 1 | #!/usr/bin/env python3 |
9dd003a9 | 2 | # group: rw |
fefac70d PB |
3 | # |
4 | # Tests for shrinking images | |
5 | # | |
6 | # Copyright (c) 2016-2017 Parallels International GmbH | |
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 | ||
68474776 | 22 | import os, random, iotests, struct, qcow2, sys |
fefac70d PB |
23 | from iotests import qemu_img, qemu_io, image_size |
24 | ||
25 | test_img = os.path.join(iotests.test_dir, 'test.img') | |
26 | check_img = os.path.join(iotests.test_dir, 'check.img') | |
27 | ||
28 | def size_to_int(str): | |
29 | suff = ['B', 'K', 'M', 'G', 'T'] | |
30 | return int(str[:-1]) * 1024**suff.index(str[-1:]) | |
31 | ||
32 | class ShrinkBaseClass(iotests.QMPTestCase): | |
33 | image_len = '128M' | |
34 | shrink_size = '10M' | |
35 | chunk_size = '16M' | |
36 | refcount_bits = '16' | |
37 | ||
38 | def __qcow2_check(self, filename): | |
39 | entry_bits = 3 | |
40 | entry_size = 1 << entry_bits | |
41 | l1_mask = 0x00fffffffffffe00 | |
9a3a9a63 | 42 | div_roundup = lambda n, d: (n + d - 1) // d |
fefac70d PB |
43 | |
44 | def split_by_n(data, n): | |
68474776 | 45 | for x in range(0, len(data), n): |
fefac70d PB |
46 | yield struct.unpack('>Q', data[x:x + n])[0] & l1_mask |
47 | ||
48 | def check_l1_table(h, l1_data): | |
49 | l1_list = list(split_by_n(l1_data, entry_size)) | |
50 | real_l1_size = div_roundup(h.size, | |
51 | 1 << (h.cluster_bits*2 - entry_size)) | |
52 | used, unused = l1_list[:real_l1_size], l1_list[real_l1_size:] | |
53 | ||
54 | self.assertTrue(len(used) != 0, "Verifying l1 table content") | |
55 | self.assertFalse(any(unused), "Verifying l1 table content") | |
56 | ||
57 | def check_reftable(fd, h, reftable): | |
58 | for offset in split_by_n(reftable, entry_size): | |
59 | if offset != 0: | |
60 | fd.seek(offset) | |
61 | cluster = fd.read(1 << h.cluster_bits) | |
62 | self.assertTrue(any(cluster), "Verifying reftable content") | |
63 | ||
64 | with open(filename, "rb") as fd: | |
65 | h = qcow2.QcowHeader(fd) | |
66 | ||
67 | fd.seek(h.l1_table_offset) | |
68 | l1_table = fd.read(h.l1_size << entry_bits) | |
69 | ||
70 | fd.seek(h.refcount_table_offset) | |
71 | reftable = fd.read(h.refcount_table_clusters << h.cluster_bits) | |
72 | ||
73 | check_l1_table(h, l1_table) | |
74 | check_reftable(fd, h, reftable) | |
75 | ||
76 | def __raw_check(self, filename): | |
77 | pass | |
78 | ||
79 | image_check = { | |
80 | 'qcow2' : __qcow2_check, | |
81 | 'raw' : __raw_check | |
82 | } | |
83 | ||
84 | def setUp(self): | |
85 | if iotests.imgfmt == 'raw': | |
86 | qemu_img('create', '-f', iotests.imgfmt, test_img, self.image_len) | |
87 | qemu_img('create', '-f', iotests.imgfmt, check_img, | |
88 | self.shrink_size) | |
89 | else: | |
90 | qemu_img('create', '-f', iotests.imgfmt, | |
91 | '-o', 'cluster_size=' + self.cluster_size + | |
92 | ',refcount_bits=' + self.refcount_bits, | |
93 | test_img, self.image_len) | |
94 | qemu_img('create', '-f', iotests.imgfmt, | |
95 | '-o', 'cluster_size=%s'% self.cluster_size, | |
96 | check_img, self.shrink_size) | |
97 | qemu_io('-c', 'write -P 0xff 0 ' + self.shrink_size, check_img) | |
98 | ||
99 | def tearDown(self): | |
100 | os.remove(test_img) | |
101 | os.remove(check_img) | |
102 | ||
103 | def image_verify(self): | |
104 | self.assertEqual(image_size(test_img), image_size(check_img), | |
105 | "Verifying image size") | |
106 | self.image_check[iotests.imgfmt](self, test_img) | |
107 | ||
108 | if iotests.imgfmt == 'raw': | |
109 | return | |
fc272d3c | 110 | qemu_img('check', test_img) |
fefac70d PB |
111 | |
112 | def test_empty_image(self): | |
113 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
114 | self.shrink_size) | |
115 | ||
a1905249 | 116 | qemu_io('-c', f"read -P 0x00 0 {self.shrink_size}", test_img) |
fefac70d PB |
117 | |
118 | self.image_verify() | |
119 | ||
120 | def test_sequential_write(self): | |
121 | for offs in range(0, size_to_int(self.image_len), | |
122 | size_to_int(self.chunk_size)): | |
123 | qemu_io('-c', 'write -P 0xff %d %s' % (offs, self.chunk_size), | |
124 | test_img) | |
125 | ||
126 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
127 | self.shrink_size) | |
128 | ||
fc272d3c | 129 | qemu_img("compare", test_img, check_img) |
fefac70d PB |
130 | |
131 | self.image_verify() | |
132 | ||
133 | def test_random_write(self): | |
68474776 HR |
134 | offs_list = list(range(0, size_to_int(self.image_len), |
135 | size_to_int(self.chunk_size))) | |
fefac70d PB |
136 | random.shuffle(offs_list) |
137 | for offs in offs_list: | |
138 | qemu_io('-c', 'write -P 0xff %d %s' % (offs, self.chunk_size), | |
139 | test_img) | |
140 | ||
141 | qemu_img('resize', '-f', iotests.imgfmt, '--shrink', test_img, | |
142 | self.shrink_size) | |
143 | ||
fc272d3c | 144 | qemu_img("compare", test_img, check_img) |
fefac70d PB |
145 | |
146 | self.image_verify() | |
147 | ||
148 | class TestShrink512(ShrinkBaseClass): | |
149 | image_len = '3M' | |
150 | shrink_size = '1M' | |
151 | chunk_size = '256K' | |
152 | cluster_size = '512' | |
153 | refcount_bits = '64' | |
154 | ||
155 | class TestShrink64K(ShrinkBaseClass): | |
156 | cluster_size = '64K' | |
157 | ||
158 | class TestShrink1M(ShrinkBaseClass): | |
159 | cluster_size = '1M' | |
160 | refcount_bits = '1' | |
161 | ||
162 | ShrinkBaseClass = None | |
163 | ||
164 | if __name__ == '__main__': | |
103cbc77 | 165 | iotests.main(supported_fmts=['raw', 'qcow2'], |
b30b8077 VSO |
166 | supported_protocols=['file'], |
167 | unsupported_imgopts=['compat']) |