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 TestSingleDrive(iotests
.QMPTestCase
):
38 image_len
= 1 * 1024 * 1024 # MB
39 qmp_cmd
= 'drive-mirror'
40 qmp_target
= target_img
43 iotests
.create_image(backing_img
, self
.image_len
)
44 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
45 self
.vm
= iotests
.VM().add_drive(test_img
)
46 if iotests
.qemu_default_machine
== 'pc':
47 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
53 os
.remove(backing_img
)
59 def test_complete(self
):
60 self
.assert_no_active_block_jobs()
62 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
63 target
=self
.qmp_target
)
64 self
.assert_qmp(result
, 'return', {})
66 self
.complete_and_wait()
67 result
= self
.vm
.qmp('query-block')
68 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
70 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
71 'target image does not match source after mirroring')
73 def test_cancel(self
):
74 self
.assert_no_active_block_jobs()
76 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
77 target
=self
.qmp_target
)
78 self
.assert_qmp(result
, 'return', {})
80 self
.cancel_and_wait(force
=True)
81 result
= self
.vm
.qmp('query-block')
82 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
85 def test_cancel_after_ready(self
):
86 self
.assert_no_active_block_jobs()
88 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
89 target
=self
.qmp_target
)
90 self
.assert_qmp(result
, 'return', {})
92 self
.wait_ready_and_cancel()
93 result
= self
.vm
.qmp('query-block')
94 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
96 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
97 'target image does not match source after mirroring')
100 self
.assert_no_active_block_jobs()
102 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
103 target
=self
.qmp_target
)
104 self
.assert_qmp(result
, 'return', {})
106 result
= self
.vm
.qmp('block-job-pause', device
='drive0')
107 self
.assert_qmp(result
, 'return', {})
110 result
= self
.vm
.qmp('query-block-jobs')
111 offset
= self
.dictpath(result
, 'return[0]/offset')
114 result
= self
.vm
.qmp('query-block-jobs')
115 self
.assert_qmp(result
, 'return[0]/offset', offset
)
117 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
118 self
.assert_qmp(result
, 'return', {})
120 self
.complete_and_wait()
122 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
123 'target image does not match source after mirroring')
125 def test_small_buffer(self
):
126 self
.assert_no_active_block_jobs()
128 # A small buffer is rounded up automatically
129 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
130 buf_size
=4096, target
=self
.qmp_target
)
131 self
.assert_qmp(result
, 'return', {})
133 self
.complete_and_wait()
134 result
= self
.vm
.qmp('query-block')
135 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
137 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
138 'target image does not match source after mirroring')
140 def test_small_buffer2(self
):
141 self
.assert_no_active_block_jobs()
143 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,size=%d'
144 % (self
.image_len
, self
.image_len
), target_img
)
145 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
146 buf_size
=65536, mode
='existing', target
=self
.qmp_target
)
147 self
.assert_qmp(result
, 'return', {})
149 self
.complete_and_wait()
150 result
= self
.vm
.qmp('query-block')
151 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
153 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
154 'target image does not match source after mirroring')
156 def test_large_cluster(self
):
157 self
.assert_no_active_block_jobs()
159 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
160 % (self
.image_len
, backing_img
), target_img
)
161 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
162 mode
='existing', target
=self
.qmp_target
)
163 self
.assert_qmp(result
, 'return', {})
165 self
.complete_and_wait()
166 result
= self
.vm
.qmp('query-block')
167 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
169 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
170 'target image does not match source after mirroring')
172 def test_medium_not_found(self
):
173 if iotests
.qemu_default_machine
!= 'pc':
176 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='ide1-cd0', sync
='full',
177 target
=self
.qmp_target
)
178 self
.assert_qmp(result
, 'error/class', 'GenericError')
180 def test_image_not_found(self
):
181 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
182 mode
='existing', target
=self
.qmp_target
)
183 self
.assert_qmp(result
, 'error/class', 'GenericError')
185 def test_device_not_found(self
):
186 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='nonexistent', sync
='full',
187 target
=self
.qmp_target
)
188 self
.assert_qmp(result
, 'error/class', 'GenericError')
190 class TestSingleBlockdev(TestSingleDrive
):
191 qmp_cmd
= 'blockdev-mirror'
195 TestSingleDrive
.setUp(self
)
196 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
198 {'driver': iotests
.imgfmt
,
199 'node-name': self
.qmp_target
,
200 'file': { 'filename': target_img
, 'driver': 'file' } } }
201 result
= self
.vm
.qmp("blockdev-add", **args
)
202 self
.assert_qmp(result
, 'return', {})
204 test_large_cluster
= None
205 test_image_not_found
= None
206 test_small_buffer2
= None
208 class TestSingleDriveZeroLength(TestSingleDrive
):
210 test_small_buffer2
= None
211 test_large_cluster
= None
213 class TestSingleBlockdevZeroLength(TestSingleBlockdev
):
216 class TestSingleDriveUnalignedLength(TestSingleDrive
):
217 image_len
= 1025 * 1024
218 test_small_buffer2
= None
219 test_large_cluster
= None
221 class TestSingleBlockdevUnalignedLength(TestSingleBlockdev
):
222 image_len
= 1025 * 1024
224 class TestMirrorNoBacking(iotests
.QMPTestCase
):
225 image_len
= 2 * 1024 * 1024 # MB
228 iotests
.create_image(backing_img
, TestMirrorNoBacking
.image_len
)
229 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
230 self
.vm
= iotests
.VM().add_drive(test_img
)
236 os
.remove(backing_img
)
238 os
.remove(target_backing_img
)
241 os
.remove(target_img
)
243 def test_complete(self
):
244 self
.assert_no_active_block_jobs()
246 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
247 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
248 mode
='existing', target
=target_img
)
249 self
.assert_qmp(result
, 'return', {})
251 self
.complete_and_wait()
252 result
= self
.vm
.qmp('query-block')
253 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
255 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
256 'target image does not match source after mirroring')
258 def test_cancel(self
):
259 self
.assert_no_active_block_jobs()
261 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
262 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
263 mode
='existing', target
=target_img
)
264 self
.assert_qmp(result
, 'return', {})
266 self
.wait_ready_and_cancel()
267 result
= self
.vm
.qmp('query-block')
268 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
270 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
271 'target image does not match source after mirroring')
273 def test_large_cluster(self
):
274 self
.assert_no_active_block_jobs()
276 # qemu-img create fails if the image is not there
277 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'size=%d'
278 %(TestMirrorNoBacking
.image_len
), target_backing_img
)
279 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
280 % (TestMirrorNoBacking
.image_len
, target_backing_img
), target_img
)
282 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
283 mode
='existing', target
=target_img
)
284 self
.assert_qmp(result
, 'return', {})
286 self
.complete_and_wait()
287 result
= self
.vm
.qmp('query-block')
288 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
290 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
291 'target image does not match source after mirroring')
293 class TestMirrorResized(iotests
.QMPTestCase
):
294 backing_len
= 1 * 1024 * 1024 # MB
295 image_len
= 2 * 1024 * 1024 # MB
298 iotests
.create_image(backing_img
, TestMirrorResized
.backing_len
)
299 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
300 qemu_img('resize', test_img
, '2M')
301 self
.vm
= iotests
.VM().add_drive(test_img
)
307 os
.remove(backing_img
)
309 os
.remove(target_img
)
313 def test_complete_top(self
):
314 self
.assert_no_active_block_jobs()
316 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
318 self
.assert_qmp(result
, 'return', {})
320 self
.complete_and_wait()
321 result
= self
.vm
.qmp('query-block')
322 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
324 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
325 'target image does not match source after mirroring')
327 def test_complete_full(self
):
328 self
.assert_no_active_block_jobs()
330 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
332 self
.assert_qmp(result
, 'return', {})
334 self
.complete_and_wait()
335 result
= self
.vm
.qmp('query-block')
336 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
338 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
339 'target image does not match source after mirroring')
341 class TestReadErrors(iotests
.QMPTestCase
):
342 image_len
= 2 * 1024 * 1024 # MB
344 # this should be a multiple of twice the default granularity
345 # so that we hit this offset first in state 1
346 MIRROR_GRANULARITY
= 1024 * 1024
348 def create_blkdebug_file(self
, name
, event
, errno
):
349 file = open(name
, 'w')
368 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
372 self
.blkdebug_file
= backing_img
+ ".blkdebug"
373 iotests
.create_image(backing_img
, TestReadErrors
.image_len
)
374 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
375 qemu_img('create', '-f', iotests
.imgfmt
,
376 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
377 % (self
.blkdebug_file
, backing_img
),
379 # Write something for tests that use sync='top'
380 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
382 self
.vm
= iotests
.VM().add_drive(test_img
)
388 os
.remove(backing_img
)
389 os
.remove(self
.blkdebug_file
)
391 def test_report_read(self
):
392 self
.assert_no_active_block_jobs()
394 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
396 self
.assert_qmp(result
, 'return', {})
401 for event
in self
.vm
.get_qmp_events(wait
=True):
402 if event
['event'] == 'BLOCK_JOB_ERROR':
403 self
.assert_qmp(event
, 'data/device', 'drive0')
404 self
.assert_qmp(event
, 'data/operation', 'read')
406 elif event
['event'] == 'BLOCK_JOB_READY':
407 self
.assertTrue(False, 'job completed unexpectedly')
408 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
409 self
.assertTrue(error
, 'job completed unexpectedly')
410 self
.assert_qmp(event
, 'data/type', 'mirror')
411 self
.assert_qmp(event
, 'data/device', 'drive0')
412 self
.assert_qmp(event
, 'data/error', 'Input/output error')
415 self
.assert_no_active_block_jobs()
418 def test_ignore_read(self
):
419 self
.assert_no_active_block_jobs()
421 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
422 target
=target_img
, on_source_error
='ignore')
423 self
.assert_qmp(result
, 'return', {})
425 event
= self
.vm
.get_qmp_event(wait
=True)
426 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
427 self
.assert_qmp(event
, 'data/device', 'drive0')
428 self
.assert_qmp(event
, 'data/operation', 'read')
429 result
= self
.vm
.qmp('query-block-jobs')
430 self
.assert_qmp(result
, 'return[0]/paused', False)
431 self
.complete_and_wait()
434 def test_large_cluster(self
):
435 self
.assert_no_active_block_jobs()
437 # Test COW into the target image. The first half of the
438 # cluster at MIRROR_GRANULARITY has to be copied from
439 # backing_img, even though sync='top'.
440 qemu_img('create', '-f', iotests
.imgfmt
, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img
)
441 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
442 on_source_error
='ignore',
443 mode
='existing', target
=target_img
)
444 self
.assert_qmp(result
, 'return', {})
446 event
= self
.vm
.get_qmp_event(wait
=True)
447 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
448 self
.assert_qmp(event
, 'data/device', 'drive0')
449 self
.assert_qmp(event
, 'data/operation', 'read')
450 result
= self
.vm
.qmp('query-block-jobs')
451 self
.assert_qmp(result
, 'return[0]/paused', False)
452 self
.complete_and_wait()
455 # Detach blkdebug to compare images successfully
456 qemu_img('rebase', '-f', iotests
.imgfmt
, '-u', '-b', backing_img
, test_img
)
457 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
458 'target image does not match source after mirroring')
460 def test_stop_read(self
):
461 self
.assert_no_active_block_jobs()
463 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
464 target
=target_img
, on_source_error
='stop')
465 self
.assert_qmp(result
, 'return', {})
470 for event
in self
.vm
.get_qmp_events(wait
=True):
471 if event
['event'] == 'BLOCK_JOB_ERROR':
472 self
.assert_qmp(event
, 'data/device', 'drive0')
473 self
.assert_qmp(event
, 'data/operation', 'read')
475 result
= self
.vm
.qmp('query-block-jobs')
476 self
.assert_qmp(result
, 'return[0]/paused', True)
477 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
479 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
480 self
.assert_qmp(result
, 'return', {})
482 elif event
['event'] == 'BLOCK_JOB_READY':
483 self
.assertTrue(error
, 'job completed unexpectedly')
484 self
.assert_qmp(event
, 'data/device', 'drive0')
487 result
= self
.vm
.qmp('query-block-jobs')
488 self
.assert_qmp(result
, 'return[0]/paused', False)
489 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
491 self
.complete_and_wait(wait_ready
=False)
492 self
.assert_no_active_block_jobs()
495 class TestWriteErrors(iotests
.QMPTestCase
):
496 image_len
= 2 * 1024 * 1024 # MB
498 # this should be a multiple of twice the default granularity
499 # so that we hit this offset first in state 1
500 MIRROR_GRANULARITY
= 1024 * 1024
502 def create_blkdebug_file(self
, name
, event
, errno
):
503 file = open(name
, 'w')
522 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
526 self
.blkdebug_file
= target_img
+ ".blkdebug"
527 iotests
.create_image(backing_img
, TestWriteErrors
.image_len
)
528 self
.create_blkdebug_file(self
.blkdebug_file
, "write_aio", 5)
529 qemu_img('create', '-f', iotests
.imgfmt
, '-obacking_file=%s' %(backing_img), test_img
)
530 self
.vm
= iotests
.VM().add_drive(test_img
)
531 self
.target_img
= 'blkdebug:%s:%s' % (self
.blkdebug_file
, target_img
)
532 qemu_img('create', '-f', iotests
.imgfmt
, '-osize=%d' %(TestWriteErrors
.image_len
), target_img
)
538 os
.remove(backing_img
)
539 os
.remove(self
.blkdebug_file
)
541 def test_report_write(self
):
542 self
.assert_no_active_block_jobs()
544 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
545 mode
='existing', target
=self
.target_img
)
546 self
.assert_qmp(result
, 'return', {})
551 for event
in self
.vm
.get_qmp_events(wait
=True):
552 if event
['event'] == 'BLOCK_JOB_ERROR':
553 self
.assert_qmp(event
, 'data/device', 'drive0')
554 self
.assert_qmp(event
, 'data/operation', 'write')
556 elif event
['event'] == 'BLOCK_JOB_READY':
557 self
.assertTrue(False, 'job completed unexpectedly')
558 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
559 self
.assertTrue(error
, 'job completed unexpectedly')
560 self
.assert_qmp(event
, 'data/type', 'mirror')
561 self
.assert_qmp(event
, 'data/device', 'drive0')
562 self
.assert_qmp(event
, 'data/error', 'Input/output error')
565 self
.assert_no_active_block_jobs()
568 def test_ignore_write(self
):
569 self
.assert_no_active_block_jobs()
571 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
572 mode
='existing', target
=self
.target_img
,
573 on_target_error
='ignore')
574 self
.assert_qmp(result
, 'return', {})
576 event
= self
.vm
.get_qmp_event(wait
=True)
577 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
578 self
.assert_qmp(event
, 'data/device', 'drive0')
579 self
.assert_qmp(event
, 'data/operation', 'write')
580 result
= self
.vm
.qmp('query-block-jobs')
581 self
.assert_qmp(result
, 'return[0]/paused', False)
582 self
.complete_and_wait()
585 def test_stop_write(self
):
586 self
.assert_no_active_block_jobs()
588 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
589 mode
='existing', target
=self
.target_img
,
590 on_target_error
='stop')
591 self
.assert_qmp(result
, 'return', {})
596 for event
in self
.vm
.get_qmp_events(wait
=True):
597 if event
['event'] == 'BLOCK_JOB_ERROR':
598 self
.assert_qmp(event
, 'data/device', 'drive0')
599 self
.assert_qmp(event
, 'data/operation', 'write')
601 result
= self
.vm
.qmp('query-block-jobs')
602 self
.assert_qmp(result
, 'return[0]/paused', True)
603 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
605 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
606 self
.assert_qmp(result
, 'return', {})
608 result
= self
.vm
.qmp('query-block-jobs')
609 self
.assert_qmp(result
, 'return[0]/paused', False)
610 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
612 elif event
['event'] == 'BLOCK_JOB_READY':
613 self
.assertTrue(error
, 'job completed unexpectedly')
614 self
.assert_qmp(event
, 'data/device', 'drive0')
617 self
.complete_and_wait(wait_ready
=False)
618 self
.assert_no_active_block_jobs()
621 class TestSetSpeed(iotests
.QMPTestCase
):
622 image_len
= 80 * 1024 * 1024 # MB
625 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
626 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
627 self
.vm
= iotests
.VM().add_drive(test_img
)
633 os
.remove(backing_img
)
634 os
.remove(target_img
)
636 def test_set_speed(self
):
637 self
.assert_no_active_block_jobs()
639 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
641 self
.assert_qmp(result
, 'return', {})
644 result
= self
.vm
.qmp('query-block-jobs')
645 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
646 self
.assert_qmp(result
, 'return[0]/speed', 0)
648 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
649 self
.assert_qmp(result
, 'return', {})
651 # Ensure the speed we set was accepted
652 result
= self
.vm
.qmp('query-block-jobs')
653 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
654 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
656 self
.wait_ready_and_cancel()
658 # Check setting speed in drive-mirror works
659 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
660 target
=target_img
, speed
=4*1024*1024)
661 self
.assert_qmp(result
, 'return', {})
663 result
= self
.vm
.qmp('query-block-jobs')
664 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
665 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
667 self
.wait_ready_and_cancel()
669 def test_set_speed_invalid(self
):
670 self
.assert_no_active_block_jobs()
672 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
673 target
=target_img
, speed
=-1)
674 self
.assert_qmp(result
, 'error/class', 'GenericError')
676 self
.assert_no_active_block_jobs()
678 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
680 self
.assert_qmp(result
, 'return', {})
682 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
683 self
.assert_qmp(result
, 'error/class', 'GenericError')
685 self
.wait_ready_and_cancel()
687 class TestUnbackedSource(iotests
.QMPTestCase
):
688 image_len
= 2 * 1024 * 1024 # MB
691 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
692 str(TestUnbackedSource
.image_len
))
693 self
.vm
= iotests
.VM().add_drive(test_img
)
699 os
.remove(target_img
)
701 def test_absolute_paths_full(self
):
702 self
.assert_no_active_block_jobs()
703 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
704 sync
='full', target
=target_img
,
705 mode
='absolute-paths')
706 self
.assert_qmp(result
, 'return', {})
707 self
.complete_and_wait()
708 self
.assert_no_active_block_jobs()
710 def test_absolute_paths_top(self
):
711 self
.assert_no_active_block_jobs()
712 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
713 sync
='top', 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_none(self
):
720 self
.assert_no_active_block_jobs()
721 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
722 sync
='none', 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 class TestGranularity(iotests
.QMPTestCase
):
729 image_len
= 10 * 1024 * 1024 # MB
732 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
733 str(TestGranularity
.image_len
))
734 qemu_io('-c', 'write 0 %d' % (self
.image_len
),
736 self
.vm
= iotests
.VM().add_drive(test_img
)
741 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
742 'target image does not match source after mirroring')
744 os
.remove(target_img
)
746 def test_granularity(self
):
747 self
.assert_no_active_block_jobs()
748 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
749 sync
='full', target
=target_img
,
750 mode
='absolute-paths', granularity
=8192)
751 self
.assert_qmp(result
, 'return', {})
752 event
= self
.vm
.get_qmp_event(wait
=60.0)
753 # Failures will manifest as COMPLETED/ERROR.
754 self
.assert_qmp(event
, 'event', 'BLOCK_JOB_READY')
755 self
.complete_and_wait(drive
='drive0', wait_ready
=False)
756 self
.assert_no_active_block_jobs()
758 class TestRepairQuorum(iotests
.QMPTestCase
):
759 """ This class test quorum file repair using drive-mirror.
760 It's mostly a fork of TestSingleDrive """
761 image_len
= 1 * 1024 * 1024 # MB
762 IMAGES
= [ quorum_img1
, quorum_img2
, quorum_img3
]
764 def has_quorum(self
):
765 return 'quorum' in iotests
.qemu_img_pipe('--help')
768 self
.vm
= iotests
.VM()
770 if iotests
.qemu_default_machine
== 'pc':
771 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
773 # Add each individual quorum images
774 for i
in self
.IMAGES
:
775 qemu_img('create', '-f', iotests
.imgfmt
, i
,
776 str(TestSingleDrive
.image_len
))
777 # Assign a node name to each quorum image in order to manipulate
779 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
780 self
.vm
= self
.vm
.add_drive(i
, opts
)
784 #assemble the quorum block device from the individual files
785 args
= { "options" : { "driver": "quorum", "node-name": "quorum0",
786 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
787 if self
.has_quorum():
788 result
= self
.vm
.qmp("blockdev-add", **args
)
789 self
.assert_qmp(result
, 'return', {})
794 for i
in self
.IMAGES
+ [ quorum_repair_img
]:
795 # Do a try/except because the test may have deleted some images
801 def test_complete(self
):
802 if not self
.has_quorum():
805 self
.assert_no_active_block_jobs()
807 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
808 sync
='full', node_name
="repair0", replaces
="img1",
809 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
810 self
.assert_qmp(result
, 'return', {})
812 self
.complete_and_wait(drive
="job0")
813 self
.assert_has_block_node("repair0", quorum_repair_img
)
814 # TODO: a better test requiring some QEMU infrastructure will be added
815 # to check that this file is really driven by quorum
817 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
818 'target image does not match source after mirroring')
820 def test_cancel(self
):
821 if not self
.has_quorum():
824 self
.assert_no_active_block_jobs()
826 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
827 sync
='full', node_name
="repair0", replaces
="img1",
828 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
829 self
.assert_qmp(result
, 'return', {})
831 self
.cancel_and_wait(drive
="job0", force
=True)
832 # here we check that the last registered quorum file has not been
833 # swapped out and unref
834 self
.assert_has_block_node(None, quorum_img3
)
837 def test_cancel_after_ready(self
):
838 if not self
.has_quorum():
841 self
.assert_no_active_block_jobs()
843 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
844 sync
='full', node_name
="repair0", replaces
="img1",
845 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
846 self
.assert_qmp(result
, 'return', {})
848 self
.wait_ready_and_cancel(drive
="job0")
849 # here we check that the last registered quorum file has not been
850 # swapped out and unref
851 self
.assert_has_block_node(None, quorum_img3
)
853 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
854 'target image does not match source after mirroring')
856 def test_pause(self
):
857 if not self
.has_quorum():
860 self
.assert_no_active_block_jobs()
862 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
863 sync
='full', node_name
="repair0", replaces
="img1",
864 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
865 self
.assert_qmp(result
, 'return', {})
867 result
= self
.vm
.qmp('block-job-pause', device
='job0')
868 self
.assert_qmp(result
, 'return', {})
871 result
= self
.vm
.qmp('query-block-jobs')
872 offset
= self
.dictpath(result
, 'return[0]/offset')
875 result
= self
.vm
.qmp('query-block-jobs')
876 self
.assert_qmp(result
, 'return[0]/offset', offset
)
878 result
= self
.vm
.qmp('block-job-resume', device
='job0')
879 self
.assert_qmp(result
, 'return', {})
881 self
.complete_and_wait(drive
="job0")
883 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
884 'target image does not match source after mirroring')
886 def test_medium_not_found(self
):
887 if not self
.has_quorum():
890 if iotests
.qemu_default_machine
!= 'pc':
893 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='drive0', # CD-ROM
897 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
898 self
.assert_qmp(result
, 'error/class', 'GenericError')
900 def test_image_not_found(self
):
901 if not self
.has_quorum():
904 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
905 sync
='full', node_name
='repair0', replaces
='img1',
906 mode
='existing', target
=quorum_repair_img
,
907 format
=iotests
.imgfmt
)
908 self
.assert_qmp(result
, 'error/class', 'GenericError')
910 def test_device_not_found(self
):
911 if not self
.has_quorum():
914 result
= self
.vm
.qmp('drive-mirror', job_id
='job0',
915 device
='nonexistent', sync
='full',
918 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
919 self
.assert_qmp(result
, 'error/class', 'GenericError')
921 def test_wrong_sync_mode(self
):
922 if not self
.has_quorum():
925 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', job_id
='job0',
928 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
929 self
.assert_qmp(result
, 'error/class', 'GenericError')
931 def test_no_node_name(self
):
932 if not self
.has_quorum():
935 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
936 sync
='full', replaces
='img1',
937 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
938 self
.assert_qmp(result
, 'error/class', 'GenericError')
940 def test_nonexistent_replaces(self
):
941 if not self
.has_quorum():
944 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
945 sync
='full', node_name
='repair0', replaces
='img77',
946 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
947 self
.assert_qmp(result
, 'error/class', 'GenericError')
949 def test_after_a_quorum_snapshot(self
):
950 if not self
.has_quorum():
953 result
= self
.vm
.qmp('blockdev-snapshot-sync', node_name
='img1',
954 snapshot_file
=quorum_snapshot_file
,
955 snapshot_node_name
="snap1");
957 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
958 sync
='full', node_name
='repair0', replaces
="img1",
959 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
960 self
.assert_qmp(result
, 'error/class', 'GenericError')
962 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
963 sync
='full', node_name
='repair0', replaces
="snap1",
964 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
965 self
.assert_qmp(result
, 'return', {})
967 self
.complete_and_wait('job0')
968 self
.assert_has_block_node("repair0", quorum_repair_img
)
969 # TODO: a better test requiring some QEMU infrastructure will be added
970 # to check that this file is really driven by quorum
973 if __name__
== '__main__':
974 iotests
.main(supported_fmts
=['qcow2', 'qed'])