]>
Commit | Line | Data |
---|---|---|
903cb1bf | 1 | #!/usr/bin/env python3 |
44c7ca5e PB |
2 | # |
3 | # Tests for image mirroring. | |
4 | # | |
5 | # Copyright (C) 2012 Red Hat, Inc. | |
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 time | |
22 | import os | |
a1da1878 | 23 | import re |
44c7ca5e PB |
24 | import iotests |
25 | from iotests import qemu_img, qemu_io | |
44c7ca5e PB |
26 | |
27 | backing_img = os.path.join(iotests.test_dir, 'backing.img') | |
28 | target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') | |
29 | test_img = os.path.join(iotests.test_dir, 'test.img') | |
30 | target_img = os.path.join(iotests.test_dir, 'target.img') | |
31 | ||
d88964ae BC |
32 | quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') |
33 | quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') | |
34 | quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') | |
35 | quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') | |
36 | quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') | |
37 | ||
e5ac52d8 | 38 | nbd_sock_path = os.path.join(iotests.sock_dir, 'nbd.sock') |
a1da1878 | 39 | |
866323f3 | 40 | class TestSingleDrive(iotests.QMPTestCase): |
44c7ca5e | 41 | image_len = 1 * 1024 * 1024 # MB |
94ca2c73 FZ |
42 | qmp_cmd = 'drive-mirror' |
43 | qmp_target = target_img | |
44c7ca5e PB |
44 | |
45 | def setUp(self): | |
3b9f27d2 | 46 | iotests.create_image(backing_img, self.image_len) |
44c7ca5e | 47 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
d3c8c674 | 48 | self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base") |
0ed82f7a HR |
49 | if iotests.qemu_default_machine == 'pc': |
50 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
44c7ca5e PB |
51 | self.vm.launch() |
52 | ||
53 | def tearDown(self): | |
54 | self.vm.shutdown() | |
55 | os.remove(test_img) | |
56 | os.remove(backing_img) | |
57 | try: | |
58 | os.remove(target_img) | |
59 | except OSError: | |
60 | pass | |
61 | ||
62 | def test_complete(self): | |
ecc1c88e | 63 | self.assert_no_active_block_jobs() |
44c7ca5e | 64 | |
94ca2c73 FZ |
65 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
66 | target=self.qmp_target) | |
44c7ca5e PB |
67 | self.assert_qmp(result, 'return', {}) |
68 | ||
69 | self.complete_and_wait() | |
70 | result = self.vm.qmp('query-block') | |
71 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
72 | self.vm.shutdown() | |
3a3918c3 | 73 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
74 | 'target image does not match source after mirroring') |
75 | ||
76 | def test_cancel(self): | |
ecc1c88e | 77 | self.assert_no_active_block_jobs() |
44c7ca5e | 78 | |
94ca2c73 FZ |
79 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
80 | target=self.qmp_target) | |
44c7ca5e PB |
81 | self.assert_qmp(result, 'return', {}) |
82 | ||
2575fe16 | 83 | self.cancel_and_wait(force=True) |
44c7ca5e PB |
84 | result = self.vm.qmp('query-block') |
85 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
44c7ca5e PB |
86 | |
87 | def test_cancel_after_ready(self): | |
ecc1c88e | 88 | self.assert_no_active_block_jobs() |
44c7ca5e | 89 | |
94ca2c73 FZ |
90 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
91 | target=self.qmp_target) | |
44c7ca5e PB |
92 | self.assert_qmp(result, 'return', {}) |
93 | ||
2575fe16 | 94 | self.wait_ready_and_cancel() |
44c7ca5e PB |
95 | result = self.vm.qmp('query-block') |
96 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
97 | self.vm.shutdown() | |
3a3918c3 | 98 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
99 | 'target image does not match source after mirroring') |
100 | ||
101 | def test_pause(self): | |
ecc1c88e | 102 | self.assert_no_active_block_jobs() |
44c7ca5e | 103 | |
94ca2c73 FZ |
104 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
105 | target=self.qmp_target) | |
44c7ca5e PB |
106 | self.assert_qmp(result, 'return', {}) |
107 | ||
2c93c5cb | 108 | self.pause_job('drive0') |
44c7ca5e | 109 | |
44c7ca5e PB |
110 | result = self.vm.qmp('query-block-jobs') |
111 | offset = self.dictpath(result, 'return[0]/offset') | |
112 | ||
2c93c5cb | 113 | time.sleep(0.5) |
44c7ca5e PB |
114 | result = self.vm.qmp('query-block-jobs') |
115 | self.assert_qmp(result, 'return[0]/offset', offset) | |
116 | ||
117 | result = self.vm.qmp('block-job-resume', device='drive0') | |
118 | self.assert_qmp(result, 'return', {}) | |
119 | ||
120 | self.complete_and_wait() | |
121 | self.vm.shutdown() | |
3a3918c3 | 122 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
123 | 'target image does not match source after mirroring') |
124 | ||
125 | def test_small_buffer(self): | |
ecc1c88e | 126 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
127 | |
128 | # A small buffer is rounded up automatically | |
94ca2c73 FZ |
129 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
130 | buf_size=4096, target=self.qmp_target) | |
08e4ed6c PB |
131 | self.assert_qmp(result, 'return', {}) |
132 | ||
133 | self.complete_and_wait() | |
134 | result = self.vm.qmp('query-block') | |
135 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
136 | self.vm.shutdown() | |
3a3918c3 | 137 | self.assertTrue(iotests.compare_images(test_img, target_img), |
08e4ed6c PB |
138 | 'target image does not match source after mirroring') |
139 | ||
140 | def test_small_buffer2(self): | |
ecc1c88e | 141 | self.assert_no_active_block_jobs() |
08e4ed6c PB |
142 | |
143 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' | |
3b9f27d2 | 144 | % (self.image_len, self.image_len), target_img) |
94ca2c73 FZ |
145 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
146 | buf_size=65536, mode='existing', target=self.qmp_target) | |
08e4ed6c PB |
147 | self.assert_qmp(result, 'return', {}) |
148 | ||
149 | self.complete_and_wait() | |
150 | result = self.vm.qmp('query-block') | |
151 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
152 | self.vm.shutdown() | |
3a3918c3 | 153 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
154 | 'target image does not match source after mirroring') |
155 | ||
156 | def test_large_cluster(self): | |
ecc1c88e | 157 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
158 | |
159 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
3b9f27d2 | 160 | % (self.image_len, backing_img), target_img) |
94ca2c73 FZ |
161 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
162 | mode='existing', target=self.qmp_target) | |
44c7ca5e PB |
163 | self.assert_qmp(result, 'return', {}) |
164 | ||
165 | self.complete_and_wait() | |
166 | result = self.vm.qmp('query-block') | |
167 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
168 | self.vm.shutdown() | |
3a3918c3 | 169 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
170 | 'target image does not match source after mirroring') |
171 | ||
d3c8c674 KW |
172 | # Tests that the insertion of the mirror_top filter node doesn't make a |
173 | # difference to query-block | |
174 | def test_implicit_node(self): | |
175 | self.assert_no_active_block_jobs() | |
176 | ||
177 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
178 | target=self.qmp_target) | |
179 | self.assert_qmp(result, 'return', {}) | |
180 | ||
181 | result = self.vm.qmp('query-block') | |
182 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
183 | self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) | |
184 | self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img) | |
185 | self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1) | |
186 | self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) | |
187 | self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img) | |
188 | ||
189 | result = self.vm.qmp('query-blockstats') | |
190 | self.assert_qmp(result, 'return[0]/node-name', 'top') | |
191 | self.assert_qmp(result, 'return[0]/backing/node-name', 'base') | |
192 | ||
193 | self.cancel_and_wait(force=True) | |
194 | result = self.vm.qmp('query-block') | |
195 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
196 | self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) | |
197 | self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img) | |
198 | self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1) | |
199 | self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) | |
200 | self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img) | |
201 | ||
202 | result = self.vm.qmp('query-blockstats') | |
203 | self.assert_qmp(result, 'return[0]/node-name', 'top') | |
204 | self.assert_qmp(result, 'return[0]/backing/node-name', 'base') | |
205 | ||
44c7ca5e | 206 | def test_medium_not_found(self): |
d8683155 BT |
207 | if iotests.qemu_default_machine != 'pc': |
208 | return | |
209 | ||
94ca2c73 FZ |
210 | result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full', |
211 | target=self.qmp_target) | |
0524e93a | 212 | self.assert_qmp(result, 'error/class', 'GenericError') |
44c7ca5e PB |
213 | |
214 | def test_image_not_found(self): | |
94ca2c73 FZ |
215 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', |
216 | mode='existing', target=self.qmp_target) | |
44c7ca5e PB |
217 | self.assert_qmp(result, 'error/class', 'GenericError') |
218 | ||
219 | def test_device_not_found(self): | |
94ca2c73 FZ |
220 | result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full', |
221 | target=self.qmp_target) | |
0524e93a | 222 | self.assert_qmp(result, 'error/class', 'GenericError') |
94ca2c73 FZ |
223 | |
224 | class TestSingleBlockdev(TestSingleDrive): | |
225 | qmp_cmd = 'blockdev-mirror' | |
226 | qmp_target = 'node1' | |
94ca2c73 FZ |
227 | |
228 | def setUp(self): | |
229 | TestSingleDrive.setUp(self) | |
230 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
0153d2f5 KW |
231 | args = {'driver': iotests.imgfmt, |
232 | 'node-name': self.qmp_target, | |
233 | 'file': { 'filename': target_img, 'driver': 'file' } } | |
94ca2c73 FZ |
234 | result = self.vm.qmp("blockdev-add", **args) |
235 | self.assert_qmp(result, 'return', {}) | |
236 | ||
86fae10c KW |
237 | def test_mirror_to_self(self): |
238 | result = self.vm.qmp(self.qmp_cmd, job_id='job0', | |
239 | device=self.qmp_target, sync='full', | |
240 | target=self.qmp_target) | |
241 | self.assert_qmp(result, 'error/class', 'GenericError') | |
242 | ||
94ca2c73 FZ |
243 | test_large_cluster = None |
244 | test_image_not_found = None | |
245 | test_small_buffer2 = None | |
246 | ||
3b9f27d2 FZ |
247 | class TestSingleDriveZeroLength(TestSingleDrive): |
248 | image_len = 0 | |
249 | test_small_buffer2 = None | |
250 | test_large_cluster = None | |
251 | ||
94ca2c73 FZ |
252 | class TestSingleBlockdevZeroLength(TestSingleBlockdev): |
253 | image_len = 0 | |
254 | ||
5a0f6fd5 KW |
255 | class TestSingleDriveUnalignedLength(TestSingleDrive): |
256 | image_len = 1025 * 1024 | |
257 | test_small_buffer2 = None | |
258 | test_large_cluster = None | |
259 | ||
94ca2c73 FZ |
260 | class TestSingleBlockdevUnalignedLength(TestSingleBlockdev): |
261 | image_len = 1025 * 1024 | |
262 | ||
866323f3 | 263 | class TestMirrorNoBacking(iotests.QMPTestCase): |
44c7ca5e PB |
264 | image_len = 2 * 1024 * 1024 # MB |
265 | ||
44c7ca5e | 266 | def setUp(self): |
2499a096 | 267 | iotests.create_image(backing_img, TestMirrorNoBacking.image_len) |
44c7ca5e PB |
268 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
269 | self.vm = iotests.VM().add_drive(test_img) | |
270 | self.vm.launch() | |
271 | ||
272 | def tearDown(self): | |
273 | self.vm.shutdown() | |
274 | os.remove(test_img) | |
275 | os.remove(backing_img) | |
866323f3 FZ |
276 | try: |
277 | os.remove(target_backing_img) | |
278 | except: | |
279 | pass | |
44c7ca5e PB |
280 | os.remove(target_img) |
281 | ||
282 | def test_complete(self): | |
ecc1c88e | 283 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
284 | |
285 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
286 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
287 | mode='existing', target=target_img) | |
288 | self.assert_qmp(result, 'return', {}) | |
289 | ||
290 | self.complete_and_wait() | |
291 | result = self.vm.qmp('query-block') | |
292 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
293 | self.vm.shutdown() | |
866323f3 | 294 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
295 | 'target image does not match source after mirroring') |
296 | ||
297 | def test_cancel(self): | |
ecc1c88e | 298 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
299 | |
300 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
301 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
302 | mode='existing', target=target_img) | |
303 | self.assert_qmp(result, 'return', {}) | |
304 | ||
2575fe16 | 305 | self.wait_ready_and_cancel() |
44c7ca5e PB |
306 | result = self.vm.qmp('query-block') |
307 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
308 | self.vm.shutdown() | |
866323f3 | 309 | self.assertTrue(iotests.compare_images(test_img, target_img), |
44c7ca5e PB |
310 | 'target image does not match source after mirroring') |
311 | ||
b812f671 | 312 | def test_large_cluster(self): |
ecc1c88e | 313 | self.assert_no_active_block_jobs() |
b812f671 PB |
314 | |
315 | # qemu-img create fails if the image is not there | |
316 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d' | |
317 | %(TestMirrorNoBacking.image_len), target_backing_img) | |
318 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
319 | % (TestMirrorNoBacking.image_len, target_backing_img), target_img) | |
b812f671 PB |
320 | |
321 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
322 | mode='existing', target=target_img) | |
323 | self.assert_qmp(result, 'return', {}) | |
324 | ||
325 | self.complete_and_wait() | |
326 | result = self.vm.qmp('query-block') | |
327 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
328 | self.vm.shutdown() | |
866323f3 | 329 | self.assertTrue(iotests.compare_images(test_img, target_img), |
b812f671 PB |
330 | 'target image does not match source after mirroring') |
331 | ||
866323f3 | 332 | class TestMirrorResized(iotests.QMPTestCase): |
a04eca10 VI |
333 | backing_len = 1 * 1024 * 1024 # MB |
334 | image_len = 2 * 1024 * 1024 # MB | |
335 | ||
336 | def setUp(self): | |
2499a096 | 337 | iotests.create_image(backing_img, TestMirrorResized.backing_len) |
a04eca10 VI |
338 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) |
339 | qemu_img('resize', test_img, '2M') | |
340 | self.vm = iotests.VM().add_drive(test_img) | |
341 | self.vm.launch() | |
342 | ||
343 | def tearDown(self): | |
344 | self.vm.shutdown() | |
345 | os.remove(test_img) | |
346 | os.remove(backing_img) | |
347 | try: | |
348 | os.remove(target_img) | |
349 | except OSError: | |
350 | pass | |
351 | ||
352 | def test_complete_top(self): | |
ecc1c88e | 353 | self.assert_no_active_block_jobs() |
a04eca10 VI |
354 | |
355 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
356 | target=target_img) | |
357 | self.assert_qmp(result, 'return', {}) | |
358 | ||
359 | self.complete_and_wait() | |
360 | result = self.vm.qmp('query-block') | |
361 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
362 | self.vm.shutdown() | |
3a3918c3 | 363 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
364 | 'target image does not match source after mirroring') |
365 | ||
366 | def test_complete_full(self): | |
ecc1c88e | 367 | self.assert_no_active_block_jobs() |
a04eca10 VI |
368 | |
369 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
370 | target=target_img) | |
371 | self.assert_qmp(result, 'return', {}) | |
372 | ||
373 | self.complete_and_wait() | |
374 | result = self.vm.qmp('query-block') | |
375 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
376 | self.vm.shutdown() | |
3a3918c3 | 377 | self.assertTrue(iotests.compare_images(test_img, target_img), |
a04eca10 VI |
378 | 'target image does not match source after mirroring') |
379 | ||
866323f3 | 380 | class TestReadErrors(iotests.QMPTestCase): |
9dfa9f59 PB |
381 | image_len = 2 * 1024 * 1024 # MB |
382 | ||
383 | # this should be a multiple of twice the default granularity | |
384 | # so that we hit this offset first in state 1 | |
385 | MIRROR_GRANULARITY = 1024 * 1024 | |
386 | ||
387 | def create_blkdebug_file(self, name, event, errno): | |
388 | file = open(name, 'w') | |
389 | file.write(''' | |
390 | [inject-error] | |
391 | state = "1" | |
392 | event = "%s" | |
393 | errno = "%d" | |
394 | immediately = "off" | |
395 | once = "on" | |
396 | sector = "%d" | |
397 | ||
398 | [set-state] | |
399 | state = "1" | |
400 | event = "%s" | |
401 | new_state = "2" | |
402 | ||
403 | [set-state] | |
404 | state = "2" | |
405 | event = "%s" | |
406 | new_state = "1" | |
9a3a9a63 | 407 | ''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) |
9dfa9f59 PB |
408 | file.close() |
409 | ||
410 | def setUp(self): | |
411 | self.blkdebug_file = backing_img + ".blkdebug" | |
2499a096 | 412 | iotests.create_image(backing_img, TestReadErrors.image_len) |
9dfa9f59 PB |
413 | self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) |
414 | qemu_img('create', '-f', iotests.imgfmt, | |
415 | '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' | |
416 | % (self.blkdebug_file, backing_img), | |
417 | test_img) | |
b812f671 PB |
418 | # Write something for tests that use sync='top' |
419 | qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), | |
420 | test_img) | |
9dfa9f59 PB |
421 | self.vm = iotests.VM().add_drive(test_img) |
422 | self.vm.launch() | |
423 | ||
424 | def tearDown(self): | |
425 | self.vm.shutdown() | |
426 | os.remove(test_img) | |
db11d1ee | 427 | os.remove(target_img) |
9dfa9f59 PB |
428 | os.remove(backing_img) |
429 | os.remove(self.blkdebug_file) | |
430 | ||
431 | def test_report_read(self): | |
ecc1c88e | 432 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
433 | |
434 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
435 | target=target_img) | |
436 | self.assert_qmp(result, 'return', {}) | |
437 | ||
438 | completed = False | |
439 | error = False | |
440 | while not completed: | |
441 | for event in self.vm.get_qmp_events(wait=True): | |
442 | if event['event'] == 'BLOCK_JOB_ERROR': | |
443 | self.assert_qmp(event, 'data/device', 'drive0') | |
444 | self.assert_qmp(event, 'data/operation', 'read') | |
445 | error = True | |
446 | elif event['event'] == 'BLOCK_JOB_READY': | |
447 | self.assertTrue(False, 'job completed unexpectedly') | |
448 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
449 | self.assertTrue(error, 'job completed unexpectedly') | |
450 | self.assert_qmp(event, 'data/type', 'mirror') | |
451 | self.assert_qmp(event, 'data/device', 'drive0') | |
452 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
9dfa9f59 | 453 | completed = True |
1dac83f1 KW |
454 | elif event['event'] == 'JOB_STATUS_CHANGE': |
455 | self.assert_qmp(event, 'data/id', 'drive0') | |
9dfa9f59 | 456 | |
ecc1c88e | 457 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
458 | |
459 | def test_ignore_read(self): | |
ecc1c88e | 460 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
461 | |
462 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
463 | target=target_img, on_source_error='ignore') | |
464 | self.assert_qmp(result, 'return', {}) | |
465 | ||
466 | event = self.vm.get_qmp_event(wait=True) | |
1dac83f1 KW |
467 | while event['event'] == 'JOB_STATUS_CHANGE': |
468 | self.assert_qmp(event, 'data/id', 'drive0') | |
469 | event = self.vm.get_qmp_event(wait=True) | |
470 | ||
fa1cfb40 | 471 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') |
9dfa9f59 PB |
472 | self.assert_qmp(event, 'data/device', 'drive0') |
473 | self.assert_qmp(event, 'data/operation', 'read') | |
474 | result = self.vm.qmp('query-block-jobs') | |
475 | self.assert_qmp(result, 'return[0]/paused', False) | |
476 | self.complete_and_wait() | |
9dfa9f59 | 477 | |
b812f671 | 478 | def test_large_cluster(self): |
ecc1c88e | 479 | self.assert_no_active_block_jobs() |
b812f671 PB |
480 | |
481 | # Test COW into the target image. The first half of the | |
482 | # cluster at MIRROR_GRANULARITY has to be copied from | |
483 | # backing_img, even though sync='top'. | |
484 | qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) | |
485 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
486 | on_source_error='ignore', | |
487 | mode='existing', target=target_img) | |
488 | self.assert_qmp(result, 'return', {}) | |
489 | ||
490 | event = self.vm.get_qmp_event(wait=True) | |
1dac83f1 KW |
491 | while event['event'] == 'JOB_STATUS_CHANGE': |
492 | self.assert_qmp(event, 'data/id', 'drive0') | |
493 | event = self.vm.get_qmp_event(wait=True) | |
494 | ||
fa1cfb40 | 495 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') |
b812f671 PB |
496 | self.assert_qmp(event, 'data/device', 'drive0') |
497 | self.assert_qmp(event, 'data/operation', 'read') | |
498 | result = self.vm.qmp('query-block-jobs') | |
499 | self.assert_qmp(result, 'return[0]/paused', False) | |
500 | self.complete_and_wait() | |
501 | self.vm.shutdown() | |
502 | ||
503 | # Detach blkdebug to compare images successfully | |
504 | qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) | |
3a3918c3 | 505 | self.assertTrue(iotests.compare_images(test_img, target_img), |
b812f671 PB |
506 | 'target image does not match source after mirroring') |
507 | ||
9dfa9f59 | 508 | def test_stop_read(self): |
ecc1c88e | 509 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
510 | |
511 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
512 | target=target_img, on_source_error='stop') | |
513 | self.assert_qmp(result, 'return', {}) | |
514 | ||
515 | error = False | |
516 | ready = False | |
517 | while not ready: | |
518 | for event in self.vm.get_qmp_events(wait=True): | |
519 | if event['event'] == 'BLOCK_JOB_ERROR': | |
520 | self.assert_qmp(event, 'data/device', 'drive0') | |
521 | self.assert_qmp(event, 'data/operation', 'read') | |
522 | ||
523 | result = self.vm.qmp('query-block-jobs') | |
524 | self.assert_qmp(result, 'return[0]/paused', True) | |
525 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
526 | ||
527 | result = self.vm.qmp('block-job-resume', device='drive0') | |
528 | self.assert_qmp(result, 'return', {}) | |
529 | error = True | |
530 | elif event['event'] == 'BLOCK_JOB_READY': | |
531 | self.assertTrue(error, 'job completed unexpectedly') | |
532 | self.assert_qmp(event, 'data/device', 'drive0') | |
533 | ready = True | |
534 | ||
535 | result = self.vm.qmp('query-block-jobs') | |
536 | self.assert_qmp(result, 'return[0]/paused', False) | |
537 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
538 | ||
539 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 540 | self.assert_no_active_block_jobs() |
9dfa9f59 | 541 | |
866323f3 | 542 | class TestWriteErrors(iotests.QMPTestCase): |
9dfa9f59 PB |
543 | image_len = 2 * 1024 * 1024 # MB |
544 | ||
545 | # this should be a multiple of twice the default granularity | |
546 | # so that we hit this offset first in state 1 | |
547 | MIRROR_GRANULARITY = 1024 * 1024 | |
548 | ||
549 | def create_blkdebug_file(self, name, event, errno): | |
550 | file = open(name, 'w') | |
551 | file.write(''' | |
552 | [inject-error] | |
553 | state = "1" | |
554 | event = "%s" | |
555 | errno = "%d" | |
556 | immediately = "off" | |
557 | once = "on" | |
558 | sector = "%d" | |
559 | ||
560 | [set-state] | |
561 | state = "1" | |
562 | event = "%s" | |
563 | new_state = "2" | |
564 | ||
565 | [set-state] | |
566 | state = "2" | |
567 | event = "%s" | |
568 | new_state = "1" | |
9a3a9a63 | 569 | ''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) |
9dfa9f59 PB |
570 | file.close() |
571 | ||
572 | def setUp(self): | |
573 | self.blkdebug_file = target_img + ".blkdebug" | |
2499a096 | 574 | iotests.create_image(backing_img, TestWriteErrors.image_len) |
9dfa9f59 PB |
575 | self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) |
576 | qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) | |
577 | self.vm = iotests.VM().add_drive(test_img) | |
578 | self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img) | |
579 | qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img) | |
580 | self.vm.launch() | |
581 | ||
582 | def tearDown(self): | |
583 | self.vm.shutdown() | |
584 | os.remove(test_img) | |
db11d1ee | 585 | os.remove(target_img) |
9dfa9f59 PB |
586 | os.remove(backing_img) |
587 | os.remove(self.blkdebug_file) | |
588 | ||
589 | def test_report_write(self): | |
ecc1c88e | 590 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
591 | |
592 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
593 | mode='existing', target=self.target_img) | |
594 | self.assert_qmp(result, 'return', {}) | |
595 | ||
596 | completed = False | |
597 | error = False | |
598 | while not completed: | |
599 | for event in self.vm.get_qmp_events(wait=True): | |
600 | if event['event'] == 'BLOCK_JOB_ERROR': | |
601 | self.assert_qmp(event, 'data/device', 'drive0') | |
602 | self.assert_qmp(event, 'data/operation', 'write') | |
603 | error = True | |
604 | elif event['event'] == 'BLOCK_JOB_READY': | |
605 | self.assertTrue(False, 'job completed unexpectedly') | |
606 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
607 | self.assertTrue(error, 'job completed unexpectedly') | |
608 | self.assert_qmp(event, 'data/type', 'mirror') | |
609 | self.assert_qmp(event, 'data/device', 'drive0') | |
610 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
9dfa9f59 PB |
611 | completed = True |
612 | ||
ecc1c88e | 613 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
614 | |
615 | def test_ignore_write(self): | |
ecc1c88e | 616 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
617 | |
618 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
619 | mode='existing', target=self.target_img, | |
620 | on_target_error='ignore') | |
621 | self.assert_qmp(result, 'return', {}) | |
622 | ||
1dac83f1 | 623 | event = self.vm.event_wait(name='BLOCK_JOB_ERROR') |
fa1cfb40 | 624 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') |
9dfa9f59 PB |
625 | self.assert_qmp(event, 'data/device', 'drive0') |
626 | self.assert_qmp(event, 'data/operation', 'write') | |
627 | result = self.vm.qmp('query-block-jobs') | |
628 | self.assert_qmp(result, 'return[0]/paused', False) | |
629 | self.complete_and_wait() | |
9dfa9f59 PB |
630 | |
631 | def test_stop_write(self): | |
ecc1c88e | 632 | self.assert_no_active_block_jobs() |
9dfa9f59 PB |
633 | |
634 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
635 | mode='existing', target=self.target_img, | |
636 | on_target_error='stop') | |
637 | self.assert_qmp(result, 'return', {}) | |
638 | ||
639 | error = False | |
640 | ready = False | |
641 | while not ready: | |
642 | for event in self.vm.get_qmp_events(wait=True): | |
643 | if event['event'] == 'BLOCK_JOB_ERROR': | |
644 | self.assert_qmp(event, 'data/device', 'drive0') | |
645 | self.assert_qmp(event, 'data/operation', 'write') | |
646 | ||
647 | result = self.vm.qmp('query-block-jobs') | |
648 | self.assert_qmp(result, 'return[0]/paused', True) | |
649 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
650 | ||
651 | result = self.vm.qmp('block-job-resume', device='drive0') | |
652 | self.assert_qmp(result, 'return', {}) | |
653 | ||
654 | result = self.vm.qmp('query-block-jobs') | |
655 | self.assert_qmp(result, 'return[0]/paused', False) | |
656 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
657 | error = True | |
658 | elif event['event'] == 'BLOCK_JOB_READY': | |
659 | self.assertTrue(error, 'job completed unexpectedly') | |
660 | self.assert_qmp(event, 'data/device', 'drive0') | |
661 | ready = True | |
662 | ||
663 | self.complete_and_wait(wait_ready=False) | |
ecc1c88e | 664 | self.assert_no_active_block_jobs() |
9dfa9f59 | 665 | |
866323f3 | 666 | class TestSetSpeed(iotests.QMPTestCase): |
44c7ca5e PB |
667 | image_len = 80 * 1024 * 1024 # MB |
668 | ||
669 | def setUp(self): | |
670 | qemu_img('create', backing_img, str(TestSetSpeed.image_len)) | |
671 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
672 | self.vm = iotests.VM().add_drive(test_img) | |
673 | self.vm.launch() | |
674 | ||
675 | def tearDown(self): | |
676 | self.vm.shutdown() | |
677 | os.remove(test_img) | |
678 | os.remove(backing_img) | |
679 | os.remove(target_img) | |
680 | ||
681 | def test_set_speed(self): | |
ecc1c88e | 682 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
683 | |
684 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
685 | target=target_img) | |
686 | self.assert_qmp(result, 'return', {}) | |
687 | ||
688 | # Default speed is 0 | |
689 | result = self.vm.qmp('query-block-jobs') | |
690 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
691 | self.assert_qmp(result, 'return[0]/speed', 0) | |
692 | ||
693 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) | |
694 | self.assert_qmp(result, 'return', {}) | |
695 | ||
696 | # Ensure the speed we set was accepted | |
697 | result = self.vm.qmp('query-block-jobs') | |
698 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
699 | self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) | |
700 | ||
2575fe16 | 701 | self.wait_ready_and_cancel() |
44c7ca5e PB |
702 | |
703 | # Check setting speed in drive-mirror works | |
704 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
705 | target=target_img, speed=4*1024*1024) | |
706 | self.assert_qmp(result, 'return', {}) | |
707 | ||
708 | result = self.vm.qmp('query-block-jobs') | |
709 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
710 | self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) | |
711 | ||
2575fe16 | 712 | self.wait_ready_and_cancel() |
44c7ca5e PB |
713 | |
714 | def test_set_speed_invalid(self): | |
ecc1c88e | 715 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
716 | |
717 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
718 | target=target_img, speed=-1) | |
719 | self.assert_qmp(result, 'error/class', 'GenericError') | |
720 | ||
ecc1c88e | 721 | self.assert_no_active_block_jobs() |
44c7ca5e PB |
722 | |
723 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
724 | target=target_img) | |
725 | self.assert_qmp(result, 'return', {}) | |
726 | ||
727 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) | |
728 | self.assert_qmp(result, 'error/class', 'GenericError') | |
729 | ||
2575fe16 | 730 | self.wait_ready_and_cancel() |
44c7ca5e | 731 | |
866323f3 | 732 | class TestUnbackedSource(iotests.QMPTestCase): |
c15badee HR |
733 | image_len = 2 * 1024 * 1024 # MB |
734 | ||
735 | def setUp(self): | |
736 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
737 | str(TestUnbackedSource.image_len)) | |
9463ee1f | 738 | self.vm = iotests.VM() |
c15badee | 739 | self.vm.launch() |
9463ee1f HR |
740 | result = self.vm.qmp('blockdev-add', node_name='drive0', |
741 | driver=iotests.imgfmt, | |
742 | file={ | |
743 | 'driver': 'file', | |
744 | 'filename': test_img, | |
745 | }) | |
746 | self.assert_qmp(result, 'return', {}) | |
c15badee HR |
747 | |
748 | def tearDown(self): | |
749 | self.vm.shutdown() | |
750 | os.remove(test_img) | |
751 | os.remove(target_img) | |
752 | ||
171d6431 HR |
753 | def test_absolute_paths_full(self): |
754 | self.assert_no_active_block_jobs() | |
9463ee1f | 755 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', |
171d6431 HR |
756 | sync='full', target=target_img, |
757 | mode='absolute-paths') | |
758 | self.assert_qmp(result, 'return', {}) | |
759 | self.complete_and_wait() | |
760 | self.assert_no_active_block_jobs() | |
761 | ||
762 | def test_absolute_paths_top(self): | |
763 | self.assert_no_active_block_jobs() | |
9463ee1f | 764 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', |
171d6431 HR |
765 | sync='top', target=target_img, |
766 | mode='absolute-paths') | |
767 | self.assert_qmp(result, 'return', {}) | |
768 | self.complete_and_wait() | |
c15badee HR |
769 | self.assert_no_active_block_jobs() |
770 | ||
171d6431 HR |
771 | def test_absolute_paths_none(self): |
772 | self.assert_no_active_block_jobs() | |
9463ee1f | 773 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', |
171d6431 HR |
774 | sync='none', target=target_img, |
775 | mode='absolute-paths') | |
776 | self.assert_qmp(result, 'return', {}) | |
777 | self.complete_and_wait() | |
778 | self.assert_no_active_block_jobs() | |
c15badee | 779 | |
9463ee1f HR |
780 | def test_existing_full(self): |
781 | qemu_img('create', '-f', iotests.imgfmt, target_img, | |
782 | str(self.image_len)) | |
783 | qemu_io('-c', 'write -P 42 0 64k', target_img) | |
784 | ||
785 | self.assert_no_active_block_jobs() | |
786 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', | |
787 | sync='full', target=target_img, mode='existing') | |
788 | self.assert_qmp(result, 'return', {}) | |
789 | self.complete_and_wait() | |
790 | self.assert_no_active_block_jobs() | |
791 | ||
792 | result = self.vm.qmp('blockdev-del', node_name='drive0') | |
793 | self.assert_qmp(result, 'return', {}) | |
794 | ||
795 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
796 | 'target image does not match source after mirroring') | |
797 | ||
798 | def test_blockdev_full(self): | |
799 | qemu_img('create', '-f', iotests.imgfmt, target_img, | |
800 | str(self.image_len)) | |
801 | qemu_io('-c', 'write -P 42 0 64k', target_img) | |
802 | ||
803 | result = self.vm.qmp('blockdev-add', node_name='target', | |
804 | driver=iotests.imgfmt, | |
805 | file={ | |
806 | 'driver': 'file', | |
807 | 'filename': target_img, | |
808 | }) | |
809 | self.assert_qmp(result, 'return', {}) | |
810 | ||
811 | self.assert_no_active_block_jobs() | |
812 | result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0', | |
813 | sync='full', target='target') | |
814 | self.assert_qmp(result, 'return', {}) | |
815 | self.complete_and_wait() | |
816 | self.assert_no_active_block_jobs() | |
817 | ||
818 | result = self.vm.qmp('blockdev-del', node_name='drive0') | |
819 | self.assert_qmp(result, 'return', {}) | |
820 | ||
821 | result = self.vm.qmp('blockdev-del', node_name='target') | |
822 | self.assert_qmp(result, 'return', {}) | |
823 | ||
824 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
825 | 'target image does not match source after mirroring') | |
826 | ||
ccee3d8f JS |
827 | class TestGranularity(iotests.QMPTestCase): |
828 | image_len = 10 * 1024 * 1024 # MB | |
829 | ||
830 | def setUp(self): | |
831 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
832 | str(TestGranularity.image_len)) | |
833 | qemu_io('-c', 'write 0 %d' % (self.image_len), | |
834 | test_img) | |
835 | self.vm = iotests.VM().add_drive(test_img) | |
836 | self.vm.launch() | |
837 | ||
838 | def tearDown(self): | |
839 | self.vm.shutdown() | |
840 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
841 | 'target image does not match source after mirroring') | |
842 | os.remove(test_img) | |
843 | os.remove(target_img) | |
844 | ||
845 | def test_granularity(self): | |
846 | self.assert_no_active_block_jobs() | |
847 | result = self.vm.qmp('drive-mirror', device='drive0', | |
848 | sync='full', target=target_img, | |
849 | mode='absolute-paths', granularity=8192) | |
850 | self.assert_qmp(result, 'return', {}) | |
1dac83f1 | 851 | |
ccee3d8f | 852 | event = self.vm.get_qmp_event(wait=60.0) |
1dac83f1 KW |
853 | while event['event'] == 'JOB_STATUS_CHANGE': |
854 | self.assert_qmp(event, 'data/id', 'drive0') | |
855 | event = self.vm.get_qmp_event(wait=60.0) | |
856 | ||
ccee3d8f JS |
857 | # Failures will manifest as COMPLETED/ERROR. |
858 | self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') | |
859 | self.complete_and_wait(drive='drive0', wait_ready=False) | |
860 | self.assert_no_active_block_jobs() | |
861 | ||
866323f3 | 862 | class TestRepairQuorum(iotests.QMPTestCase): |
d88964ae BC |
863 | """ This class test quorum file repair using drive-mirror. |
864 | It's mostly a fork of TestSingleDrive """ | |
865 | image_len = 1 * 1024 * 1024 # MB | |
866 | IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] | |
867 | ||
9442bebe | 868 | @iotests.skip_if_unsupported(['quorum']) |
d88964ae BC |
869 | def setUp(self): |
870 | self.vm = iotests.VM() | |
871 | ||
0ed82f7a HR |
872 | if iotests.qemu_default_machine == 'pc': |
873 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
874 | ||
d88964ae BC |
875 | # Add each individual quorum images |
876 | for i in self.IMAGES: | |
877 | qemu_img('create', '-f', iotests.imgfmt, i, | |
89e21945 | 878 | str(self.image_len)) |
d88964ae BC |
879 | # Assign a node name to each quorum image in order to manipulate |
880 | # them | |
881 | opts = "node-name=img%i" % self.IMAGES.index(i) | |
f718ca14 HR |
882 | opts += ',driver=%s' % iotests.imgfmt |
883 | opts += ',file.driver=file' | |
884 | opts += ',file.filename=%s' % i | |
885 | self.vm = self.vm.add_blockdev(opts) | |
d88964ae BC |
886 | |
887 | self.vm.launch() | |
888 | ||
889 | #assemble the quorum block device from the individual files | |
0153d2f5 KW |
890 | args = { "driver": "quorum", "node-name": "quorum0", |
891 | "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } | |
9442bebe TH |
892 | result = self.vm.qmp("blockdev-add", **args) |
893 | self.assert_qmp(result, 'return', {}) | |
d88964ae BC |
894 | |
895 | ||
896 | def tearDown(self): | |
897 | self.vm.shutdown() | |
a1da1878 HR |
898 | for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file, |
899 | nbd_sock_path ]: | |
d88964ae BC |
900 | # Do a try/except because the test may have deleted some images |
901 | try: | |
902 | os.remove(i) | |
903 | except OSError: | |
904 | pass | |
905 | ||
906 | def test_complete(self): | |
476fb028 KW |
907 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
908 | sync='full', node_name="repair0", replaces="img1", | |
d88964ae BC |
909 | target=quorum_repair_img, format=iotests.imgfmt) |
910 | self.assert_qmp(result, 'return', {}) | |
911 | ||
476fb028 | 912 | self.complete_and_wait(drive="job0") |
e71fc0ba | 913 | self.assert_has_block_node("repair0", quorum_repair_img) |
c351afd6 | 914 | self.vm.assert_block_path('quorum0', '/children.1', 'repair0') |
d88964ae BC |
915 | self.vm.shutdown() |
916 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
917 | 'target image does not match source after mirroring') | |
918 | ||
919 | def test_cancel(self): | |
476fb028 KW |
920 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
921 | sync='full', node_name="repair0", replaces="img1", | |
d88964ae BC |
922 | target=quorum_repair_img, format=iotests.imgfmt) |
923 | self.assert_qmp(result, 'return', {}) | |
924 | ||
476fb028 | 925 | self.cancel_and_wait(drive="job0", force=True) |
d88964ae BC |
926 | # here we check that the last registered quorum file has not been |
927 | # swapped out and unref | |
e71fc0ba | 928 | self.assert_has_block_node(None, quorum_img3) |
d88964ae BC |
929 | |
930 | def test_cancel_after_ready(self): | |
476fb028 KW |
931 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
932 | sync='full', node_name="repair0", replaces="img1", | |
d88964ae BC |
933 | target=quorum_repair_img, format=iotests.imgfmt) |
934 | self.assert_qmp(result, 'return', {}) | |
935 | ||
476fb028 | 936 | self.wait_ready_and_cancel(drive="job0") |
d88964ae BC |
937 | # here we check that the last registered quorum file has not been |
938 | # swapped out and unref | |
e71fc0ba | 939 | self.assert_has_block_node(None, quorum_img3) |
d88964ae BC |
940 | self.vm.shutdown() |
941 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
942 | 'target image does not match source after mirroring') | |
943 | ||
944 | def test_pause(self): | |
476fb028 KW |
945 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
946 | sync='full', node_name="repair0", replaces="img1", | |
d88964ae BC |
947 | target=quorum_repair_img, format=iotests.imgfmt) |
948 | self.assert_qmp(result, 'return', {}) | |
949 | ||
2c93c5cb | 950 | self.pause_job('job0') |
d88964ae | 951 | |
d88964ae BC |
952 | result = self.vm.qmp('query-block-jobs') |
953 | offset = self.dictpath(result, 'return[0]/offset') | |
954 | ||
2c93c5cb | 955 | time.sleep(0.5) |
d88964ae BC |
956 | result = self.vm.qmp('query-block-jobs') |
957 | self.assert_qmp(result, 'return[0]/offset', offset) | |
958 | ||
476fb028 | 959 | result = self.vm.qmp('block-job-resume', device='job0') |
d88964ae BC |
960 | self.assert_qmp(result, 'return', {}) |
961 | ||
476fb028 | 962 | self.complete_and_wait(drive="job0") |
d88964ae BC |
963 | self.vm.shutdown() |
964 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
965 | 'target image does not match source after mirroring') | |
966 | ||
967 | def test_medium_not_found(self): | |
d8683155 BT |
968 | if iotests.qemu_default_machine != 'pc': |
969 | return | |
970 | ||
476fb028 | 971 | result = self.vm.qmp('drive-mirror', job_id='job0', device='drive0', # CD-ROM |
0ed82f7a | 972 | sync='full', |
d88964ae BC |
973 | node_name='repair0', |
974 | replaces='img1', | |
975 | target=quorum_repair_img, format=iotests.imgfmt) | |
976 | self.assert_qmp(result, 'error/class', 'GenericError') | |
977 | ||
978 | def test_image_not_found(self): | |
476fb028 KW |
979 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
980 | sync='full', node_name='repair0', replaces='img1', | |
981 | mode='existing', target=quorum_repair_img, | |
982 | format=iotests.imgfmt) | |
d88964ae BC |
983 | self.assert_qmp(result, 'error/class', 'GenericError') |
984 | ||
985 | def test_device_not_found(self): | |
476fb028 KW |
986 | result = self.vm.qmp('drive-mirror', job_id='job0', |
987 | device='nonexistent', sync='full', | |
d88964ae BC |
988 | node_name='repair0', |
989 | replaces='img1', | |
990 | target=quorum_repair_img, format=iotests.imgfmt) | |
0524e93a | 991 | self.assert_qmp(result, 'error/class', 'GenericError') |
d88964ae BC |
992 | |
993 | def test_wrong_sync_mode(self): | |
476fb028 | 994 | result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0', |
d88964ae BC |
995 | node_name='repair0', |
996 | replaces='img1', | |
997 | target=quorum_repair_img, format=iotests.imgfmt) | |
998 | self.assert_qmp(result, 'error/class', 'GenericError') | |
999 | ||
1000 | def test_no_node_name(self): | |
476fb028 KW |
1001 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
1002 | sync='full', replaces='img1', | |
d88964ae BC |
1003 | target=quorum_repair_img, format=iotests.imgfmt) |
1004 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1005 | ||
67cc32eb | 1006 | def test_nonexistent_replaces(self): |
476fb028 KW |
1007 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
1008 | sync='full', node_name='repair0', replaces='img77', | |
d88964ae BC |
1009 | target=quorum_repair_img, format=iotests.imgfmt) |
1010 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1011 | ||
1012 | def test_after_a_quorum_snapshot(self): | |
1013 | result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', | |
1014 | snapshot_file=quorum_snapshot_file, | |
1015 | snapshot_node_name="snap1"); | |
1016 | ||
476fb028 KW |
1017 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
1018 | sync='full', node_name='repair0', replaces="img1", | |
d88964ae BC |
1019 | target=quorum_repair_img, format=iotests.imgfmt) |
1020 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1021 | ||
476fb028 KW |
1022 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', |
1023 | sync='full', node_name='repair0', replaces="snap1", | |
d88964ae BC |
1024 | target=quorum_repair_img, format=iotests.imgfmt) |
1025 | self.assert_qmp(result, 'return', {}) | |
1026 | ||
476fb028 | 1027 | self.complete_and_wait('job0') |
e71fc0ba | 1028 | self.assert_has_block_node("repair0", quorum_repair_img) |
c351afd6 | 1029 | self.vm.assert_block_path('quorum0', '/children.1', 'repair0') |
d88964ae | 1030 | |
a1da1878 HR |
1031 | def test_with_other_parent(self): |
1032 | """ | |
1033 | Check that we cannot replace a Quorum child when it has other | |
1034 | parents. | |
1035 | """ | |
1036 | result = self.vm.qmp('nbd-server-start', | |
1037 | addr={ | |
1038 | 'type': 'unix', | |
1039 | 'data': {'path': nbd_sock_path} | |
1040 | }) | |
1041 | self.assert_qmp(result, 'return', {}) | |
1042 | ||
1043 | result = self.vm.qmp('nbd-server-add', device='img1') | |
1044 | self.assert_qmp(result, 'return', {}) | |
1045 | ||
1046 | result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0', | |
1047 | sync='full', node_name='repair0', replaces='img1', | |
1048 | target=quorum_repair_img, format=iotests.imgfmt) | |
1049 | self.assert_qmp(result, 'error/desc', | |
1050 | "Cannot replace 'img1' by a node mirrored from " | |
1051 | "'quorum0', because it cannot be guaranteed that doing " | |
1052 | "so would not lead to an abrupt change of visible data") | |
1053 | ||
1054 | def test_with_other_parents_after_mirror_start(self): | |
1055 | """ | |
1056 | The same as test_with_other_parent(), but add the NBD server | |
1057 | only when the mirror job is already running. | |
1058 | """ | |
1059 | result = self.vm.qmp('nbd-server-start', | |
1060 | addr={ | |
1061 | 'type': 'unix', | |
1062 | 'data': {'path': nbd_sock_path} | |
1063 | }) | |
1064 | self.assert_qmp(result, 'return', {}) | |
1065 | ||
1066 | result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0', | |
1067 | sync='full', node_name='repair0', replaces='img1', | |
1068 | target=quorum_repair_img, format=iotests.imgfmt) | |
1069 | self.assert_qmp(result, 'return', {}) | |
1070 | ||
1071 | result = self.vm.qmp('nbd-server-add', device='img1') | |
1072 | self.assert_qmp(result, 'return', {}) | |
1073 | ||
1074 | # The full error message goes to stderr, we will check it later | |
1075 | self.complete_and_wait('mirror', | |
1076 | completion_error='Operation not permitted') | |
1077 | ||
1078 | # Should not have been replaced | |
1079 | self.vm.assert_block_path('quorum0', '/children.1', 'img1') | |
1080 | ||
1081 | # Check the full error message now | |
1082 | self.vm.shutdown() | |
1083 | log = self.vm.get_log() | |
1084 | log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) | |
1085 | log = re.sub(r'^Formatting.*\n', '', log) | |
1086 | log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log) | |
1087 | log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log) | |
1088 | ||
1089 | self.assertEqual(log, | |
1090 | "Can no longer replace 'img1' by 'repair0', because " + | |
1091 | "it can no longer be guaranteed that doing so would " + | |
1092 | "not lead to an abrupt change of visible data") | |
1093 | ||
1094 | ||
5694923a HR |
1095 | # Test mirroring with a source that does not have any parents (not even a |
1096 | # BlockBackend) | |
1097 | class TestOrphanedSource(iotests.QMPTestCase): | |
1098 | def setUp(self): | |
1099 | blk0 = { 'node-name': 'src', | |
1100 | 'driver': 'null-co' } | |
1101 | ||
1102 | blk1 = { 'node-name': 'dest', | |
1103 | 'driver': 'null-co' } | |
1104 | ||
1105 | blk2 = { 'node-name': 'dest-ro', | |
1106 | 'driver': 'null-co', | |
1107 | 'read-only': 'on' } | |
1108 | ||
1109 | self.vm = iotests.VM() | |
62a94288 KW |
1110 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) |
1111 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) | |
1112 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) | |
5694923a HR |
1113 | self.vm.launch() |
1114 | ||
1115 | def tearDown(self): | |
1116 | self.vm.shutdown() | |
1117 | ||
1118 | def test_no_job_id(self): | |
1119 | self.assert_no_active_block_jobs() | |
1120 | ||
1121 | result = self.vm.qmp('blockdev-mirror', device='src', sync='full', | |
1122 | target='dest') | |
1123 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1124 | ||
1125 | def test_success(self): | |
1126 | self.assert_no_active_block_jobs() | |
1127 | ||
1128 | result = self.vm.qmp('blockdev-mirror', job_id='job', device='src', | |
1129 | sync='full', target='dest') | |
1130 | self.assert_qmp(result, 'return', {}) | |
1131 | ||
1132 | self.complete_and_wait('job') | |
1133 | ||
1134 | def test_failing_permissions(self): | |
1135 | self.assert_no_active_block_jobs() | |
1136 | ||
1137 | result = self.vm.qmp('blockdev-mirror', device='src', sync='full', | |
1138 | target='dest-ro') | |
1139 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1140 | ||
9592fe45 HR |
1141 | def test_failing_permission_in_complete(self): |
1142 | self.assert_no_active_block_jobs() | |
1143 | ||
1144 | # Unshare consistent-read on the target | |
1145 | # (The mirror job does not care) | |
1146 | result = self.vm.qmp('blockdev-add', | |
1147 | driver='blkdebug', | |
1148 | node_name='dest-perm', | |
1149 | image='dest', | |
1150 | unshare_child_perms=['consistent-read']) | |
1151 | self.assert_qmp(result, 'return', {}) | |
1152 | ||
1153 | result = self.vm.qmp('blockdev-mirror', job_id='job', device='src', | |
1154 | sync='full', target='dest', | |
1155 | filter_node_name='mirror-filter') | |
1156 | self.assert_qmp(result, 'return', {}) | |
1157 | ||
1158 | # Require consistent-read on the source | |
1159 | # (We can only add this node once the job has started, or it | |
1160 | # will complain that it does not want to run on non-root nodes) | |
1161 | result = self.vm.qmp('blockdev-add', | |
1162 | driver='blkdebug', | |
1163 | node_name='src-perm', | |
1164 | image='src', | |
1165 | take_child_perms=['consistent-read']) | |
1166 | self.assert_qmp(result, 'return', {}) | |
1167 | ||
1168 | # While completing, mirror will attempt to replace src by | |
1169 | # dest, which must fail because src-perm requires | |
1170 | # consistent-read but dest-perm does not share it; thus | |
1171 | # aborting the job when it is supposed to complete | |
1172 | self.complete_and_wait('job', | |
1173 | completion_error='Operation not permitted') | |
1174 | ||
1175 | # Assert that all of our nodes are still there (except for the | |
1176 | # mirror filter, which should be gone despite the failure) | |
1177 | nodes = self.vm.qmp('query-named-block-nodes')['return'] | |
1178 | nodes = [node['node-name'] for node in nodes] | |
1179 | ||
1180 | for expect in ('src', 'src-perm', 'dest', 'dest-perm'): | |
1181 | self.assertTrue(expect in nodes, '%s disappeared' % expect) | |
1182 | self.assertFalse('mirror-filter' in nodes, | |
1183 | 'Mirror filter node did not disappear') | |
1184 | ||
c45a88f4 HR |
1185 | # Test cases for @replaces that do not necessarily involve Quorum |
1186 | class TestReplaces(iotests.QMPTestCase): | |
1187 | # Each of these test cases needs their own block graph, so do not | |
1188 | # create any nodes here | |
1189 | def setUp(self): | |
1190 | self.vm = iotests.VM() | |
1191 | self.vm.launch() | |
1192 | ||
1193 | def tearDown(self): | |
1194 | self.vm.shutdown() | |
1195 | for img in (test_img, target_img): | |
1196 | try: | |
1197 | os.remove(img) | |
1198 | except OSError: | |
1199 | pass | |
1200 | ||
1201 | @iotests.skip_if_unsupported(['copy-on-read']) | |
1202 | def test_replace_filter(self): | |
1203 | """ | |
1204 | Check that we can replace filter nodes. | |
1205 | """ | |
1206 | result = self.vm.qmp('blockdev-add', **{ | |
1207 | 'driver': 'copy-on-read', | |
1208 | 'node-name': 'filter0', | |
1209 | 'file': { | |
1210 | 'driver': 'copy-on-read', | |
1211 | 'node-name': 'filter1', | |
1212 | 'file': { | |
1213 | 'driver': 'null-co' | |
1214 | } | |
1215 | } | |
1216 | }) | |
1217 | self.assert_qmp(result, 'return', {}) | |
1218 | ||
1219 | result = self.vm.qmp('blockdev-add', | |
1220 | node_name='target', driver='null-co') | |
1221 | self.assert_qmp(result, 'return', {}) | |
1222 | ||
1223 | result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0', | |
1224 | target='target', sync='full', replaces='filter1') | |
1225 | self.assert_qmp(result, 'return', {}) | |
1226 | ||
1227 | self.complete_and_wait('mirror') | |
1228 | ||
1229 | self.vm.assert_block_path('filter0', '/file', 'target') | |
1230 | ||
44c7ca5e | 1231 | if __name__ == '__main__': |
103cbc77 | 1232 | iotests.main(supported_fmts=['qcow2', 'qed'], |
877d18f2 TH |
1233 | supported_protocols=['file'], |
1234 | supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd']) |