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', event
['data']['len'])
57 def complete_and_wait(self
, drive
='drive0', wait_ready
=True):
58 '''Complete a block job and wait for it to finish'''
60 self
.wait_ready(drive
=drive
)
62 result
= self
.vm
.qmp('block-job-complete', device
=drive
)
63 self
.assert_qmp(result
, 'return', {})
65 event
= self
.wait_until_completed(drive
=drive
)
66 self
.assert_qmp(event
, 'data/type', 'mirror')
68 class TestSingleDrive(ImageMirroringTestCase
):
69 image_len
= 1 * 1024 * 1024 # MB
72 iotests
.create_image(backing_img
, self
.image_len
)
73 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
74 self
.vm
= iotests
.VM().add_drive(test_img
)
80 os
.remove(backing_img
)
86 def test_complete(self
):
87 self
.assert_no_active_block_jobs()
89 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
91 self
.assert_qmp(result
, 'return', {})
93 self
.complete_and_wait()
94 result
= self
.vm
.qmp('query-block')
95 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
97 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
98 'target image does not match source after mirroring')
100 def test_cancel(self
):
101 self
.assert_no_active_block_jobs()
103 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
105 self
.assert_qmp(result
, 'return', {})
107 self
.cancel_and_wait(force
=True)
108 result
= self
.vm
.qmp('query-block')
109 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
112 def test_cancel_after_ready(self
):
113 self
.assert_no_active_block_jobs()
115 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
117 self
.assert_qmp(result
, 'return', {})
119 self
.wait_ready_and_cancel()
120 result
= self
.vm
.qmp('query-block')
121 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
123 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
124 'target image does not match source after mirroring')
126 def test_pause(self
):
127 self
.assert_no_active_block_jobs()
129 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
131 self
.assert_qmp(result
, 'return', {})
133 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
134 self
.assert_qmp(result
, 'return', {})
137 result
= self
.vm
.qmp('query-block-jobs')
138 offset
= self
.dictpath(result
, 'return[0]/offset')
141 result
= self
.vm
.qmp('query-block-jobs')
142 self
.assert_qmp(result
, 'return[0]/offset', offset
)
144 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
145 self
.assert_qmp(result
, 'return', {})
147 self
.complete_and_wait()
149 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
150 'target image does not match source after mirroring')
152 def test_small_buffer(self
):
153 self
.assert_no_active_block_jobs()
155 # A small buffer is rounded up automatically
156 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
157 buf_size
=4096, target
=target_img
)
158 self
.assert_qmp(result
, 'return', {})
160 self
.complete_and_wait()
161 result
= self
.vm
.qmp('query-block')
162 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
164 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
165 'target image does not match source after mirroring')
167 def test_small_buffer2(self
):
168 self
.assert_no_active_block_jobs()
170 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,size=%d'
171 % (self
.image_len
, self
.image_len
), target_img
)
172 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
173 buf_size
=65536, mode
='existing', target
=target_img
)
174 self
.assert_qmp(result
, 'return', {})
176 self
.complete_and_wait()
177 result
= self
.vm
.qmp('query-block')
178 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
180 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
181 'target image does not match source after mirroring')
183 def test_large_cluster(self
):
184 self
.assert_no_active_block_jobs()
186 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
187 % (self
.image_len
, backing_img
), target_img
)
188 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
189 mode
='existing', target
=target_img
)
190 self
.assert_qmp(result
, 'return', {})
192 self
.complete_and_wait()
193 result
= self
.vm
.qmp('query-block')
194 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
196 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
197 'target image does not match source after mirroring')
199 def test_medium_not_found(self
):
200 result
= self
.vm
.qmp('drive-mirror', device
='ide1-cd0', sync
='full',
202 self
.assert_qmp(result
, 'error/class', 'GenericError')
204 def test_image_not_found(self
):
205 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
206 mode
='existing', target
=target_img
)
207 self
.assert_qmp(result
, 'error/class', 'GenericError')
209 def test_device_not_found(self
):
210 result
= self
.vm
.qmp('drive-mirror', device
='nonexistent', sync
='full',
212 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
214 class TestSingleDriveZeroLength(TestSingleDrive
):
216 test_small_buffer2
= None
217 test_large_cluster
= None
219 class TestSingleDriveUnalignedLength(TestSingleDrive
):
220 image_len
= 1025 * 1024
221 test_small_buffer2
= None
222 test_large_cluster
= None
224 class TestMirrorNoBacking(ImageMirroringTestCase
):
225 image_len
= 2 * 1024 * 1024 # MB
227 def complete_and_wait(self
, drive
='drive0', wait_ready
=True):
228 iotests
.create_image(target_backing_img
, TestMirrorNoBacking
.image_len
)
229 return ImageMirroringTestCase
.complete_and_wait(self
, drive
, wait_ready
)
231 def compare_images(self
, img1
, img2
):
232 iotests
.create_image(target_backing_img
, TestMirrorNoBacking
.image_len
)
233 return iotests
.compare_images(img1
, img2
)
236 iotests
.create_image(backing_img
, TestMirrorNoBacking
.image_len
)
237 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
238 self
.vm
= iotests
.VM().add_drive(test_img
)
244 os
.remove(backing_img
)
245 os
.remove(target_backing_img
)
246 os
.remove(target_img
)
248 def test_complete(self
):
249 self
.assert_no_active_block_jobs()
251 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % 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(self
.compare_images(test_img
, target_img
),
261 'target image does not match source after mirroring')
263 def test_cancel(self
):
264 self
.assert_no_active_block_jobs()
266 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
267 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
268 mode
='existing', target
=target_img
)
269 self
.assert_qmp(result
, 'return', {})
271 self
.wait_ready_and_cancel()
272 result
= self
.vm
.qmp('query-block')
273 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
275 self
.assertTrue(self
.compare_images(test_img
, target_img
),
276 'target image does not match source after mirroring')
278 def test_large_cluster(self
):
279 self
.assert_no_active_block_jobs()
281 # qemu-img create fails if the image is not there
282 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'size=%d'
283 %(TestMirrorNoBacking
.image_len
), target_backing_img
)
284 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
285 % (TestMirrorNoBacking
.image_len
, target_backing_img
), target_img
)
286 os
.remove(target_backing_img
)
288 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
289 mode
='existing', target
=target_img
)
290 self
.assert_qmp(result
, 'return', {})
292 self
.complete_and_wait()
293 result
= self
.vm
.qmp('query-block')
294 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
296 self
.assertTrue(self
.compare_images(test_img
, target_img
),
297 'target image does not match source after mirroring')
299 class TestMirrorResized(ImageMirroringTestCase
):
300 backing_len
= 1 * 1024 * 1024 # MB
301 image_len
= 2 * 1024 * 1024 # MB
304 iotests
.create_image(backing_img
, TestMirrorResized
.backing_len
)
305 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
306 qemu_img('resize', test_img
, '2M')
307 self
.vm
= iotests
.VM().add_drive(test_img
)
313 os
.remove(backing_img
)
315 os
.remove(target_img
)
319 def test_complete_top(self
):
320 self
.assert_no_active_block_jobs()
322 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
324 self
.assert_qmp(result
, 'return', {})
326 self
.complete_and_wait()
327 result
= self
.vm
.qmp('query-block')
328 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
330 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
331 'target image does not match source after mirroring')
333 def test_complete_full(self
):
334 self
.assert_no_active_block_jobs()
336 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
338 self
.assert_qmp(result
, 'return', {})
340 self
.complete_and_wait()
341 result
= self
.vm
.qmp('query-block')
342 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
344 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
345 'target image does not match source after mirroring')
347 class TestReadErrors(ImageMirroringTestCase
):
348 image_len
= 2 * 1024 * 1024 # MB
350 # this should be a multiple of twice the default granularity
351 # so that we hit this offset first in state 1
352 MIRROR_GRANULARITY
= 1024 * 1024
354 def create_blkdebug_file(self
, name
, event
, errno
):
355 file = open(name
, 'w')
374 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
378 self
.blkdebug_file
= backing_img
+ ".blkdebug"
379 iotests
.create_image(backing_img
, TestReadErrors
.image_len
)
380 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
381 qemu_img('create', '-f', iotests
.imgfmt
,
382 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
383 % (self
.blkdebug_file
, backing_img
),
385 # Write something for tests that use sync='top'
386 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
388 self
.vm
= iotests
.VM().add_drive(test_img
)
394 os
.remove(backing_img
)
395 os
.remove(self
.blkdebug_file
)
397 def test_report_read(self
):
398 self
.assert_no_active_block_jobs()
400 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
402 self
.assert_qmp(result
, 'return', {})
407 for event
in self
.vm
.get_qmp_events(wait
=True):
408 if event
['event'] == 'BLOCK_JOB_ERROR':
409 self
.assert_qmp(event
, 'data/device', 'drive0')
410 self
.assert_qmp(event
, 'data/operation', 'read')
412 elif event
['event'] == 'BLOCK_JOB_READY':
413 self
.assertTrue(False, 'job completed unexpectedly')
414 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
415 self
.assertTrue(error
, 'job completed unexpectedly')
416 self
.assert_qmp(event
, 'data/type', 'mirror')
417 self
.assert_qmp(event
, 'data/device', 'drive0')
418 self
.assert_qmp(event
, 'data/error', 'Input/output error')
421 self
.assert_no_active_block_jobs()
424 def test_ignore_read(self
):
425 self
.assert_no_active_block_jobs()
427 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
428 target
=target_img
, on_source_error
='ignore')
429 self
.assert_qmp(result
, 'return', {})
431 event
= self
.vm
.get_qmp_event(wait
=True)
432 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
433 self
.assert_qmp(event
, 'data/device', 'drive0')
434 self
.assert_qmp(event
, 'data/operation', 'read')
435 result
= self
.vm
.qmp('query-block-jobs')
436 self
.assert_qmp(result
, 'return[0]/paused', False)
437 self
.complete_and_wait()
440 def test_large_cluster(self
):
441 self
.assert_no_active_block_jobs()
443 # Test COW into the target image. The first half of the
444 # cluster at MIRROR_GRANULARITY has to be copied from
445 # backing_img, even though sync='top'.
446 qemu_img('create', '-f', iotests
.imgfmt
, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img
)
447 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
448 on_source_error
='ignore',
449 mode
='existing', target
=target_img
)
450 self
.assert_qmp(result
, 'return', {})
452 event
= self
.vm
.get_qmp_event(wait
=True)
453 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
454 self
.assert_qmp(event
, 'data/device', 'drive0')
455 self
.assert_qmp(event
, 'data/operation', 'read')
456 result
= self
.vm
.qmp('query-block-jobs')
457 self
.assert_qmp(result
, 'return[0]/paused', False)
458 self
.complete_and_wait()
461 # Detach blkdebug to compare images successfully
462 qemu_img('rebase', '-f', iotests
.imgfmt
, '-u', '-b', backing_img
, test_img
)
463 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
464 'target image does not match source after mirroring')
466 def test_stop_read(self
):
467 self
.assert_no_active_block_jobs()
469 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
470 target
=target_img
, on_source_error
='stop')
471 self
.assert_qmp(result
, 'return', {})
476 for event
in self
.vm
.get_qmp_events(wait
=True):
477 if event
['event'] == 'BLOCK_JOB_ERROR':
478 self
.assert_qmp(event
, 'data/device', 'drive0')
479 self
.assert_qmp(event
, 'data/operation', 'read')
481 result
= self
.vm
.qmp('query-block-jobs')
482 self
.assert_qmp(result
, 'return[0]/paused', True)
483 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
485 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
486 self
.assert_qmp(result
, 'return', {})
488 elif event
['event'] == 'BLOCK_JOB_READY':
489 self
.assertTrue(error
, 'job completed unexpectedly')
490 self
.assert_qmp(event
, 'data/device', 'drive0')
493 result
= self
.vm
.qmp('query-block-jobs')
494 self
.assert_qmp(result
, 'return[0]/paused', False)
495 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
497 self
.complete_and_wait(wait_ready
=False)
498 self
.assert_no_active_block_jobs()
501 class TestWriteErrors(ImageMirroringTestCase
):
502 image_len
= 2 * 1024 * 1024 # MB
504 # this should be a multiple of twice the default granularity
505 # so that we hit this offset first in state 1
506 MIRROR_GRANULARITY
= 1024 * 1024
508 def create_blkdebug_file(self
, name
, event
, errno
):
509 file = open(name
, 'w')
528 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
532 self
.blkdebug_file
= target_img
+ ".blkdebug"
533 iotests
.create_image(backing_img
, TestWriteErrors
.image_len
)
534 self
.create_blkdebug_file(self
.blkdebug_file
, "write_aio", 5)
535 qemu_img('create', '-f', iotests
.imgfmt
, '-obacking_file=%s' %(backing_img), test_img
)
536 self
.vm
= iotests
.VM().add_drive(test_img
)
537 self
.target_img
= 'blkdebug:%s:%s' % (self
.blkdebug_file
, target_img
)
538 qemu_img('create', '-f', iotests
.imgfmt
, '-osize=%d' %(TestWriteErrors
.image_len
), target_img
)
544 os
.remove(backing_img
)
545 os
.remove(self
.blkdebug_file
)
547 def test_report_write(self
):
548 self
.assert_no_active_block_jobs()
550 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
551 mode
='existing', target
=self
.target_img
)
552 self
.assert_qmp(result
, 'return', {})
557 for event
in self
.vm
.get_qmp_events(wait
=True):
558 if event
['event'] == 'BLOCK_JOB_ERROR':
559 self
.assert_qmp(event
, 'data/device', 'drive0')
560 self
.assert_qmp(event
, 'data/operation', 'write')
562 elif event
['event'] == 'BLOCK_JOB_READY':
563 self
.assertTrue(False, 'job completed unexpectedly')
564 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
565 self
.assertTrue(error
, 'job completed unexpectedly')
566 self
.assert_qmp(event
, 'data/type', 'mirror')
567 self
.assert_qmp(event
, 'data/device', 'drive0')
568 self
.assert_qmp(event
, 'data/error', 'Input/output error')
571 self
.assert_no_active_block_jobs()
574 def test_ignore_write(self
):
575 self
.assert_no_active_block_jobs()
577 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
578 mode
='existing', target
=self
.target_img
,
579 on_target_error
='ignore')
580 self
.assert_qmp(result
, 'return', {})
582 event
= self
.vm
.get_qmp_event(wait
=True)
583 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
584 self
.assert_qmp(event
, 'data/device', 'drive0')
585 self
.assert_qmp(event
, 'data/operation', 'write')
586 result
= self
.vm
.qmp('query-block-jobs')
587 self
.assert_qmp(result
, 'return[0]/paused', False)
588 self
.complete_and_wait()
591 def test_stop_write(self
):
592 self
.assert_no_active_block_jobs()
594 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
595 mode
='existing', target
=self
.target_img
,
596 on_target_error
='stop')
597 self
.assert_qmp(result
, 'return', {})
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')
607 result
= self
.vm
.qmp('query-block-jobs')
608 self
.assert_qmp(result
, 'return[0]/paused', True)
609 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
611 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
612 self
.assert_qmp(result
, 'return', {})
614 result
= self
.vm
.qmp('query-block-jobs')
615 self
.assert_qmp(result
, 'return[0]/paused', False)
616 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
618 elif event
['event'] == 'BLOCK_JOB_READY':
619 self
.assertTrue(error
, 'job completed unexpectedly')
620 self
.assert_qmp(event
, 'data/device', 'drive0')
623 self
.complete_and_wait(wait_ready
=False)
624 self
.assert_no_active_block_jobs()
627 class TestSetSpeed(ImageMirroringTestCase
):
628 image_len
= 80 * 1024 * 1024 # MB
631 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
632 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
633 self
.vm
= iotests
.VM().add_drive(test_img
)
639 os
.remove(backing_img
)
640 os
.remove(target_img
)
642 def test_set_speed(self
):
643 self
.assert_no_active_block_jobs()
645 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
647 self
.assert_qmp(result
, 'return', {})
650 result
= self
.vm
.qmp('query-block-jobs')
651 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
652 self
.assert_qmp(result
, 'return[0]/speed', 0)
654 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
655 self
.assert_qmp(result
, 'return', {})
657 # Ensure the speed we set was accepted
658 result
= self
.vm
.qmp('query-block-jobs')
659 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
660 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
662 self
.wait_ready_and_cancel()
664 # Check setting speed in drive-mirror works
665 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
666 target
=target_img
, speed
=4*1024*1024)
667 self
.assert_qmp(result
, 'return', {})
669 result
= self
.vm
.qmp('query-block-jobs')
670 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
671 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
673 self
.wait_ready_and_cancel()
675 def test_set_speed_invalid(self
):
676 self
.assert_no_active_block_jobs()
678 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
679 target
=target_img
, speed
=-1)
680 self
.assert_qmp(result
, 'error/class', 'GenericError')
682 self
.assert_no_active_block_jobs()
684 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
686 self
.assert_qmp(result
, 'return', {})
688 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
689 self
.assert_qmp(result
, 'error/class', 'GenericError')
691 self
.wait_ready_and_cancel()
693 class TestUnbackedSource(ImageMirroringTestCase
):
694 image_len
= 2 * 1024 * 1024 # MB
697 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
698 str(TestUnbackedSource
.image_len
))
699 self
.vm
= iotests
.VM().add_drive(test_img
)
705 os
.remove(target_img
)
707 def test_absolute_paths_full(self
):
708 self
.assert_no_active_block_jobs()
709 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
710 sync
='full', target
=target_img
,
711 mode
='absolute-paths')
712 self
.assert_qmp(result
, 'return', {})
713 self
.complete_and_wait()
714 self
.assert_no_active_block_jobs()
716 def test_absolute_paths_top(self
):
717 self
.assert_no_active_block_jobs()
718 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
719 sync
='top', target
=target_img
,
720 mode
='absolute-paths')
721 self
.assert_qmp(result
, 'return', {})
722 self
.complete_and_wait()
723 self
.assert_no_active_block_jobs()
725 def test_absolute_paths_none(self
):
726 self
.assert_no_active_block_jobs()
727 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
728 sync
='none', target
=target_img
,
729 mode
='absolute-paths')
730 self
.assert_qmp(result
, 'return', {})
731 self
.complete_and_wait()
732 self
.assert_no_active_block_jobs()
734 class TestRepairQuorum(ImageMirroringTestCase
):
735 """ This class test quorum file repair using drive-mirror.
736 It's mostly a fork of TestSingleDrive """
737 image_len
= 1 * 1024 * 1024 # MB
738 IMAGES
= [ quorum_img1
, quorum_img2
, quorum_img3
]
740 def has_quorum(self
):
741 return 'quorum' in iotests
.qemu_img_pipe('--help')
744 self
.vm
= iotests
.VM()
746 # Add each individual quorum images
747 for i
in self
.IMAGES
:
748 qemu_img('create', '-f', iotests
.imgfmt
, i
,
749 str(TestSingleDrive
.image_len
))
750 # Assign a node name to each quorum image in order to manipulate
752 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
753 self
.vm
= self
.vm
.add_drive(i
, opts
)
757 #assemble the quorum block device from the individual files
758 args
= { "options" : { "driver": "quorum", "id": "quorum0",
759 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
760 if self
.has_quorum():
761 result
= self
.vm
.qmp("blockdev-add", **args
)
762 self
.assert_qmp(result
, 'return', {})
767 for i
in self
.IMAGES
+ [ quorum_repair_img
]:
768 # Do a try/except because the test may have deleted some images
774 def test_complete(self
):
775 if not self
.has_quorum():
778 self
.assert_no_active_block_jobs()
780 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
783 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
784 self
.assert_qmp(result
, 'return', {})
786 self
.complete_and_wait(drive
="quorum0")
787 result
= self
.vm
.qmp('query-named-block-nodes')
788 self
.assert_qmp(result
, 'return[0]/file', quorum_repair_img
)
789 # TODO: a better test requiring some QEMU infrastructure will be added
790 # to check that this file is really driven by quorum
792 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
793 'target image does not match source after mirroring')
795 def test_cancel(self
):
796 if not self
.has_quorum():
799 self
.assert_no_active_block_jobs()
801 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
804 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
805 self
.assert_qmp(result
, 'return', {})
807 self
.cancel_and_wait(drive
="quorum0", force
=True)
808 # here we check that the last registered quorum file has not been
809 # swapped out and unref
810 result
= self
.vm
.qmp('query-named-block-nodes')
811 self
.assert_qmp(result
, 'return[0]/file', quorum_img3
)
814 def test_cancel_after_ready(self
):
815 if not self
.has_quorum():
818 self
.assert_no_active_block_jobs()
820 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
823 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
824 self
.assert_qmp(result
, 'return', {})
826 self
.wait_ready_and_cancel(drive
="quorum0")
827 result
= self
.vm
.qmp('query-named-block-nodes')
828 # here we check that the last registered quorum file has not been
829 # swapped out and unref
830 self
.assert_qmp(result
, 'return[0]/file', quorum_img3
)
832 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
833 'target image does not match source after mirroring')
835 def test_pause(self
):
836 if not self
.has_quorum():
839 self
.assert_no_active_block_jobs()
841 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
844 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
845 self
.assert_qmp(result
, 'return', {})
847 result
= self
.vm
.qmp('block-job-pause', device
='quorum0')
848 self
.assert_qmp(result
, 'return', {})
851 result
= self
.vm
.qmp('query-block-jobs')
852 offset
= self
.dictpath(result
, 'return[0]/offset')
855 result
= self
.vm
.qmp('query-block-jobs')
856 self
.assert_qmp(result
, 'return[0]/offset', offset
)
858 result
= self
.vm
.qmp('block-job-resume', device
='quorum0')
859 self
.assert_qmp(result
, 'return', {})
861 self
.complete_and_wait(drive
="quorum0")
863 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
864 'target image does not match source after mirroring')
866 def test_medium_not_found(self
):
867 if not self
.has_quorum():
870 result
= self
.vm
.qmp('drive-mirror', device
='ide1-cd0', sync
='full',
873 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
874 self
.assert_qmp(result
, 'error/class', 'GenericError')
876 def test_image_not_found(self
):
877 if not self
.has_quorum():
880 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
884 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
885 self
.assert_qmp(result
, 'error/class', 'GenericError')
887 def test_device_not_found(self
):
888 if not self
.has_quorum():
891 result
= self
.vm
.qmp('drive-mirror', device
='nonexistent', sync
='full',
894 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
895 self
.assert_qmp(result
, 'error/class', 'DeviceNotFound')
897 def test_wrong_sync_mode(self
):
898 if not self
.has_quorum():
901 result
= self
.vm
.qmp('drive-mirror', device
='quorum0',
904 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
905 self
.assert_qmp(result
, 'error/class', 'GenericError')
907 def test_no_node_name(self
):
908 if not self
.has_quorum():
911 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
913 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
914 self
.assert_qmp(result
, 'error/class', 'GenericError')
916 def test_unexistant_replaces(self
):
917 if not self
.has_quorum():
920 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
923 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
924 self
.assert_qmp(result
, 'error/class', 'GenericError')
926 def test_after_a_quorum_snapshot(self
):
927 if not self
.has_quorum():
930 result
= self
.vm
.qmp('blockdev-snapshot-sync', node_name
='img1',
931 snapshot_file
=quorum_snapshot_file
,
932 snapshot_node_name
="snap1");
934 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
937 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
938 self
.assert_qmp(result
, 'error/class', 'GenericError')
940 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', sync
='full',
943 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
944 self
.assert_qmp(result
, 'return', {})
946 self
.complete_and_wait(drive
="quorum0")
947 result
= self
.vm
.qmp('query-named-block-nodes')
948 self
.assert_qmp(result
, 'return[0]/file', quorum_repair_img
)
949 # TODO: a better test requiring some QEMU infrastructure will be added
950 # to check that this file is really driven by quorum
953 if __name__
== '__main__':
954 iotests
.main(supported_fmts
=['qcow2', 'qed'])