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/>.
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
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
)
50 os
.remove(backing_img
)
56 def test_complete(self
):
57 self
.assert_no_active_block_jobs()
59 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
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
)
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',
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
)
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',
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
)
93 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
94 'target image does not match source after mirroring')
97 self
.assert_no_active_block_jobs()
99 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
101 self
.assert_qmp(result
, 'return', {})
103 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
104 self
.assert_qmp(result
, 'return', {})
107 result
= self
.vm
.qmp('query-block-jobs')
108 offset
= self
.dictpath(result
, 'return[0]/offset')
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()
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
)
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
)
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
)
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',
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',
182 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
184 class TestSingleDriveZeroLength(TestSingleDrive
):
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
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
)
206 os
.remove(backing_img
)
208 os
.remove(target_backing_img
)
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
)
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
)
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
)
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
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
)
277 os
.remove(backing_img
)
279 os
.remove(target_img
)
283 def test_complete_top(self
):
284 self
.assert_no_active_block_jobs()
286 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
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
)
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',
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
)
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')
338 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
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
),
349 # Write something for tests that use sync='top'
350 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
352 self
.vm
= iotests
.VM().add_drive(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',
366 self
.assert_qmp(result
, 'return', {})
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')
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')
385 self
.assert_no_active_block_jobs()
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()
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()
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', {})
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', {})
452 elif event
['event'] == 'BLOCK_JOB_READY':
453 self
.assertTrue(error
, 'job completed unexpectedly')
454 self
.assert_qmp(event
, 'data/device', 'drive0')
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()
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')
492 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
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
)
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', {})
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')
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')
535 self
.assert_no_active_block_jobs()
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()
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', {})
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')
582 elif event
['event'] == 'BLOCK_JOB_READY':
583 self
.assertTrue(error
, 'job completed unexpectedly')
584 self
.assert_qmp(event
, 'data/device', 'drive0')
587 self
.complete_and_wait(wait_ready
=False)
588 self
.assert_no_active_block_jobs()
591 class TestSetSpeed(iotests
.QMPTestCase
):
592 image_len
= 80 * 1024 * 1024 # MB
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
)
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',
611 self
.assert_qmp(result
, 'return', {})
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',
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
661 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
662 str(TestUnbackedSource
.image_len
))
663 self
.vm
= iotests
.VM().add_drive(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')
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
716 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
717 self
.vm
= self
.vm
.add_drive(i
, opts
)
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', {})
731 for i
in self
.IMAGES
+ [ quorum_repair_img
]:
732 # Do a try/except because the test may have deleted some images
738 def test_complete(self
):
739 if not self
.has_quorum():
742 self
.assert_no_active_block_jobs()
744 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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
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():
763 self
.assert_no_active_block_jobs()
765 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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
)
778 def test_cancel_after_ready(self
):
779 if not self
.has_quorum():
782 self
.assert_no_active_block_jobs()
784 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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
)
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():
803 self
.assert_no_active_block_jobs()
805 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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', {})
815 result
= self
.vm
.qmp('query-block-jobs')
816 offset
= self
.dictpath(result
, 'return[0]/offset')
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")
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():
834 result
= self
.vm
.qmp('drive-mirror', device
='ide1-cd0', sync
='full',
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():
844 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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():
855 result
= self
.vm
.qmp('drive-mirror', device
='nonexistent', sync
='full',
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():
865 result
= self
.vm
.qmp('drive-mirror', device
='quorum0',
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():
875 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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():
884 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
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():
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',
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',
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
917 if __name__
== '__main__':
918 iotests
.main(supported_fmts
=['qcow2', 'qed'])