]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/env python | |
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 | |
23 | import iotests | |
24 | from iotests import qemu_img, qemu_io | |
25 | ||
26 | backing_img = os.path.join(iotests.test_dir, 'backing.img') | |
27 | target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') | |
28 | test_img = os.path.join(iotests.test_dir, 'test.img') | |
29 | target_img = os.path.join(iotests.test_dir, 'target.img') | |
30 | ||
31 | quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img') | |
32 | quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img') | |
33 | quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img') | |
34 | quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img') | |
35 | quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img') | |
36 | ||
37 | class TestSingleDrive(iotests.QMPTestCase): | |
38 | image_len = 1 * 1024 * 1024 # MB | |
39 | qmp_cmd = 'drive-mirror' | |
40 | qmp_target = target_img | |
41 | ||
42 | def setUp(self): | |
43 | iotests.create_image(backing_img, self.image_len) | |
44 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
45 | self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base") | |
46 | if iotests.qemu_default_machine == 'pc': | |
47 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
48 | self.vm.launch() | |
49 | ||
50 | def tearDown(self): | |
51 | self.vm.shutdown() | |
52 | os.remove(test_img) | |
53 | os.remove(backing_img) | |
54 | try: | |
55 | os.remove(target_img) | |
56 | except OSError: | |
57 | pass | |
58 | ||
59 | def test_complete(self): | |
60 | self.assert_no_active_block_jobs() | |
61 | ||
62 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
63 | target=self.qmp_target) | |
64 | self.assert_qmp(result, 'return', {}) | |
65 | ||
66 | self.complete_and_wait() | |
67 | result = self.vm.qmp('query-block') | |
68 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
69 | self.vm.shutdown() | |
70 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
71 | 'target image does not match source after mirroring') | |
72 | ||
73 | def test_cancel(self): | |
74 | self.assert_no_active_block_jobs() | |
75 | ||
76 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
77 | target=self.qmp_target) | |
78 | self.assert_qmp(result, 'return', {}) | |
79 | ||
80 | self.cancel_and_wait(force=True) | |
81 | result = self.vm.qmp('query-block') | |
82 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
83 | self.vm.shutdown() | |
84 | ||
85 | def test_cancel_after_ready(self): | |
86 | self.assert_no_active_block_jobs() | |
87 | ||
88 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
89 | target=self.qmp_target) | |
90 | self.assert_qmp(result, 'return', {}) | |
91 | ||
92 | self.wait_ready_and_cancel() | |
93 | result = self.vm.qmp('query-block') | |
94 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
95 | self.vm.shutdown() | |
96 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
97 | 'target image does not match source after mirroring') | |
98 | ||
99 | def test_pause(self): | |
100 | self.assert_no_active_block_jobs() | |
101 | ||
102 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
103 | target=self.qmp_target) | |
104 | self.assert_qmp(result, 'return', {}) | |
105 | ||
106 | self.pause_job('drive0') | |
107 | ||
108 | result = self.vm.qmp('query-block-jobs') | |
109 | offset = self.dictpath(result, 'return[0]/offset') | |
110 | ||
111 | time.sleep(0.5) | |
112 | result = self.vm.qmp('query-block-jobs') | |
113 | self.assert_qmp(result, 'return[0]/offset', offset) | |
114 | ||
115 | result = self.vm.qmp('block-job-resume', device='drive0') | |
116 | self.assert_qmp(result, 'return', {}) | |
117 | ||
118 | self.complete_and_wait() | |
119 | self.vm.shutdown() | |
120 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
121 | 'target image does not match source after mirroring') | |
122 | ||
123 | def test_small_buffer(self): | |
124 | self.assert_no_active_block_jobs() | |
125 | ||
126 | # A small buffer is rounded up automatically | |
127 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
128 | buf_size=4096, target=self.qmp_target) | |
129 | self.assert_qmp(result, 'return', {}) | |
130 | ||
131 | self.complete_and_wait() | |
132 | result = self.vm.qmp('query-block') | |
133 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
134 | self.vm.shutdown() | |
135 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
136 | 'target image does not match source after mirroring') | |
137 | ||
138 | def test_small_buffer2(self): | |
139 | self.assert_no_active_block_jobs() | |
140 | ||
141 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' | |
142 | % (self.image_len, self.image_len), target_img) | |
143 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
144 | buf_size=65536, mode='existing', target=self.qmp_target) | |
145 | self.assert_qmp(result, 'return', {}) | |
146 | ||
147 | self.complete_and_wait() | |
148 | result = self.vm.qmp('query-block') | |
149 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
150 | self.vm.shutdown() | |
151 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
152 | 'target image does not match source after mirroring') | |
153 | ||
154 | def test_large_cluster(self): | |
155 | self.assert_no_active_block_jobs() | |
156 | ||
157 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s' | |
158 | % (self.image_len, backing_img), target_img) | |
159 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
160 | mode='existing', target=self.qmp_target) | |
161 | self.assert_qmp(result, 'return', {}) | |
162 | ||
163 | self.complete_and_wait() | |
164 | result = self.vm.qmp('query-block') | |
165 | self.assert_qmp(result, 'return[0]/inserted/file', target_img) | |
166 | self.vm.shutdown() | |
167 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
168 | 'target image does not match source after mirroring') | |
169 | ||
170 | # Tests that the insertion of the mirror_top filter node doesn't make a | |
171 | # difference to query-block | |
172 | def test_implicit_node(self): | |
173 | self.assert_no_active_block_jobs() | |
174 | ||
175 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
176 | target=self.qmp_target) | |
177 | self.assert_qmp(result, 'return', {}) | |
178 | ||
179 | result = self.vm.qmp('query-block') | |
180 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
181 | self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) | |
182 | self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img) | |
183 | self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1) | |
184 | self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) | |
185 | self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img) | |
186 | ||
187 | result = self.vm.qmp('query-blockstats') | |
188 | self.assert_qmp(result, 'return[0]/node-name', 'top') | |
189 | self.assert_qmp(result, 'return[0]/backing/node-name', 'base') | |
190 | ||
191 | self.cancel_and_wait(force=True) | |
192 | result = self.vm.qmp('query-block') | |
193 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
194 | self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt) | |
195 | self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img) | |
196 | self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1) | |
197 | self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img) | |
198 | self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img) | |
199 | ||
200 | result = self.vm.qmp('query-blockstats') | |
201 | self.assert_qmp(result, 'return[0]/node-name', 'top') | |
202 | self.assert_qmp(result, 'return[0]/backing/node-name', 'base') | |
203 | ||
204 | self.vm.shutdown() | |
205 | ||
206 | def test_medium_not_found(self): | |
207 | if iotests.qemu_default_machine != 'pc': | |
208 | return | |
209 | ||
210 | result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full', | |
211 | target=self.qmp_target) | |
212 | self.assert_qmp(result, 'error/class', 'GenericError') | |
213 | ||
214 | def test_image_not_found(self): | |
215 | result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full', | |
216 | mode='existing', target=self.qmp_target) | |
217 | self.assert_qmp(result, 'error/class', 'GenericError') | |
218 | ||
219 | def test_device_not_found(self): | |
220 | result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full', | |
221 | target=self.qmp_target) | |
222 | self.assert_qmp(result, 'error/class', 'GenericError') | |
223 | ||
224 | class TestSingleBlockdev(TestSingleDrive): | |
225 | qmp_cmd = 'blockdev-mirror' | |
226 | qmp_target = 'node1' | |
227 | ||
228 | def setUp(self): | |
229 | TestSingleDrive.setUp(self) | |
230 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img) | |
231 | args = {'driver': iotests.imgfmt, | |
232 | 'node-name': self.qmp_target, | |
233 | 'file': { 'filename': target_img, 'driver': 'file' } } | |
234 | result = self.vm.qmp("blockdev-add", **args) | |
235 | self.assert_qmp(result, 'return', {}) | |
236 | ||
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 | ||
243 | test_large_cluster = None | |
244 | test_image_not_found = None | |
245 | test_small_buffer2 = None | |
246 | ||
247 | class TestSingleDriveZeroLength(TestSingleDrive): | |
248 | image_len = 0 | |
249 | test_small_buffer2 = None | |
250 | test_large_cluster = None | |
251 | ||
252 | class TestSingleBlockdevZeroLength(TestSingleBlockdev): | |
253 | image_len = 0 | |
254 | ||
255 | class TestSingleDriveUnalignedLength(TestSingleDrive): | |
256 | image_len = 1025 * 1024 | |
257 | test_small_buffer2 = None | |
258 | test_large_cluster = None | |
259 | ||
260 | class TestSingleBlockdevUnalignedLength(TestSingleBlockdev): | |
261 | image_len = 1025 * 1024 | |
262 | ||
263 | class TestMirrorNoBacking(iotests.QMPTestCase): | |
264 | image_len = 2 * 1024 * 1024 # MB | |
265 | ||
266 | def setUp(self): | |
267 | iotests.create_image(backing_img, TestMirrorNoBacking.image_len) | |
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) | |
276 | try: | |
277 | os.remove(target_backing_img) | |
278 | except: | |
279 | pass | |
280 | os.remove(target_img) | |
281 | ||
282 | def test_complete(self): | |
283 | self.assert_no_active_block_jobs() | |
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() | |
294 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
295 | 'target image does not match source after mirroring') | |
296 | ||
297 | def test_cancel(self): | |
298 | self.assert_no_active_block_jobs() | |
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 | ||
305 | self.wait_ready_and_cancel() | |
306 | result = self.vm.qmp('query-block') | |
307 | self.assert_qmp(result, 'return[0]/inserted/file', test_img) | |
308 | self.vm.shutdown() | |
309 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
310 | 'target image does not match source after mirroring') | |
311 | ||
312 | def test_large_cluster(self): | |
313 | self.assert_no_active_block_jobs() | |
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) | |
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() | |
329 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
330 | 'target image does not match source after mirroring') | |
331 | ||
332 | class TestMirrorResized(iotests.QMPTestCase): | |
333 | backing_len = 1 * 1024 * 1024 # MB | |
334 | image_len = 2 * 1024 * 1024 # MB | |
335 | ||
336 | def setUp(self): | |
337 | iotests.create_image(backing_img, TestMirrorResized.backing_len) | |
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): | |
353 | self.assert_no_active_block_jobs() | |
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() | |
363 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
364 | 'target image does not match source after mirroring') | |
365 | ||
366 | def test_complete_full(self): | |
367 | self.assert_no_active_block_jobs() | |
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() | |
377 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
378 | 'target image does not match source after mirroring') | |
379 | ||
380 | class TestReadErrors(iotests.QMPTestCase): | |
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" | |
407 | ''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) | |
408 | file.close() | |
409 | ||
410 | def setUp(self): | |
411 | self.blkdebug_file = backing_img + ".blkdebug" | |
412 | iotests.create_image(backing_img, TestReadErrors.image_len) | |
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) | |
418 | # Write something for tests that use sync='top' | |
419 | qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536), | |
420 | test_img) | |
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) | |
427 | os.remove(target_img) | |
428 | os.remove(backing_img) | |
429 | os.remove(self.blkdebug_file) | |
430 | ||
431 | def test_report_read(self): | |
432 | self.assert_no_active_block_jobs() | |
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') | |
453 | completed = True | |
454 | elif event['event'] == 'JOB_STATUS_CHANGE': | |
455 | self.assert_qmp(event, 'data/id', 'drive0') | |
456 | ||
457 | self.assert_no_active_block_jobs() | |
458 | self.vm.shutdown() | |
459 | ||
460 | def test_ignore_read(self): | |
461 | self.assert_no_active_block_jobs() | |
462 | ||
463 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
464 | target=target_img, on_source_error='ignore') | |
465 | self.assert_qmp(result, 'return', {}) | |
466 | ||
467 | event = self.vm.get_qmp_event(wait=True) | |
468 | while event['event'] == 'JOB_STATUS_CHANGE': | |
469 | self.assert_qmp(event, 'data/id', 'drive0') | |
470 | event = self.vm.get_qmp_event(wait=True) | |
471 | ||
472 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') | |
473 | self.assert_qmp(event, 'data/device', 'drive0') | |
474 | self.assert_qmp(event, 'data/operation', 'read') | |
475 | result = self.vm.qmp('query-block-jobs') | |
476 | self.assert_qmp(result, 'return[0]/paused', False) | |
477 | self.complete_and_wait() | |
478 | self.vm.shutdown() | |
479 | ||
480 | def test_large_cluster(self): | |
481 | self.assert_no_active_block_jobs() | |
482 | ||
483 | # Test COW into the target image. The first half of the | |
484 | # cluster at MIRROR_GRANULARITY has to be copied from | |
485 | # backing_img, even though sync='top'. | |
486 | qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img) | |
487 | result = self.vm.qmp('drive-mirror', device='drive0', sync='top', | |
488 | on_source_error='ignore', | |
489 | mode='existing', target=target_img) | |
490 | self.assert_qmp(result, 'return', {}) | |
491 | ||
492 | event = self.vm.get_qmp_event(wait=True) | |
493 | while event['event'] == 'JOB_STATUS_CHANGE': | |
494 | self.assert_qmp(event, 'data/id', 'drive0') | |
495 | event = self.vm.get_qmp_event(wait=True) | |
496 | ||
497 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') | |
498 | self.assert_qmp(event, 'data/device', 'drive0') | |
499 | self.assert_qmp(event, 'data/operation', 'read') | |
500 | result = self.vm.qmp('query-block-jobs') | |
501 | self.assert_qmp(result, 'return[0]/paused', False) | |
502 | self.complete_and_wait() | |
503 | self.vm.shutdown() | |
504 | ||
505 | # Detach blkdebug to compare images successfully | |
506 | qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img) | |
507 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
508 | 'target image does not match source after mirroring') | |
509 | ||
510 | def test_stop_read(self): | |
511 | self.assert_no_active_block_jobs() | |
512 | ||
513 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
514 | target=target_img, on_source_error='stop') | |
515 | self.assert_qmp(result, 'return', {}) | |
516 | ||
517 | error = False | |
518 | ready = False | |
519 | while not ready: | |
520 | for event in self.vm.get_qmp_events(wait=True): | |
521 | if event['event'] == 'BLOCK_JOB_ERROR': | |
522 | self.assert_qmp(event, 'data/device', 'drive0') | |
523 | self.assert_qmp(event, 'data/operation', 'read') | |
524 | ||
525 | result = self.vm.qmp('query-block-jobs') | |
526 | self.assert_qmp(result, 'return[0]/paused', True) | |
527 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
528 | ||
529 | result = self.vm.qmp('block-job-resume', device='drive0') | |
530 | self.assert_qmp(result, 'return', {}) | |
531 | error = True | |
532 | elif event['event'] == 'BLOCK_JOB_READY': | |
533 | self.assertTrue(error, 'job completed unexpectedly') | |
534 | self.assert_qmp(event, 'data/device', 'drive0') | |
535 | ready = True | |
536 | ||
537 | result = self.vm.qmp('query-block-jobs') | |
538 | self.assert_qmp(result, 'return[0]/paused', False) | |
539 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
540 | ||
541 | self.complete_and_wait(wait_ready=False) | |
542 | self.assert_no_active_block_jobs() | |
543 | self.vm.shutdown() | |
544 | ||
545 | class TestWriteErrors(iotests.QMPTestCase): | |
546 | image_len = 2 * 1024 * 1024 # MB | |
547 | ||
548 | # this should be a multiple of twice the default granularity | |
549 | # so that we hit this offset first in state 1 | |
550 | MIRROR_GRANULARITY = 1024 * 1024 | |
551 | ||
552 | def create_blkdebug_file(self, name, event, errno): | |
553 | file = open(name, 'w') | |
554 | file.write(''' | |
555 | [inject-error] | |
556 | state = "1" | |
557 | event = "%s" | |
558 | errno = "%d" | |
559 | immediately = "off" | |
560 | once = "on" | |
561 | sector = "%d" | |
562 | ||
563 | [set-state] | |
564 | state = "1" | |
565 | event = "%s" | |
566 | new_state = "2" | |
567 | ||
568 | [set-state] | |
569 | state = "2" | |
570 | event = "%s" | |
571 | new_state = "1" | |
572 | ''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) | |
573 | file.close() | |
574 | ||
575 | def setUp(self): | |
576 | self.blkdebug_file = target_img + ".blkdebug" | |
577 | iotests.create_image(backing_img, TestWriteErrors.image_len) | |
578 | self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5) | |
579 | qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img) | |
580 | self.vm = iotests.VM().add_drive(test_img) | |
581 | self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img) | |
582 | qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img) | |
583 | self.vm.launch() | |
584 | ||
585 | def tearDown(self): | |
586 | self.vm.shutdown() | |
587 | os.remove(test_img) | |
588 | os.remove(target_img) | |
589 | os.remove(backing_img) | |
590 | os.remove(self.blkdebug_file) | |
591 | ||
592 | def test_report_write(self): | |
593 | self.assert_no_active_block_jobs() | |
594 | ||
595 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
596 | mode='existing', target=self.target_img) | |
597 | self.assert_qmp(result, 'return', {}) | |
598 | ||
599 | completed = False | |
600 | error = False | |
601 | while not completed: | |
602 | for event in self.vm.get_qmp_events(wait=True): | |
603 | if event['event'] == 'BLOCK_JOB_ERROR': | |
604 | self.assert_qmp(event, 'data/device', 'drive0') | |
605 | self.assert_qmp(event, 'data/operation', 'write') | |
606 | error = True | |
607 | elif event['event'] == 'BLOCK_JOB_READY': | |
608 | self.assertTrue(False, 'job completed unexpectedly') | |
609 | elif event['event'] == 'BLOCK_JOB_COMPLETED': | |
610 | self.assertTrue(error, 'job completed unexpectedly') | |
611 | self.assert_qmp(event, 'data/type', 'mirror') | |
612 | self.assert_qmp(event, 'data/device', 'drive0') | |
613 | self.assert_qmp(event, 'data/error', 'Input/output error') | |
614 | completed = True | |
615 | ||
616 | self.assert_no_active_block_jobs() | |
617 | self.vm.shutdown() | |
618 | ||
619 | def test_ignore_write(self): | |
620 | self.assert_no_active_block_jobs() | |
621 | ||
622 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
623 | mode='existing', target=self.target_img, | |
624 | on_target_error='ignore') | |
625 | self.assert_qmp(result, 'return', {}) | |
626 | ||
627 | event = self.vm.event_wait(name='BLOCK_JOB_ERROR') | |
628 | self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') | |
629 | self.assert_qmp(event, 'data/device', 'drive0') | |
630 | self.assert_qmp(event, 'data/operation', 'write') | |
631 | result = self.vm.qmp('query-block-jobs') | |
632 | self.assert_qmp(result, 'return[0]/paused', False) | |
633 | self.complete_and_wait() | |
634 | self.vm.shutdown() | |
635 | ||
636 | def test_stop_write(self): | |
637 | self.assert_no_active_block_jobs() | |
638 | ||
639 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
640 | mode='existing', target=self.target_img, | |
641 | on_target_error='stop') | |
642 | self.assert_qmp(result, 'return', {}) | |
643 | ||
644 | error = False | |
645 | ready = False | |
646 | while not ready: | |
647 | for event in self.vm.get_qmp_events(wait=True): | |
648 | if event['event'] == 'BLOCK_JOB_ERROR': | |
649 | self.assert_qmp(event, 'data/device', 'drive0') | |
650 | self.assert_qmp(event, 'data/operation', 'write') | |
651 | ||
652 | result = self.vm.qmp('query-block-jobs') | |
653 | self.assert_qmp(result, 'return[0]/paused', True) | |
654 | self.assert_qmp(result, 'return[0]/io-status', 'failed') | |
655 | ||
656 | result = self.vm.qmp('block-job-resume', device='drive0') | |
657 | self.assert_qmp(result, 'return', {}) | |
658 | ||
659 | result = self.vm.qmp('query-block-jobs') | |
660 | self.assert_qmp(result, 'return[0]/paused', False) | |
661 | self.assert_qmp(result, 'return[0]/io-status', 'ok') | |
662 | error = True | |
663 | elif event['event'] == 'BLOCK_JOB_READY': | |
664 | self.assertTrue(error, 'job completed unexpectedly') | |
665 | self.assert_qmp(event, 'data/device', 'drive0') | |
666 | ready = True | |
667 | ||
668 | self.complete_and_wait(wait_ready=False) | |
669 | self.assert_no_active_block_jobs() | |
670 | self.vm.shutdown() | |
671 | ||
672 | class TestSetSpeed(iotests.QMPTestCase): | |
673 | image_len = 80 * 1024 * 1024 # MB | |
674 | ||
675 | def setUp(self): | |
676 | qemu_img('create', backing_img, str(TestSetSpeed.image_len)) | |
677 | qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img) | |
678 | self.vm = iotests.VM().add_drive(test_img) | |
679 | self.vm.launch() | |
680 | ||
681 | def tearDown(self): | |
682 | self.vm.shutdown() | |
683 | os.remove(test_img) | |
684 | os.remove(backing_img) | |
685 | os.remove(target_img) | |
686 | ||
687 | def test_set_speed(self): | |
688 | self.assert_no_active_block_jobs() | |
689 | ||
690 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
691 | target=target_img) | |
692 | self.assert_qmp(result, 'return', {}) | |
693 | ||
694 | # Default speed is 0 | |
695 | result = self.vm.qmp('query-block-jobs') | |
696 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
697 | self.assert_qmp(result, 'return[0]/speed', 0) | |
698 | ||
699 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024) | |
700 | self.assert_qmp(result, 'return', {}) | |
701 | ||
702 | # Ensure the speed we set was accepted | |
703 | result = self.vm.qmp('query-block-jobs') | |
704 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
705 | self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024) | |
706 | ||
707 | self.wait_ready_and_cancel() | |
708 | ||
709 | # Check setting speed in drive-mirror works | |
710 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
711 | target=target_img, speed=4*1024*1024) | |
712 | self.assert_qmp(result, 'return', {}) | |
713 | ||
714 | result = self.vm.qmp('query-block-jobs') | |
715 | self.assert_qmp(result, 'return[0]/device', 'drive0') | |
716 | self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024) | |
717 | ||
718 | self.wait_ready_and_cancel() | |
719 | ||
720 | def test_set_speed_invalid(self): | |
721 | self.assert_no_active_block_jobs() | |
722 | ||
723 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
724 | target=target_img, speed=-1) | |
725 | self.assert_qmp(result, 'error/class', 'GenericError') | |
726 | ||
727 | self.assert_no_active_block_jobs() | |
728 | ||
729 | result = self.vm.qmp('drive-mirror', device='drive0', sync='full', | |
730 | target=target_img) | |
731 | self.assert_qmp(result, 'return', {}) | |
732 | ||
733 | result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1) | |
734 | self.assert_qmp(result, 'error/class', 'GenericError') | |
735 | ||
736 | self.wait_ready_and_cancel() | |
737 | ||
738 | class TestUnbackedSource(iotests.QMPTestCase): | |
739 | image_len = 2 * 1024 * 1024 # MB | |
740 | ||
741 | def setUp(self): | |
742 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
743 | str(TestUnbackedSource.image_len)) | |
744 | self.vm = iotests.VM() | |
745 | self.vm.launch() | |
746 | result = self.vm.qmp('blockdev-add', node_name='drive0', | |
747 | driver=iotests.imgfmt, | |
748 | file={ | |
749 | 'driver': 'file', | |
750 | 'filename': test_img, | |
751 | }) | |
752 | self.assert_qmp(result, 'return', {}) | |
753 | ||
754 | def tearDown(self): | |
755 | self.vm.shutdown() | |
756 | os.remove(test_img) | |
757 | os.remove(target_img) | |
758 | ||
759 | def test_absolute_paths_full(self): | |
760 | self.assert_no_active_block_jobs() | |
761 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', | |
762 | sync='full', target=target_img, | |
763 | mode='absolute-paths') | |
764 | self.assert_qmp(result, 'return', {}) | |
765 | self.complete_and_wait() | |
766 | self.assert_no_active_block_jobs() | |
767 | ||
768 | def test_absolute_paths_top(self): | |
769 | self.assert_no_active_block_jobs() | |
770 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', | |
771 | sync='top', target=target_img, | |
772 | mode='absolute-paths') | |
773 | self.assert_qmp(result, 'return', {}) | |
774 | self.complete_and_wait() | |
775 | self.assert_no_active_block_jobs() | |
776 | ||
777 | def test_absolute_paths_none(self): | |
778 | self.assert_no_active_block_jobs() | |
779 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', | |
780 | sync='none', target=target_img, | |
781 | mode='absolute-paths') | |
782 | self.assert_qmp(result, 'return', {}) | |
783 | self.complete_and_wait() | |
784 | self.assert_no_active_block_jobs() | |
785 | ||
786 | def test_existing_full(self): | |
787 | qemu_img('create', '-f', iotests.imgfmt, target_img, | |
788 | str(self.image_len)) | |
789 | qemu_io('-c', 'write -P 42 0 64k', target_img) | |
790 | ||
791 | self.assert_no_active_block_jobs() | |
792 | result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0', | |
793 | sync='full', target=target_img, mode='existing') | |
794 | self.assert_qmp(result, 'return', {}) | |
795 | self.complete_and_wait() | |
796 | self.assert_no_active_block_jobs() | |
797 | ||
798 | result = self.vm.qmp('blockdev-del', node_name='drive0') | |
799 | self.assert_qmp(result, 'return', {}) | |
800 | ||
801 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
802 | 'target image does not match source after mirroring') | |
803 | ||
804 | def test_blockdev_full(self): | |
805 | qemu_img('create', '-f', iotests.imgfmt, target_img, | |
806 | str(self.image_len)) | |
807 | qemu_io('-c', 'write -P 42 0 64k', target_img) | |
808 | ||
809 | result = self.vm.qmp('blockdev-add', node_name='target', | |
810 | driver=iotests.imgfmt, | |
811 | file={ | |
812 | 'driver': 'file', | |
813 | 'filename': target_img, | |
814 | }) | |
815 | self.assert_qmp(result, 'return', {}) | |
816 | ||
817 | self.assert_no_active_block_jobs() | |
818 | result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0', | |
819 | sync='full', target='target') | |
820 | self.assert_qmp(result, 'return', {}) | |
821 | self.complete_and_wait() | |
822 | self.assert_no_active_block_jobs() | |
823 | ||
824 | result = self.vm.qmp('blockdev-del', node_name='drive0') | |
825 | self.assert_qmp(result, 'return', {}) | |
826 | ||
827 | result = self.vm.qmp('blockdev-del', node_name='target') | |
828 | self.assert_qmp(result, 'return', {}) | |
829 | ||
830 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
831 | 'target image does not match source after mirroring') | |
832 | ||
833 | class TestGranularity(iotests.QMPTestCase): | |
834 | image_len = 10 * 1024 * 1024 # MB | |
835 | ||
836 | def setUp(self): | |
837 | qemu_img('create', '-f', iotests.imgfmt, test_img, | |
838 | str(TestGranularity.image_len)) | |
839 | qemu_io('-c', 'write 0 %d' % (self.image_len), | |
840 | test_img) | |
841 | self.vm = iotests.VM().add_drive(test_img) | |
842 | self.vm.launch() | |
843 | ||
844 | def tearDown(self): | |
845 | self.vm.shutdown() | |
846 | self.assertTrue(iotests.compare_images(test_img, target_img), | |
847 | 'target image does not match source after mirroring') | |
848 | os.remove(test_img) | |
849 | os.remove(target_img) | |
850 | ||
851 | def test_granularity(self): | |
852 | self.assert_no_active_block_jobs() | |
853 | result = self.vm.qmp('drive-mirror', device='drive0', | |
854 | sync='full', target=target_img, | |
855 | mode='absolute-paths', granularity=8192) | |
856 | self.assert_qmp(result, 'return', {}) | |
857 | ||
858 | event = self.vm.get_qmp_event(wait=60.0) | |
859 | while event['event'] == 'JOB_STATUS_CHANGE': | |
860 | self.assert_qmp(event, 'data/id', 'drive0') | |
861 | event = self.vm.get_qmp_event(wait=60.0) | |
862 | ||
863 | # Failures will manifest as COMPLETED/ERROR. | |
864 | self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') | |
865 | self.complete_and_wait(drive='drive0', wait_ready=False) | |
866 | self.assert_no_active_block_jobs() | |
867 | ||
868 | class TestRepairQuorum(iotests.QMPTestCase): | |
869 | """ This class test quorum file repair using drive-mirror. | |
870 | It's mostly a fork of TestSingleDrive """ | |
871 | image_len = 1 * 1024 * 1024 # MB | |
872 | IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ] | |
873 | ||
874 | def setUp(self): | |
875 | self.vm = iotests.VM() | |
876 | ||
877 | if iotests.qemu_default_machine == 'pc': | |
878 | self.vm.add_drive(None, 'media=cdrom', 'ide') | |
879 | ||
880 | # Add each individual quorum images | |
881 | for i in self.IMAGES: | |
882 | qemu_img('create', '-f', iotests.imgfmt, i, | |
883 | str(TestSingleDrive.image_len)) | |
884 | # Assign a node name to each quorum image in order to manipulate | |
885 | # them | |
886 | opts = "node-name=img%i" % self.IMAGES.index(i) | |
887 | self.vm = self.vm.add_drive(i, opts) | |
888 | ||
889 | self.vm.launch() | |
890 | ||
891 | #assemble the quorum block device from the individual files | |
892 | args = { "driver": "quorum", "node-name": "quorum0", | |
893 | "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } | |
894 | if iotests.supports_quorum(): | |
895 | result = self.vm.qmp("blockdev-add", **args) | |
896 | self.assert_qmp(result, 'return', {}) | |
897 | ||
898 | ||
899 | def tearDown(self): | |
900 | self.vm.shutdown() | |
901 | for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file ]: | |
902 | # Do a try/except because the test may have deleted some images | |
903 | try: | |
904 | os.remove(i) | |
905 | except OSError: | |
906 | pass | |
907 | ||
908 | def test_complete(self): | |
909 | if not iotests.supports_quorum(): | |
910 | return | |
911 | ||
912 | self.assert_no_active_block_jobs() | |
913 | ||
914 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
915 | sync='full', node_name="repair0", replaces="img1", | |
916 | target=quorum_repair_img, format=iotests.imgfmt) | |
917 | self.assert_qmp(result, 'return', {}) | |
918 | ||
919 | self.complete_and_wait(drive="job0") | |
920 | self.assert_has_block_node("repair0", quorum_repair_img) | |
921 | # TODO: a better test requiring some QEMU infrastructure will be added | |
922 | # to check that this file is really driven by quorum | |
923 | self.vm.shutdown() | |
924 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
925 | 'target image does not match source after mirroring') | |
926 | ||
927 | def test_cancel(self): | |
928 | if not iotests.supports_quorum(): | |
929 | return | |
930 | ||
931 | self.assert_no_active_block_jobs() | |
932 | ||
933 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
934 | sync='full', node_name="repair0", replaces="img1", | |
935 | target=quorum_repair_img, format=iotests.imgfmt) | |
936 | self.assert_qmp(result, 'return', {}) | |
937 | ||
938 | self.cancel_and_wait(drive="job0", force=True) | |
939 | # here we check that the last registered quorum file has not been | |
940 | # swapped out and unref | |
941 | self.assert_has_block_node(None, quorum_img3) | |
942 | self.vm.shutdown() | |
943 | ||
944 | def test_cancel_after_ready(self): | |
945 | if not iotests.supports_quorum(): | |
946 | return | |
947 | ||
948 | self.assert_no_active_block_jobs() | |
949 | ||
950 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
951 | sync='full', node_name="repair0", replaces="img1", | |
952 | target=quorum_repair_img, format=iotests.imgfmt) | |
953 | self.assert_qmp(result, 'return', {}) | |
954 | ||
955 | self.wait_ready_and_cancel(drive="job0") | |
956 | # here we check that the last registered quorum file has not been | |
957 | # swapped out and unref | |
958 | self.assert_has_block_node(None, quorum_img3) | |
959 | self.vm.shutdown() | |
960 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
961 | 'target image does not match source after mirroring') | |
962 | ||
963 | def test_pause(self): | |
964 | if not iotests.supports_quorum(): | |
965 | return | |
966 | ||
967 | self.assert_no_active_block_jobs() | |
968 | ||
969 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
970 | sync='full', node_name="repair0", replaces="img1", | |
971 | target=quorum_repair_img, format=iotests.imgfmt) | |
972 | self.assert_qmp(result, 'return', {}) | |
973 | ||
974 | self.pause_job('job0') | |
975 | ||
976 | result = self.vm.qmp('query-block-jobs') | |
977 | offset = self.dictpath(result, 'return[0]/offset') | |
978 | ||
979 | time.sleep(0.5) | |
980 | result = self.vm.qmp('query-block-jobs') | |
981 | self.assert_qmp(result, 'return[0]/offset', offset) | |
982 | ||
983 | result = self.vm.qmp('block-job-resume', device='job0') | |
984 | self.assert_qmp(result, 'return', {}) | |
985 | ||
986 | self.complete_and_wait(drive="job0") | |
987 | self.vm.shutdown() | |
988 | self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img), | |
989 | 'target image does not match source after mirroring') | |
990 | ||
991 | def test_medium_not_found(self): | |
992 | if not iotests.supports_quorum(): | |
993 | return | |
994 | ||
995 | if iotests.qemu_default_machine != 'pc': | |
996 | return | |
997 | ||
998 | result = self.vm.qmp('drive-mirror', job_id='job0', device='drive0', # CD-ROM | |
999 | sync='full', | |
1000 | node_name='repair0', | |
1001 | replaces='img1', | |
1002 | target=quorum_repair_img, format=iotests.imgfmt) | |
1003 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1004 | ||
1005 | def test_image_not_found(self): | |
1006 | if not iotests.supports_quorum(): | |
1007 | return | |
1008 | ||
1009 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
1010 | sync='full', node_name='repair0', replaces='img1', | |
1011 | mode='existing', target=quorum_repair_img, | |
1012 | format=iotests.imgfmt) | |
1013 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1014 | ||
1015 | def test_device_not_found(self): | |
1016 | if not iotests.supports_quorum(): | |
1017 | return | |
1018 | ||
1019 | result = self.vm.qmp('drive-mirror', job_id='job0', | |
1020 | device='nonexistent', sync='full', | |
1021 | node_name='repair0', | |
1022 | replaces='img1', | |
1023 | target=quorum_repair_img, format=iotests.imgfmt) | |
1024 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1025 | ||
1026 | def test_wrong_sync_mode(self): | |
1027 | if not iotests.supports_quorum(): | |
1028 | return | |
1029 | ||
1030 | result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0', | |
1031 | node_name='repair0', | |
1032 | replaces='img1', | |
1033 | target=quorum_repair_img, format=iotests.imgfmt) | |
1034 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1035 | ||
1036 | def test_no_node_name(self): | |
1037 | if not iotests.supports_quorum(): | |
1038 | return | |
1039 | ||
1040 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
1041 | sync='full', replaces='img1', | |
1042 | target=quorum_repair_img, format=iotests.imgfmt) | |
1043 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1044 | ||
1045 | def test_nonexistent_replaces(self): | |
1046 | if not iotests.supports_quorum(): | |
1047 | return | |
1048 | ||
1049 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
1050 | sync='full', node_name='repair0', replaces='img77', | |
1051 | target=quorum_repair_img, format=iotests.imgfmt) | |
1052 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1053 | ||
1054 | def test_after_a_quorum_snapshot(self): | |
1055 | if not iotests.supports_quorum(): | |
1056 | return | |
1057 | ||
1058 | result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1', | |
1059 | snapshot_file=quorum_snapshot_file, | |
1060 | snapshot_node_name="snap1"); | |
1061 | ||
1062 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
1063 | sync='full', node_name='repair0', replaces="img1", | |
1064 | target=quorum_repair_img, format=iotests.imgfmt) | |
1065 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1066 | ||
1067 | result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0', | |
1068 | sync='full', node_name='repair0', replaces="snap1", | |
1069 | target=quorum_repair_img, format=iotests.imgfmt) | |
1070 | self.assert_qmp(result, 'return', {}) | |
1071 | ||
1072 | self.complete_and_wait('job0') | |
1073 | self.assert_has_block_node("repair0", quorum_repair_img) | |
1074 | # TODO: a better test requiring some QEMU infrastructure will be added | |
1075 | # to check that this file is really driven by quorum | |
1076 | self.vm.shutdown() | |
1077 | ||
1078 | # Test mirroring with a source that does not have any parents (not even a | |
1079 | # BlockBackend) | |
1080 | class TestOrphanedSource(iotests.QMPTestCase): | |
1081 | def setUp(self): | |
1082 | blk0 = { 'node-name': 'src', | |
1083 | 'driver': 'null-co' } | |
1084 | ||
1085 | blk1 = { 'node-name': 'dest', | |
1086 | 'driver': 'null-co' } | |
1087 | ||
1088 | blk2 = { 'node-name': 'dest-ro', | |
1089 | 'driver': 'null-co', | |
1090 | 'read-only': 'on' } | |
1091 | ||
1092 | self.vm = iotests.VM() | |
1093 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) | |
1094 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) | |
1095 | self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) | |
1096 | self.vm.launch() | |
1097 | ||
1098 | def tearDown(self): | |
1099 | self.vm.shutdown() | |
1100 | ||
1101 | def test_no_job_id(self): | |
1102 | self.assert_no_active_block_jobs() | |
1103 | ||
1104 | result = self.vm.qmp('blockdev-mirror', device='src', sync='full', | |
1105 | target='dest') | |
1106 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1107 | ||
1108 | def test_success(self): | |
1109 | self.assert_no_active_block_jobs() | |
1110 | ||
1111 | result = self.vm.qmp('blockdev-mirror', job_id='job', device='src', | |
1112 | sync='full', target='dest') | |
1113 | self.assert_qmp(result, 'return', {}) | |
1114 | ||
1115 | self.complete_and_wait('job') | |
1116 | ||
1117 | def test_failing_permissions(self): | |
1118 | self.assert_no_active_block_jobs() | |
1119 | ||
1120 | result = self.vm.qmp('blockdev-mirror', device='src', sync='full', | |
1121 | target='dest-ro') | |
1122 | self.assert_qmp(result, 'error/class', 'GenericError') | |
1123 | ||
1124 | if __name__ == '__main__': | |
1125 | iotests.main(supported_fmts=['qcow2', 'qed'], | |
1126 | supported_protocols=['file']) |