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')
37 class ImageMirroringTestCase(iotests
.QMPTestCase
):
38 '''Abstract base class for image mirroring test cases'''
40 def wait_ready(self
, drive
='drive0'):
41 '''Wait until a block job BLOCK_JOB_READY event'''
44 for event
in self
.vm
.get_qmp_events(wait
=True):
45 if event
['event'] == 'BLOCK_JOB_READY':
46 self
.assert_qmp(event
, 'data/type', 'mirror')
47 self
.assert_qmp(event
, 'data/device', drive
)
50 def wait_ready_and_cancel(self
, drive
='drive0'):
51 self
.wait_ready(drive
=drive
)
52 event
= self
.cancel_and_wait(drive
=drive
)
53 self
.assertEquals(event
['event'], 'BLOCK_JOB_COMPLETED')
54 self
.assert_qmp(event
, 'data/type', 'mirror')
55 self
.assert_qmp(event
, 'data/offset', self
.image_len
)
56 self
.assert_qmp(event
, 'data/len', self
.image_len
)
58 def complete_and_wait(self
, drive
='drive0', wait_ready
=True):
59 '''Complete a block job and wait for it to finish'''
61 self
.wait_ready(drive
=drive
)
63 result
= self
.vm
.qmp('block-job-complete', device
=drive
)
64 self
.assert_qmp(result
, 'return', {})
66 event
= self
.wait_until_completed(drive
=drive
)
67 self
.assert_qmp(event
, 'data/type', 'mirror')
69 class TestSingleDrive(ImageMirroringTestCase
):
70 image_len
= 1 * 1024 * 1024 # MB
73 iotests
.create_image(backing_img
, self
.image_len
)
74 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
75 self
.vm
= iotests
.VM().add_drive(test_img
)
81 os
.remove(backing_img
)
87 def test_complete(self
):
88 self
.assert_no_active_block_jobs()
90 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
92 self
.assert_qmp(result
, 'return', {})
94 self
.complete_and_wait()
95 result
= self
.vm
.qmp('query-block')
96 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
98 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
99 'target image does not match source after mirroring')
101 def test_cancel(self
):
102 self
.assert_no_active_block_jobs()
104 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
106 self
.assert_qmp(result
, 'return', {})
108 self
.cancel_and_wait(force
=True)
109 result
= self
.vm
.qmp('query-block')
110 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
113 def test_cancel_after_ready(self
):
114 self
.assert_no_active_block_jobs()
116 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
118 self
.assert_qmp(result
, 'return', {})
120 self
.wait_ready_and_cancel()
121 result
= self
.vm
.qmp('query-block')
122 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
124 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
125 'target image does not match source after mirroring')
127 def test_pause(self
):
128 self
.assert_no_active_block_jobs()
130 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
132 self
.assert_qmp(result
, 'return', {})
134 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
135 self
.assert_qmp(result
, 'return', {})
138 result
= self
.vm
.qmp('query-block-jobs')
139 offset
= self
.dictpath(result
, 'return[0]/offset')
142 result
= self
.vm
.qmp('query-block-jobs')
143 self
.assert_qmp(result
, 'return[0]/offset', offset
)
145 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
146 self
.assert_qmp(result
, 'return', {})
148 self
.complete_and_wait()
150 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
151 'target image does not match source after mirroring')
153 def test_small_buffer(self
):
154 self
.assert_no_active_block_jobs()
156 # A small buffer is rounded up automatically
157 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
158 buf_size
=4096, target
=target_img
)
159 self
.assert_qmp(result
, 'return', {})
161 self
.complete_and_wait()
162 result
= self
.vm
.qmp('query-block')
163 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
165 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
166 'target image does not match source after mirroring')
168 def test_small_buffer2(self
):
169 self
.assert_no_active_block_jobs()
171 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,size=%d'
172 % (self
.image_len
, self
.image_len
), target_img
)
173 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
174 buf_size
=65536, mode
='existing', target
=target_img
)
175 self
.assert_qmp(result
, 'return', {})
177 self
.complete_and_wait()
178 result
= self
.vm
.qmp('query-block')
179 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
181 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
182 'target image does not match source after mirroring')
184 def test_large_cluster(self
):
185 self
.assert_no_active_block_jobs()
187 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
188 % (self
.image_len
, backing_img
), target_img
)
189 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
190 mode
='existing', target
=target_img
)
191 self
.assert_qmp(result
, 'return', {})
193 self
.complete_and_wait()
194 result
= self
.vm
.qmp('query-block')
195 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
197 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
198 'target image does not match source after mirroring')
200 def test_medium_not_found(self
):
201 result
= self
.vm
.qmp('drive-mirror', device
='ide1-cd0', sync
='full',
203 self
.assert_qmp(result
, 'error/class', 'GenericError')
205 def test_image_not_found(self
):
206 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
207 mode
='existing', target
=target_img
)
208 self
.assert_qmp(result
, 'error/class', 'GenericError')
210 def test_device_not_found(self
):
211 result
= self
.vm
.qmp('drive-mirror', device
='nonexistent', sync
='full',
213 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
215 class TestSingleDriveZeroLength(TestSingleDrive
):
217 test_small_buffer2
= None
218 test_large_cluster
= None
220 class TestSingleDriveUnalignedLength(TestSingleDrive
):
221 image_len
= 1025 * 1024
222 test_small_buffer2
= None
223 test_large_cluster
= None
225 class TestMirrorNoBacking(ImageMirroringTestCase
):
226 image_len
= 2 * 1024 * 1024 # MB
228 def complete_and_wait(self
, drive
='drive0', wait_ready
=True):
229 iotests
.create_image(target_backing_img
, TestMirrorNoBacking
.image_len
)
230 return ImageMirroringTestCase
.complete_and_wait(self
, drive
, wait_ready
)
232 def compare_images(self
, img1
, img2
):
233 iotests
.create_image(target_backing_img
, TestMirrorNoBacking
.image_len
)
234 return iotests
.compare_images(img1
, img2
)
237 iotests
.create_image(backing_img
, TestMirrorNoBacking
.image_len
)
238 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
239 self
.vm
= iotests
.VM().add_drive(test_img
)
245 os
.remove(backing_img
)
246 os
.remove(target_backing_img
)
247 os
.remove(target_img
)
249 def test_complete(self
):
250 self
.assert_no_active_block_jobs()
252 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
253 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
254 mode
='existing', target
=target_img
)
255 self
.assert_qmp(result
, 'return', {})
257 self
.complete_and_wait()
258 result
= self
.vm
.qmp('query-block')
259 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
261 self
.assertTrue(self
.compare_images(test_img
, target_img
),
262 'target image does not match source after mirroring')
264 def test_cancel(self
):
265 self
.assert_no_active_block_jobs()
267 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
268 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
269 mode
='existing', target
=target_img
)
270 self
.assert_qmp(result
, 'return', {})
272 self
.wait_ready_and_cancel()
273 result
= self
.vm
.qmp('query-block')
274 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
276 self
.assertTrue(self
.compare_images(test_img
, target_img
),
277 'target image does not match source after mirroring')
279 def test_large_cluster(self
):
280 self
.assert_no_active_block_jobs()
282 # qemu-img create fails if the image is not there
283 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'size=%d'
284 %(TestMirrorNoBacking
.image_len
), target_backing_img
)
285 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
286 % (TestMirrorNoBacking
.image_len
, target_backing_img
), target_img
)
287 os
.remove(target_backing_img
)
289 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
290 mode
='existing', target
=target_img
)
291 self
.assert_qmp(result
, 'return', {})
293 self
.complete_and_wait()
294 result
= self
.vm
.qmp('query-block')
295 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
297 self
.assertTrue(self
.compare_images(test_img
, target_img
),
298 'target image does not match source after mirroring')
300 class TestMirrorResized(ImageMirroringTestCase
):
301 backing_len
= 1 * 1024 * 1024 # MB
302 image_len
= 2 * 1024 * 1024 # MB
305 iotests
.create_image(backing_img
, TestMirrorResized
.backing_len
)
306 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
307 qemu_img('resize', test_img
, '2M')
308 self
.vm
= iotests
.VM().add_drive(test_img
)
314 os
.remove(backing_img
)
316 os
.remove(target_img
)
320 def test_complete_top(self
):
321 self
.assert_no_active_block_jobs()
323 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
325 self
.assert_qmp(result
, 'return', {})
327 self
.complete_and_wait()
328 result
= self
.vm
.qmp('query-block')
329 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
331 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
332 'target image does not match source after mirroring')
334 def test_complete_full(self
):
335 self
.assert_no_active_block_jobs()
337 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
339 self
.assert_qmp(result
, 'return', {})
341 self
.complete_and_wait()
342 result
= self
.vm
.qmp('query-block')
343 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
345 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
346 'target image does not match source after mirroring')
348 class TestReadErrors(ImageMirroringTestCase
):
349 image_len
= 2 * 1024 * 1024 # MB
351 # this should be a multiple of twice the default granularity
352 # so that we hit this offset first in state 1
353 MIRROR_GRANULARITY
= 1024 * 1024
355 def create_blkdebug_file(self
, name
, event
, errno
):
356 file = open(name
, 'w')
375 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
379 self
.blkdebug_file
= backing_img
+ ".blkdebug"
380 iotests
.create_image(backing_img
, TestReadErrors
.image_len
)
381 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
382 qemu_img('create', '-f', iotests
.imgfmt
,
383 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
384 % (self
.blkdebug_file
, backing_img
),
386 # Write something for tests that use sync='top'
387 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
389 self
.vm
= iotests
.VM().add_drive(test_img
)
395 os
.remove(backing_img
)
396 os
.remove(self
.blkdebug_file
)
398 def test_report_read(self
):
399 self
.assert_no_active_block_jobs()
401 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
403 self
.assert_qmp(result
, 'return', {})
408 for event
in self
.vm
.get_qmp_events(wait
=True):
409 if event
['event'] == 'BLOCK_JOB_ERROR':
410 self
.assert_qmp(event
, 'data/device', 'drive0')
411 self
.assert_qmp(event
, 'data/operation', 'read')
413 elif event
['event'] == 'BLOCK_JOB_READY':
414 self
.assertTrue(False, 'job completed unexpectedly')
415 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
416 self
.assertTrue(error
, 'job completed unexpectedly')
417 self
.assert_qmp(event
, 'data/type', 'mirror')
418 self
.assert_qmp(event
, 'data/device', 'drive0')
419 self
.assert_qmp(event
, 'data/error', 'Input/output error')
420 self
.assert_qmp(event
, 'data/len', self
.image_len
)
423 self
.assert_no_active_block_jobs()
426 def test_ignore_read(self
):
427 self
.assert_no_active_block_jobs()
429 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
430 target
=target_img
, on_source_error
='ignore')
431 self
.assert_qmp(result
, 'return', {})
433 event
= self
.vm
.get_qmp_event(wait
=True)
434 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
435 self
.assert_qmp(event
, 'data/device', 'drive0')
436 self
.assert_qmp(event
, 'data/operation', 'read')
437 result
= self
.vm
.qmp('query-block-jobs')
438 self
.assert_qmp(result
, 'return[0]/paused', False)
439 self
.complete_and_wait()
442 def test_large_cluster(self
):
443 self
.assert_no_active_block_jobs()
445 # Test COW into the target image. The first half of the
446 # cluster at MIRROR_GRANULARITY has to be copied from
447 # backing_img, even though sync='top'.
448 qemu_img('create', '-f', iotests
.imgfmt
, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img
)
449 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
450 on_source_error
='ignore',
451 mode
='existing', target
=target_img
)
452 self
.assert_qmp(result
, 'return', {})
454 event
= self
.vm
.get_qmp_event(wait
=True)
455 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
456 self
.assert_qmp(event
, 'data/device', 'drive0')
457 self
.assert_qmp(event
, 'data/operation', 'read')
458 result
= self
.vm
.qmp('query-block-jobs')
459 self
.assert_qmp(result
, 'return[0]/paused', False)
460 self
.complete_and_wait()
463 # Detach blkdebug to compare images successfully
464 qemu_img('rebase', '-f', iotests
.imgfmt
, '-u', '-b', backing_img
, test_img
)
465 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
466 'target image does not match source after mirroring')
468 def test_stop_read(self
):
469 self
.assert_no_active_block_jobs()
471 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
472 target
=target_img
, on_source_error
='stop')
473 self
.assert_qmp(result
, 'return', {})
478 for event
in self
.vm
.get_qmp_events(wait
=True):
479 if event
['event'] == 'BLOCK_JOB_ERROR':
480 self
.assert_qmp(event
, 'data/device', 'drive0')
481 self
.assert_qmp(event
, 'data/operation', 'read')
483 result
= self
.vm
.qmp('query-block-jobs')
484 self
.assert_qmp(result
, 'return[0]/paused', True)
485 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
487 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
488 self
.assert_qmp(result
, 'return', {})
490 elif event
['event'] == 'BLOCK_JOB_READY':
491 self
.assertTrue(error
, 'job completed unexpectedly')
492 self
.assert_qmp(event
, 'data/device', 'drive0')
495 result
= self
.vm
.qmp('query-block-jobs')
496 self
.assert_qmp(result
, 'return[0]/paused', False)
497 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
499 self
.complete_and_wait(wait_ready
=False)
500 self
.assert_no_active_block_jobs()
503 class TestWriteErrors(ImageMirroringTestCase
):
504 image_len
= 2 * 1024 * 1024 # MB
506 # this should be a multiple of twice the default granularity
507 # so that we hit this offset first in state 1
508 MIRROR_GRANULARITY
= 1024 * 1024
510 def create_blkdebug_file(self
, name
, event
, errno
):
511 file = open(name
, 'w')
530 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
534 self
.blkdebug_file
= target_img
+ ".blkdebug"
535 iotests
.create_image(backing_img
, TestWriteErrors
.image_len
)
536 self
.create_blkdebug_file(self
.blkdebug_file
, "write_aio", 5)
537 qemu_img('create', '-f', iotests
.imgfmt
, '-obacking_file=%s' %(backing_img), test_img
)
538 self
.vm
= iotests
.VM().add_drive(test_img
)
539 self
.target_img
= 'blkdebug:%s:%s' % (self
.blkdebug_file
, target_img
)
540 qemu_img('create', '-f', iotests
.imgfmt
, '-osize=%d' %(TestWriteErrors
.image_len
), target_img
)
546 os
.remove(backing_img
)
547 os
.remove(self
.blkdebug_file
)
549 def test_report_write(self
):
550 self
.assert_no_active_block_jobs()
552 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
553 mode
='existing', target
=self
.target_img
)
554 self
.assert_qmp(result
, 'return', {})
559 for event
in self
.vm
.get_qmp_events(wait
=True):
560 if event
['event'] == 'BLOCK_JOB_ERROR':
561 self
.assert_qmp(event
, 'data/device', 'drive0')
562 self
.assert_qmp(event
, 'data/operation', 'write')
564 elif event
['event'] == 'BLOCK_JOB_READY':
565 self
.assertTrue(False, 'job completed unexpectedly')
566 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
567 self
.assertTrue(error
, 'job completed unexpectedly')
568 self
.assert_qmp(event
, 'data/type', 'mirror')
569 self
.assert_qmp(event
, 'data/device', 'drive0')
570 self
.assert_qmp(event
, 'data/error', 'Input/output error')
571 self
.assert_qmp(event
, 'data/len', self
.image_len
)
574 self
.assert_no_active_block_jobs()
577 def test_ignore_write(self
):
578 self
.assert_no_active_block_jobs()
580 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
581 mode
='existing', target
=self
.target_img
,
582 on_target_error
='ignore')
583 self
.assert_qmp(result
, 'return', {})
585 event
= self
.vm
.get_qmp_event(wait
=True)
586 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
587 self
.assert_qmp(event
, 'data/device', 'drive0')
588 self
.assert_qmp(event
, 'data/operation', 'write')
589 result
= self
.vm
.qmp('query-block-jobs')
590 self
.assert_qmp(result
, 'return[0]/paused', False)
591 self
.complete_and_wait()
594 def test_stop_write(self
):
595 self
.assert_no_active_block_jobs()
597 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
598 mode
='existing', target
=self
.target_img
,
599 on_target_error
='stop')
600 self
.assert_qmp(result
, 'return', {})
605 for event
in self
.vm
.get_qmp_events(wait
=True):
606 if event
['event'] == 'BLOCK_JOB_ERROR':
607 self
.assert_qmp(event
, 'data/device', 'drive0')
608 self
.assert_qmp(event
, 'data/operation', 'write')
610 result
= self
.vm
.qmp('query-block-jobs')
611 self
.assert_qmp(result
, 'return[0]/paused', True)
612 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
614 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
615 self
.assert_qmp(result
, 'return', {})
617 result
= self
.vm
.qmp('query-block-jobs')
618 self
.assert_qmp(result
, 'return[0]/paused', False)
619 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
621 elif event
['event'] == 'BLOCK_JOB_READY':
622 self
.assertTrue(error
, 'job completed unexpectedly')
623 self
.assert_qmp(event
, 'data/device', 'drive0')
626 self
.complete_and_wait(wait_ready
=False)
627 self
.assert_no_active_block_jobs()
630 class TestSetSpeed(ImageMirroringTestCase
):
631 image_len
= 80 * 1024 * 1024 # MB
634 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
635 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
636 self
.vm
= iotests
.VM().add_drive(test_img
)
642 os
.remove(backing_img
)
643 os
.remove(target_img
)
645 def test_set_speed(self
):
646 self
.assert_no_active_block_jobs()
648 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
650 self
.assert_qmp(result
, 'return', {})
653 result
= self
.vm
.qmp('query-block-jobs')
654 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
655 self
.assert_qmp(result
, 'return[0]/speed', 0)
657 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
658 self
.assert_qmp(result
, 'return', {})
660 # Ensure the speed we set was accepted
661 result
= self
.vm
.qmp('query-block-jobs')
662 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
663 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
665 self
.wait_ready_and_cancel()
667 # Check setting speed in drive-mirror works
668 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
669 target
=target_img
, speed
=4*1024*1024)
670 self
.assert_qmp(result
, 'return', {})
672 result
= self
.vm
.qmp('query-block-jobs')
673 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
674 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
676 self
.wait_ready_and_cancel()
678 def test_set_speed_invalid(self
):
679 self
.assert_no_active_block_jobs()
681 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
682 target
=target_img
, speed
=-1)
683 self
.assert_qmp(result
, 'error/class', 'GenericError')
685 self
.assert_no_active_block_jobs()
687 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
689 self
.assert_qmp(result
, 'return', {})
691 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
692 self
.assert_qmp(result
, 'error/class', 'GenericError')
694 self
.wait_ready_and_cancel()
696 class TestUnbackedSource(ImageMirroringTestCase
):
697 image_len
= 2 * 1024 * 1024 # MB
700 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
701 str(TestUnbackedSource
.image_len
))
702 self
.vm
= iotests
.VM().add_drive(test_img
)
708 os
.remove(target_img
)
710 def test_absolute_paths_full(self
):
711 self
.assert_no_active_block_jobs()
712 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
713 sync
='full', target
=target_img
,
714 mode
='absolute-paths')
715 self
.assert_qmp(result
, 'return', {})
716 self
.complete_and_wait()
717 self
.assert_no_active_block_jobs()
719 def test_absolute_paths_top(self
):
720 self
.assert_no_active_block_jobs()
721 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
722 sync
='top', target
=target_img
,
723 mode
='absolute-paths')
724 self
.assert_qmp(result
, 'return', {})
725 self
.complete_and_wait()
726 self
.assert_no_active_block_jobs()
728 def test_absolute_paths_none(self
):
729 self
.assert_no_active_block_jobs()
730 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
731 sync
='none', target
=target_img
,
732 mode
='absolute-paths')
733 self
.assert_qmp(result
, 'return', {})
734 self
.complete_and_wait()
735 self
.assert_no_active_block_jobs()
737 class TestRepairQuorum(ImageMirroringTestCase
):
738 """ This class test quorum file repair using drive-mirror.
739 It's mostly a fork of TestSingleDrive """
740 image_len
= 1 * 1024 * 1024 # MB
741 IMAGES
= [ quorum_img1
, quorum_img2
, quorum_img3
]
743 def has_quorum(self
):
744 return 'quorum' in iotests
.qemu_img_pipe('--help')
747 self
.vm
= iotests
.VM()
749 # Add each individual quorum images
750 for i
in self
.IMAGES
:
751 qemu_img('create', '-f', iotests
.imgfmt
, i
,
752 str(TestSingleDrive
.image_len
))
753 # Assign a node name to each quorum image in order to manipulate
755 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
756 self
.vm
= self
.vm
.add_drive(i
, opts
)
760 #assemble the quorum block device from the individual files
761 args
= { "options" : { "driver": "quorum", "id": "quorum0",
762 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
763 if self
.has_quorum():
764 result
= self
.vm
.qmp("blockdev-add", **args
)
765 self
.assert_qmp(result
, 'return', {})
770 for i
in self
.IMAGES
+ [ quorum_repair_img
]:
771 # Do a try/except because the test may have deleted some images
777 def test_complete(self
):
778 if not self
.has_quorum():
781 self
.assert_no_active_block_jobs()
783 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
786 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
787 self
.assert_qmp(result
, 'return', {})
789 self
.complete_and_wait(drive
="quorum0")
790 result
= self
.vm
.qmp('query-named-block-nodes')
791 self
.assert_qmp(result
, 'return[0]/file', quorum_repair_img
)
792 # TODO: a better test requiring some QEMU infrastructure will be added
793 # to check that this file is really driven by quorum
795 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
796 'target image does not match source after mirroring')
798 def test_cancel(self
):
799 if not self
.has_quorum():
802 self
.assert_no_active_block_jobs()
804 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
807 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
808 self
.assert_qmp(result
, 'return', {})
810 self
.cancel_and_wait(drive
="quorum0", force
=True)
811 # here we check that the last registered quorum file has not been
812 # swapped out and unref
813 result
= self
.vm
.qmp('query-named-block-nodes')
814 self
.assert_qmp(result
, 'return[0]/file', quorum_img3
)
817 def test_cancel_after_ready(self
):
818 if not self
.has_quorum():
821 self
.assert_no_active_block_jobs()
823 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
826 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
827 self
.assert_qmp(result
, 'return', {})
829 self
.wait_ready_and_cancel(drive
="quorum0")
830 result
= self
.vm
.qmp('query-named-block-nodes')
831 # here we check that the last registered quorum file has not been
832 # swapped out and unref
833 self
.assert_qmp(result
, 'return[0]/file', quorum_img3
)
835 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
836 'target image does not match source after mirroring')
838 def test_pause(self
):
839 if not self
.has_quorum():
842 self
.assert_no_active_block_jobs()
844 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
847 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
848 self
.assert_qmp(result
, 'return', {})
850 result
= self
.vm
.qmp('block-job-pause', device
='quorum0')
851 self
.assert_qmp(result
, 'return', {})
854 result
= self
.vm
.qmp('query-block-jobs')
855 offset
= self
.dictpath(result
, 'return[0]/offset')
858 result
= self
.vm
.qmp('query-block-jobs')
859 self
.assert_qmp(result
, 'return[0]/offset', offset
)
861 result
= self
.vm
.qmp('block-job-resume', device
='quorum0')
862 self
.assert_qmp(result
, 'return', {})
864 self
.complete_and_wait(drive
="quorum0")
866 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
867 'target image does not match source after mirroring')
869 def test_medium_not_found(self
):
870 if not self
.has_quorum():
873 result
= self
.vm
.qmp('drive-mirror', device
='ide1-cd0', sync
='full',
876 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
877 self
.assert_qmp(result
, 'error/class', 'GenericError')
879 def test_image_not_found(self
):
880 if not self
.has_quorum():
883 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_device_not_found(self
):
891 if not self
.has_quorum():
894 result
= self
.vm
.qmp('drive-mirror', device
='nonexistent', sync
='full',
897 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
898 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
900 def test_wrong_sync_mode(self
):
901 if not self
.has_quorum():
904 result
= self
.vm
.qmp('drive-mirror', device
='quorum0',
907 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
908 self
.assert_qmp(result
, 'error/class', 'GenericError')
910 def test_no_node_name(self
):
911 if not self
.has_quorum():
914 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
916 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
917 self
.assert_qmp(result
, 'error/class', 'GenericError')
919 def test_unexistant_replaces(self
):
920 if not self
.has_quorum():
923 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
926 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
927 self
.assert_qmp(result
, 'error/class', 'GenericError')
929 def test_after_a_quorum_snapshot(self
):
930 if not self
.has_quorum():
933 result
= self
.vm
.qmp('blockdev-snapshot-sync', node_name
='img1',
934 snapshot_file
=quorum_snapshot_file
,
935 snapshot_node_name
="snap1");
937 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
940 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
941 self
.assert_qmp(result
, 'error/class', 'GenericError')
943 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
946 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
947 self
.assert_qmp(result
, 'return', {})
949 self
.complete_and_wait(drive
="quorum0")
950 result
= self
.vm
.qmp('query-named-block-nodes')
951 self
.assert_qmp(result
, 'return[0]/file', quorum_repair_img
)
952 # TODO: a better test requiring some QEMU infrastructure will be added
953 # to check that this file is really driven by quorum
956 if __name__
== '__main__':
957 iotests
.main(supported_fmts
=['qcow2', 'qed'])