qemu-iotests: Make block job methods common
[qemu/ar7.git] / tests / qemu-iotests / 041
blob3d46ed705bb9633ede66e67a585d31c40138ac40
1 #!/usr/bin/env python
3 # Tests for image mirroring.
5 # Copyright (C) 2012 Red Hat, Inc.
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.
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.
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/>.
21 import time
22 import os
23 import iotests
24 from iotests import qemu_img, qemu_io
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')
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')
38 class TestSingleDrive(iotests.QMPTestCase):
39 image_len = 1 * 1024 * 1024 # MB
41 def setUp(self):
42 iotests.create_image(backing_img, self.image_len)
43 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
44 self.vm = iotests.VM().add_drive(test_img)
45 self.vm.launch()
47 def tearDown(self):
48 self.vm.shutdown()
49 os.remove(test_img)
50 os.remove(backing_img)
51 try:
52 os.remove(target_img)
53 except OSError:
54 pass
56 def test_complete(self):
57 self.assert_no_active_block_jobs()
59 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
60 target=target_img)
61 self.assert_qmp(result, 'return', {})
63 self.complete_and_wait()
64 result = self.vm.qmp('query-block')
65 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
66 self.vm.shutdown()
67 self.assertTrue(iotests.compare_images(test_img, target_img),
68 'target image does not match source after mirroring')
70 def test_cancel(self):
71 self.assert_no_active_block_jobs()
73 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
74 target=target_img)
75 self.assert_qmp(result, 'return', {})
77 self.cancel_and_wait(force=True)
78 result = self.vm.qmp('query-block')
79 self.assert_qmp(result, 'return[0]/inserted/file', test_img)
80 self.vm.shutdown()
82 def test_cancel_after_ready(self):
83 self.assert_no_active_block_jobs()
85 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
86 target=target_img)
87 self.assert_qmp(result, 'return', {})
89 self.wait_ready_and_cancel()
90 result = self.vm.qmp('query-block')
91 self.assert_qmp(result, 'return[0]/inserted/file', test_img)
92 self.vm.shutdown()
93 self.assertTrue(iotests.compare_images(test_img, target_img),
94 'target image does not match source after mirroring')
96 def test_pause(self):
97 self.assert_no_active_block_jobs()
99 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
100 target=target_img)
101 self.assert_qmp(result, 'return', {})
103 result = self.vm.qmp('block-job-pause', device='drive0')
104 self.assert_qmp(result, 'return', {})
106 time.sleep(1)
107 result = self.vm.qmp('query-block-jobs')
108 offset = self.dictpath(result, 'return[0]/offset')
110 time.sleep(1)
111 result = self.vm.qmp('query-block-jobs')
112 self.assert_qmp(result, 'return[0]/offset', offset)
114 result = self.vm.qmp('block-job-resume', device='drive0')
115 self.assert_qmp(result, 'return', {})
117 self.complete_and_wait()
118 self.vm.shutdown()
119 self.assertTrue(iotests.compare_images(test_img, target_img),
120 'target image does not match source after mirroring')
122 def test_small_buffer(self):
123 self.assert_no_active_block_jobs()
125 # A small buffer is rounded up automatically
126 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
127 buf_size=4096, target=target_img)
128 self.assert_qmp(result, 'return', {})
130 self.complete_and_wait()
131 result = self.vm.qmp('query-block')
132 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
133 self.vm.shutdown()
134 self.assertTrue(iotests.compare_images(test_img, target_img),
135 'target image does not match source after mirroring')
137 def test_small_buffer2(self):
138 self.assert_no_active_block_jobs()
140 qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
141 % (self.image_len, self.image_len), target_img)
142 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
143 buf_size=65536, mode='existing', target=target_img)
144 self.assert_qmp(result, 'return', {})
146 self.complete_and_wait()
147 result = self.vm.qmp('query-block')
148 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
149 self.vm.shutdown()
150 self.assertTrue(iotests.compare_images(test_img, target_img),
151 'target image does not match source after mirroring')
153 def test_large_cluster(self):
154 self.assert_no_active_block_jobs()
156 qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
157 % (self.image_len, backing_img), target_img)
158 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
159 mode='existing', target=target_img)
160 self.assert_qmp(result, 'return', {})
162 self.complete_and_wait()
163 result = self.vm.qmp('query-block')
164 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
165 self.vm.shutdown()
166 self.assertTrue(iotests.compare_images(test_img, target_img),
167 'target image does not match source after mirroring')
169 def test_medium_not_found(self):
170 result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
171 target=target_img)
172 self.assert_qmp(result, 'error/class', 'GenericError')
174 def test_image_not_found(self):
175 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
176 mode='existing', target=target_img)
177 self.assert_qmp(result, 'error/class', 'GenericError')
179 def test_device_not_found(self):
180 result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
181 target=target_img)
182 self.assert_qmp(result, 'error/class', 'DeviceNotFound')
184 class TestSingleDriveZeroLength(TestSingleDrive):
185 image_len = 0
186 test_small_buffer2 = None
187 test_large_cluster = None
189 class TestSingleDriveUnalignedLength(TestSingleDrive):
190 image_len = 1025 * 1024
191 test_small_buffer2 = None
192 test_large_cluster = None
194 class TestMirrorNoBacking(iotests.QMPTestCase):
195 image_len = 2 * 1024 * 1024 # MB
197 def setUp(self):
198 iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
199 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
200 self.vm = iotests.VM().add_drive(test_img)
201 self.vm.launch()
203 def tearDown(self):
204 self.vm.shutdown()
205 os.remove(test_img)
206 os.remove(backing_img)
207 try:
208 os.remove(target_backing_img)
209 except:
210 pass
211 os.remove(target_img)
213 def test_complete(self):
214 self.assert_no_active_block_jobs()
216 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
217 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
218 mode='existing', target=target_img)
219 self.assert_qmp(result, 'return', {})
221 self.complete_and_wait()
222 result = self.vm.qmp('query-block')
223 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
224 self.vm.shutdown()
225 self.assertTrue(iotests.compare_images(test_img, target_img),
226 'target image does not match source after mirroring')
228 def test_cancel(self):
229 self.assert_no_active_block_jobs()
231 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
232 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
233 mode='existing', target=target_img)
234 self.assert_qmp(result, 'return', {})
236 self.wait_ready_and_cancel()
237 result = self.vm.qmp('query-block')
238 self.assert_qmp(result, 'return[0]/inserted/file', test_img)
239 self.vm.shutdown()
240 self.assertTrue(iotests.compare_images(test_img, target_img),
241 'target image does not match source after mirroring')
243 def test_large_cluster(self):
244 self.assert_no_active_block_jobs()
246 # qemu-img create fails if the image is not there
247 qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
248 %(TestMirrorNoBacking.image_len), target_backing_img)
249 qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
250 % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
252 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
253 mode='existing', target=target_img)
254 self.assert_qmp(result, 'return', {})
256 self.complete_and_wait()
257 result = self.vm.qmp('query-block')
258 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
259 self.vm.shutdown()
260 self.assertTrue(iotests.compare_images(test_img, target_img),
261 'target image does not match source after mirroring')
263 class TestMirrorResized(iotests.QMPTestCase):
264 backing_len = 1 * 1024 * 1024 # MB
265 image_len = 2 * 1024 * 1024 # MB
267 def setUp(self):
268 iotests.create_image(backing_img, TestMirrorResized.backing_len)
269 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
270 qemu_img('resize', test_img, '2M')
271 self.vm = iotests.VM().add_drive(test_img)
272 self.vm.launch()
274 def tearDown(self):
275 self.vm.shutdown()
276 os.remove(test_img)
277 os.remove(backing_img)
278 try:
279 os.remove(target_img)
280 except OSError:
281 pass
283 def test_complete_top(self):
284 self.assert_no_active_block_jobs()
286 result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
287 target=target_img)
288 self.assert_qmp(result, 'return', {})
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')
297 def test_complete_full(self):
298 self.assert_no_active_block_jobs()
300 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
301 target=target_img)
302 self.assert_qmp(result, 'return', {})
304 self.complete_and_wait()
305 result = self.vm.qmp('query-block')
306 self.assert_qmp(result, 'return[0]/inserted/file', target_img)
307 self.vm.shutdown()
308 self.assertTrue(iotests.compare_images(test_img, target_img),
309 'target image does not match source after mirroring')
311 class TestReadErrors(iotests.QMPTestCase):
312 image_len = 2 * 1024 * 1024 # MB
314 # this should be a multiple of twice the default granularity
315 # so that we hit this offset first in state 1
316 MIRROR_GRANULARITY = 1024 * 1024
318 def create_blkdebug_file(self, name, event, errno):
319 file = open(name, 'w')
320 file.write('''
321 [inject-error]
322 state = "1"
323 event = "%s"
324 errno = "%d"
325 immediately = "off"
326 once = "on"
327 sector = "%d"
329 [set-state]
330 state = "1"
331 event = "%s"
332 new_state = "2"
334 [set-state]
335 state = "2"
336 event = "%s"
337 new_state = "1"
338 ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
339 file.close()
341 def setUp(self):
342 self.blkdebug_file = backing_img + ".blkdebug"
343 iotests.create_image(backing_img, TestReadErrors.image_len)
344 self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
345 qemu_img('create', '-f', iotests.imgfmt,
346 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
347 % (self.blkdebug_file, backing_img),
348 test_img)
349 # Write something for tests that use sync='top'
350 qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
351 test_img)
352 self.vm = iotests.VM().add_drive(test_img)
353 self.vm.launch()
355 def tearDown(self):
356 self.vm.shutdown()
357 os.remove(test_img)
358 os.remove(backing_img)
359 os.remove(self.blkdebug_file)
361 def test_report_read(self):
362 self.assert_no_active_block_jobs()
364 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
365 target=target_img)
366 self.assert_qmp(result, 'return', {})
368 completed = False
369 error = False
370 while not completed:
371 for event in self.vm.get_qmp_events(wait=True):
372 if event['event'] == 'BLOCK_JOB_ERROR':
373 self.assert_qmp(event, 'data/device', 'drive0')
374 self.assert_qmp(event, 'data/operation', 'read')
375 error = True
376 elif event['event'] == 'BLOCK_JOB_READY':
377 self.assertTrue(False, 'job completed unexpectedly')
378 elif event['event'] == 'BLOCK_JOB_COMPLETED':
379 self.assertTrue(error, 'job completed unexpectedly')
380 self.assert_qmp(event, 'data/type', 'mirror')
381 self.assert_qmp(event, 'data/device', 'drive0')
382 self.assert_qmp(event, 'data/error', 'Input/output error')
383 completed = True
385 self.assert_no_active_block_jobs()
386 self.vm.shutdown()
388 def test_ignore_read(self):
389 self.assert_no_active_block_jobs()
391 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
392 target=target_img, on_source_error='ignore')
393 self.assert_qmp(result, 'return', {})
395 event = self.vm.get_qmp_event(wait=True)
396 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
397 self.assert_qmp(event, 'data/device', 'drive0')
398 self.assert_qmp(event, 'data/operation', 'read')
399 result = self.vm.qmp('query-block-jobs')
400 self.assert_qmp(result, 'return[0]/paused', False)
401 self.complete_and_wait()
402 self.vm.shutdown()
404 def test_large_cluster(self):
405 self.assert_no_active_block_jobs()
407 # Test COW into the target image. The first half of the
408 # cluster at MIRROR_GRANULARITY has to be copied from
409 # backing_img, even though sync='top'.
410 qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img)
411 result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
412 on_source_error='ignore',
413 mode='existing', target=target_img)
414 self.assert_qmp(result, 'return', {})
416 event = self.vm.get_qmp_event(wait=True)
417 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
418 self.assert_qmp(event, 'data/device', 'drive0')
419 self.assert_qmp(event, 'data/operation', 'read')
420 result = self.vm.qmp('query-block-jobs')
421 self.assert_qmp(result, 'return[0]/paused', False)
422 self.complete_and_wait()
423 self.vm.shutdown()
425 # Detach blkdebug to compare images successfully
426 qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
427 self.assertTrue(iotests.compare_images(test_img, target_img),
428 'target image does not match source after mirroring')
430 def test_stop_read(self):
431 self.assert_no_active_block_jobs()
433 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
434 target=target_img, on_source_error='stop')
435 self.assert_qmp(result, 'return', {})
437 error = False
438 ready = False
439 while not ready:
440 for event in self.vm.get_qmp_events(wait=True):
441 if event['event'] == 'BLOCK_JOB_ERROR':
442 self.assert_qmp(event, 'data/device', 'drive0')
443 self.assert_qmp(event, 'data/operation', 'read')
445 result = self.vm.qmp('query-block-jobs')
446 self.assert_qmp(result, 'return[0]/paused', True)
447 self.assert_qmp(result, 'return[0]/io-status', 'failed')
449 result = self.vm.qmp('block-job-resume', device='drive0')
450 self.assert_qmp(result, 'return', {})
451 error = True
452 elif event['event'] == 'BLOCK_JOB_READY':
453 self.assertTrue(error, 'job completed unexpectedly')
454 self.assert_qmp(event, 'data/device', 'drive0')
455 ready = True
457 result = self.vm.qmp('query-block-jobs')
458 self.assert_qmp(result, 'return[0]/paused', False)
459 self.assert_qmp(result, 'return[0]/io-status', 'ok')
461 self.complete_and_wait(wait_ready=False)
462 self.assert_no_active_block_jobs()
463 self.vm.shutdown()
465 class TestWriteErrors(iotests.QMPTestCase):
466 image_len = 2 * 1024 * 1024 # MB
468 # this should be a multiple of twice the default granularity
469 # so that we hit this offset first in state 1
470 MIRROR_GRANULARITY = 1024 * 1024
472 def create_blkdebug_file(self, name, event, errno):
473 file = open(name, 'w')
474 file.write('''
475 [inject-error]
476 state = "1"
477 event = "%s"
478 errno = "%d"
479 immediately = "off"
480 once = "on"
481 sector = "%d"
483 [set-state]
484 state = "1"
485 event = "%s"
486 new_state = "2"
488 [set-state]
489 state = "2"
490 event = "%s"
491 new_state = "1"
492 ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
493 file.close()
495 def setUp(self):
496 self.blkdebug_file = target_img + ".blkdebug"
497 iotests.create_image(backing_img, TestWriteErrors.image_len)
498 self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
499 qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
500 self.vm = iotests.VM().add_drive(test_img)
501 self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
502 qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
503 self.vm.launch()
505 def tearDown(self):
506 self.vm.shutdown()
507 os.remove(test_img)
508 os.remove(backing_img)
509 os.remove(self.blkdebug_file)
511 def test_report_write(self):
512 self.assert_no_active_block_jobs()
514 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
515 mode='existing', target=self.target_img)
516 self.assert_qmp(result, 'return', {})
518 completed = False
519 error = False
520 while not completed:
521 for event in self.vm.get_qmp_events(wait=True):
522 if event['event'] == 'BLOCK_JOB_ERROR':
523 self.assert_qmp(event, 'data/device', 'drive0')
524 self.assert_qmp(event, 'data/operation', 'write')
525 error = True
526 elif event['event'] == 'BLOCK_JOB_READY':
527 self.assertTrue(False, 'job completed unexpectedly')
528 elif event['event'] == 'BLOCK_JOB_COMPLETED':
529 self.assertTrue(error, 'job completed unexpectedly')
530 self.assert_qmp(event, 'data/type', 'mirror')
531 self.assert_qmp(event, 'data/device', 'drive0')
532 self.assert_qmp(event, 'data/error', 'Input/output error')
533 completed = True
535 self.assert_no_active_block_jobs()
536 self.vm.shutdown()
538 def test_ignore_write(self):
539 self.assert_no_active_block_jobs()
541 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
542 mode='existing', target=self.target_img,
543 on_target_error='ignore')
544 self.assert_qmp(result, 'return', {})
546 event = self.vm.get_qmp_event(wait=True)
547 self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
548 self.assert_qmp(event, 'data/device', 'drive0')
549 self.assert_qmp(event, 'data/operation', 'write')
550 result = self.vm.qmp('query-block-jobs')
551 self.assert_qmp(result, 'return[0]/paused', False)
552 self.complete_and_wait()
553 self.vm.shutdown()
555 def test_stop_write(self):
556 self.assert_no_active_block_jobs()
558 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
559 mode='existing', target=self.target_img,
560 on_target_error='stop')
561 self.assert_qmp(result, 'return', {})
563 error = False
564 ready = False
565 while not ready:
566 for event in self.vm.get_qmp_events(wait=True):
567 if event['event'] == 'BLOCK_JOB_ERROR':
568 self.assert_qmp(event, 'data/device', 'drive0')
569 self.assert_qmp(event, 'data/operation', 'write')
571 result = self.vm.qmp('query-block-jobs')
572 self.assert_qmp(result, 'return[0]/paused', True)
573 self.assert_qmp(result, 'return[0]/io-status', 'failed')
575 result = self.vm.qmp('block-job-resume', device='drive0')
576 self.assert_qmp(result, 'return', {})
578 result = self.vm.qmp('query-block-jobs')
579 self.assert_qmp(result, 'return[0]/paused', False)
580 self.assert_qmp(result, 'return[0]/io-status', 'ok')
581 error = True
582 elif event['event'] == 'BLOCK_JOB_READY':
583 self.assertTrue(error, 'job completed unexpectedly')
584 self.assert_qmp(event, 'data/device', 'drive0')
585 ready = True
587 self.complete_and_wait(wait_ready=False)
588 self.assert_no_active_block_jobs()
589 self.vm.shutdown()
591 class TestSetSpeed(iotests.QMPTestCase):
592 image_len = 80 * 1024 * 1024 # MB
594 def setUp(self):
595 qemu_img('create', backing_img, str(TestSetSpeed.image_len))
596 qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
597 self.vm = iotests.VM().add_drive(test_img)
598 self.vm.launch()
600 def tearDown(self):
601 self.vm.shutdown()
602 os.remove(test_img)
603 os.remove(backing_img)
604 os.remove(target_img)
606 def test_set_speed(self):
607 self.assert_no_active_block_jobs()
609 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
610 target=target_img)
611 self.assert_qmp(result, 'return', {})
613 # Default speed is 0
614 result = self.vm.qmp('query-block-jobs')
615 self.assert_qmp(result, 'return[0]/device', 'drive0')
616 self.assert_qmp(result, 'return[0]/speed', 0)
618 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
619 self.assert_qmp(result, 'return', {})
621 # Ensure the speed we set was accepted
622 result = self.vm.qmp('query-block-jobs')
623 self.assert_qmp(result, 'return[0]/device', 'drive0')
624 self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
626 self.wait_ready_and_cancel()
628 # Check setting speed in drive-mirror works
629 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
630 target=target_img, speed=4*1024*1024)
631 self.assert_qmp(result, 'return', {})
633 result = self.vm.qmp('query-block-jobs')
634 self.assert_qmp(result, 'return[0]/device', 'drive0')
635 self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
637 self.wait_ready_and_cancel()
639 def test_set_speed_invalid(self):
640 self.assert_no_active_block_jobs()
642 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
643 target=target_img, speed=-1)
644 self.assert_qmp(result, 'error/class', 'GenericError')
646 self.assert_no_active_block_jobs()
648 result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
649 target=target_img)
650 self.assert_qmp(result, 'return', {})
652 result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
653 self.assert_qmp(result, 'error/class', 'GenericError')
655 self.wait_ready_and_cancel()
657 class TestUnbackedSource(iotests.QMPTestCase):
658 image_len = 2 * 1024 * 1024 # MB
660 def setUp(self):
661 qemu_img('create', '-f', iotests.imgfmt, test_img,
662 str(TestUnbackedSource.image_len))
663 self.vm = iotests.VM().add_drive(test_img)
664 self.vm.launch()
666 def tearDown(self):
667 self.vm.shutdown()
668 os.remove(test_img)
669 os.remove(target_img)
671 def test_absolute_paths_full(self):
672 self.assert_no_active_block_jobs()
673 result = self.vm.qmp('drive-mirror', device='drive0',
674 sync='full', target=target_img,
675 mode='absolute-paths')
676 self.assert_qmp(result, 'return', {})
677 self.complete_and_wait()
678 self.assert_no_active_block_jobs()
680 def test_absolute_paths_top(self):
681 self.assert_no_active_block_jobs()
682 result = self.vm.qmp('drive-mirror', device='drive0',
683 sync='top', target=target_img,
684 mode='absolute-paths')
685 self.assert_qmp(result, 'return', {})
686 self.complete_and_wait()
687 self.assert_no_active_block_jobs()
689 def test_absolute_paths_none(self):
690 self.assert_no_active_block_jobs()
691 result = self.vm.qmp('drive-mirror', device='drive0',
692 sync='none', target=target_img,
693 mode='absolute-paths')
694 self.assert_qmp(result, 'return', {})
695 self.complete_and_wait()
696 self.assert_no_active_block_jobs()
698 class TestRepairQuorum(iotests.QMPTestCase):
699 """ This class test quorum file repair using drive-mirror.
700 It's mostly a fork of TestSingleDrive """
701 image_len = 1 * 1024 * 1024 # MB
702 IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
704 def has_quorum(self):
705 return 'quorum' in iotests.qemu_img_pipe('--help')
707 def setUp(self):
708 self.vm = iotests.VM()
710 # Add each individual quorum images
711 for i in self.IMAGES:
712 qemu_img('create', '-f', iotests.imgfmt, i,
713 str(TestSingleDrive.image_len))
714 # Assign a node name to each quorum image in order to manipulate
715 # them
716 opts = "node-name=img%i" % self.IMAGES.index(i)
717 self.vm = self.vm.add_drive(i, opts)
719 self.vm.launch()
721 #assemble the quorum block device from the individual files
722 args = { "options" : { "driver": "quorum", "id": "quorum0",
723 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
724 if self.has_quorum():
725 result = self.vm.qmp("blockdev-add", **args)
726 self.assert_qmp(result, 'return', {})
729 def tearDown(self):
730 self.vm.shutdown()
731 for i in self.IMAGES + [ quorum_repair_img ]:
732 # Do a try/except because the test may have deleted some images
733 try:
734 os.remove(i)
735 except OSError:
736 pass
738 def test_complete(self):
739 if not self.has_quorum():
740 return
742 self.assert_no_active_block_jobs()
744 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
745 node_name="repair0",
746 replaces="img1",
747 target=quorum_repair_img, format=iotests.imgfmt)
748 self.assert_qmp(result, 'return', {})
750 self.complete_and_wait(drive="quorum0")
751 result = self.vm.qmp('query-named-block-nodes')
752 self.assert_qmp(result, 'return[0]/file', quorum_repair_img)
753 # TODO: a better test requiring some QEMU infrastructure will be added
754 # to check that this file is really driven by quorum
755 self.vm.shutdown()
756 self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
757 'target image does not match source after mirroring')
759 def test_cancel(self):
760 if not self.has_quorum():
761 return
763 self.assert_no_active_block_jobs()
765 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
766 node_name="repair0",
767 replaces="img1",
768 target=quorum_repair_img, format=iotests.imgfmt)
769 self.assert_qmp(result, 'return', {})
771 self.cancel_and_wait(drive="quorum0", force=True)
772 # here we check that the last registered quorum file has not been
773 # swapped out and unref
774 result = self.vm.qmp('query-named-block-nodes')
775 self.assert_qmp(result, 'return[0]/file', quorum_img3)
776 self.vm.shutdown()
778 def test_cancel_after_ready(self):
779 if not self.has_quorum():
780 return
782 self.assert_no_active_block_jobs()
784 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
785 node_name="repair0",
786 replaces="img1",
787 target=quorum_repair_img, format=iotests.imgfmt)
788 self.assert_qmp(result, 'return', {})
790 self.wait_ready_and_cancel(drive="quorum0")
791 result = self.vm.qmp('query-named-block-nodes')
792 # here we check that the last registered quorum file has not been
793 # swapped out and unref
794 self.assert_qmp(result, 'return[0]/file', quorum_img3)
795 self.vm.shutdown()
796 self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
797 'target image does not match source after mirroring')
799 def test_pause(self):
800 if not self.has_quorum():
801 return
803 self.assert_no_active_block_jobs()
805 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
806 node_name="repair0",
807 replaces="img1",
808 target=quorum_repair_img, format=iotests.imgfmt)
809 self.assert_qmp(result, 'return', {})
811 result = self.vm.qmp('block-job-pause', device='quorum0')
812 self.assert_qmp(result, 'return', {})
814 time.sleep(1)
815 result = self.vm.qmp('query-block-jobs')
816 offset = self.dictpath(result, 'return[0]/offset')
818 time.sleep(1)
819 result = self.vm.qmp('query-block-jobs')
820 self.assert_qmp(result, 'return[0]/offset', offset)
822 result = self.vm.qmp('block-job-resume', device='quorum0')
823 self.assert_qmp(result, 'return', {})
825 self.complete_and_wait(drive="quorum0")
826 self.vm.shutdown()
827 self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
828 'target image does not match source after mirroring')
830 def test_medium_not_found(self):
831 if not self.has_quorum():
832 return
834 result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
835 node_name='repair0',
836 replaces='img1',
837 target=quorum_repair_img, format=iotests.imgfmt)
838 self.assert_qmp(result, 'error/class', 'GenericError')
840 def test_image_not_found(self):
841 if not self.has_quorum():
842 return
844 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
845 node_name='repair0',
846 replaces='img1',
847 mode='existing',
848 target=quorum_repair_img, format=iotests.imgfmt)
849 self.assert_qmp(result, 'error/class', 'GenericError')
851 def test_device_not_found(self):
852 if not self.has_quorum():
853 return
855 result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
856 node_name='repair0',
857 replaces='img1',
858 target=quorum_repair_img, format=iotests.imgfmt)
859 self.assert_qmp(result, 'error/class', 'DeviceNotFound')
861 def test_wrong_sync_mode(self):
862 if not self.has_quorum():
863 return
865 result = self.vm.qmp('drive-mirror', device='quorum0',
866 node_name='repair0',
867 replaces='img1',
868 target=quorum_repair_img, format=iotests.imgfmt)
869 self.assert_qmp(result, 'error/class', 'GenericError')
871 def test_no_node_name(self):
872 if not self.has_quorum():
873 return
875 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
876 replaces='img1',
877 target=quorum_repair_img, format=iotests.imgfmt)
878 self.assert_qmp(result, 'error/class', 'GenericError')
880 def test_unexistant_replaces(self):
881 if not self.has_quorum():
882 return
884 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
885 node_name='repair0',
886 replaces='img77',
887 target=quorum_repair_img, format=iotests.imgfmt)
888 self.assert_qmp(result, 'error/class', 'GenericError')
890 def test_after_a_quorum_snapshot(self):
891 if not self.has_quorum():
892 return
894 result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
895 snapshot_file=quorum_snapshot_file,
896 snapshot_node_name="snap1");
898 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
899 node_name='repair0',
900 replaces="img1",
901 target=quorum_repair_img, format=iotests.imgfmt)
902 self.assert_qmp(result, 'error/class', 'GenericError')
904 result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
905 node_name='repair0',
906 replaces="snap1",
907 target=quorum_repair_img, format=iotests.imgfmt)
908 self.assert_qmp(result, 'return', {})
910 self.complete_and_wait(drive="quorum0")
911 result = self.vm.qmp('query-named-block-nodes')
912 self.assert_qmp(result, 'return[0]/file', quorum_repair_img)
913 # TODO: a better test requiring some QEMU infrastructure will be added
914 # to check that this file is really driven by quorum
915 self.vm.shutdown()
917 if __name__ == '__main__':
918 iotests.main(supported_fmts=['qcow2', 'qed'])