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
)
197 args
= {'driver': iotests
.imgfmt
,
198 'node-name': self
.qmp_target
,
199 'file': { 'filename': target_img
, 'driver': 'file' } }
200 result
= self
.vm
.qmp("blockdev-add", **args
)
201 self
.assert_qmp(result
, 'return', {})
203 test_large_cluster
= None
204 test_image_not_found
= None
205 test_small_buffer2
= None
207 class TestSingleDriveZeroLength(TestSingleDrive
):
209 test_small_buffer2
= None
210 test_large_cluster
= None
212 class TestSingleBlockdevZeroLength(TestSingleBlockdev
):
215 class TestSingleDriveUnalignedLength(TestSingleDrive
):
216 image_len
= 1025 * 1024
217 test_small_buffer2
= None
218 test_large_cluster
= None
220 class TestSingleBlockdevUnalignedLength(TestSingleBlockdev
):
221 image_len
= 1025 * 1024
223 class TestMirrorNoBacking(iotests
.QMPTestCase
):
224 image_len
= 2 * 1024 * 1024 # MB
227 iotests
.create_image(backing_img
, TestMirrorNoBacking
.image_len
)
228 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
229 self
.vm
= iotests
.VM().add_drive(test_img
)
235 os
.remove(backing_img
)
237 os
.remove(target_backing_img
)
240 os
.remove(target_img
)
242 def test_complete(self
):
243 self
.assert_no_active_block_jobs()
245 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
246 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
247 mode
='existing', target
=target_img
)
248 self
.assert_qmp(result
, 'return', {})
250 self
.complete_and_wait()
251 result
= self
.vm
.qmp('query-block')
252 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
254 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
255 'target image does not match source after mirroring')
257 def test_cancel(self
):
258 self
.assert_no_active_block_jobs()
260 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
261 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
262 mode
='existing', target
=target_img
)
263 self
.assert_qmp(result
, 'return', {})
265 self
.wait_ready_and_cancel()
266 result
= self
.vm
.qmp('query-block')
267 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
269 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
270 'target image does not match source after mirroring')
272 def test_large_cluster(self
):
273 self
.assert_no_active_block_jobs()
275 # qemu-img create fails if the image is not there
276 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'size=%d'
277 %(TestMirrorNoBacking
.image_len
), target_backing_img
)
278 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
279 % (TestMirrorNoBacking
.image_len
, target_backing_img
), target_img
)
281 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
282 mode
='existing', target
=target_img
)
283 self
.assert_qmp(result
, 'return', {})
285 self
.complete_and_wait()
286 result
= self
.vm
.qmp('query-block')
287 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
289 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
290 'target image does not match source after mirroring')
292 class TestMirrorResized(iotests
.QMPTestCase
):
293 backing_len
= 1 * 1024 * 1024 # MB
294 image_len
= 2 * 1024 * 1024 # MB
297 iotests
.create_image(backing_img
, TestMirrorResized
.backing_len
)
298 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
299 qemu_img('resize', test_img
, '2M')
300 self
.vm
= iotests
.VM().add_drive(test_img
)
306 os
.remove(backing_img
)
308 os
.remove(target_img
)
312 def test_complete_top(self
):
313 self
.assert_no_active_block_jobs()
315 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
317 self
.assert_qmp(result
, 'return', {})
319 self
.complete_and_wait()
320 result
= self
.vm
.qmp('query-block')
321 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
323 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
324 'target image does not match source after mirroring')
326 def test_complete_full(self
):
327 self
.assert_no_active_block_jobs()
329 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
331 self
.assert_qmp(result
, 'return', {})
333 self
.complete_and_wait()
334 result
= self
.vm
.qmp('query-block')
335 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
337 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
338 'target image does not match source after mirroring')
340 class TestReadErrors(iotests
.QMPTestCase
):
341 image_len
= 2 * 1024 * 1024 # MB
343 # this should be a multiple of twice the default granularity
344 # so that we hit this offset first in state 1
345 MIRROR_GRANULARITY
= 1024 * 1024
347 def create_blkdebug_file(self
, name
, event
, errno
):
348 file = open(name
, 'w')
367 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
371 self
.blkdebug_file
= backing_img
+ ".blkdebug"
372 iotests
.create_image(backing_img
, TestReadErrors
.image_len
)
373 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
374 qemu_img('create', '-f', iotests
.imgfmt
,
375 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
376 % (self
.blkdebug_file
, backing_img
),
378 # Write something for tests that use sync='top'
379 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
381 self
.vm
= iotests
.VM().add_drive(test_img
)
387 os
.remove(backing_img
)
388 os
.remove(self
.blkdebug_file
)
390 def test_report_read(self
):
391 self
.assert_no_active_block_jobs()
393 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
395 self
.assert_qmp(result
, 'return', {})
400 for event
in self
.vm
.get_qmp_events(wait
=True):
401 if event
['event'] == 'BLOCK_JOB_ERROR':
402 self
.assert_qmp(event
, 'data/device', 'drive0')
403 self
.assert_qmp(event
, 'data/operation', 'read')
405 elif event
['event'] == 'BLOCK_JOB_READY':
406 self
.assertTrue(False, 'job completed unexpectedly')
407 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
408 self
.assertTrue(error
, 'job completed unexpectedly')
409 self
.assert_qmp(event
, 'data/type', 'mirror')
410 self
.assert_qmp(event
, 'data/device', 'drive0')
411 self
.assert_qmp(event
, 'data/error', 'Input/output error')
414 self
.assert_no_active_block_jobs()
417 def test_ignore_read(self
):
418 self
.assert_no_active_block_jobs()
420 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
421 target
=target_img
, on_source_error
='ignore')
422 self
.assert_qmp(result
, 'return', {})
424 event
= self
.vm
.get_qmp_event(wait
=True)
425 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
426 self
.assert_qmp(event
, 'data/device', 'drive0')
427 self
.assert_qmp(event
, 'data/operation', 'read')
428 result
= self
.vm
.qmp('query-block-jobs')
429 self
.assert_qmp(result
, 'return[0]/paused', False)
430 self
.complete_and_wait()
433 def test_large_cluster(self
):
434 self
.assert_no_active_block_jobs()
436 # Test COW into the target image. The first half of the
437 # cluster at MIRROR_GRANULARITY has to be copied from
438 # backing_img, even though sync='top'.
439 qemu_img('create', '-f', iotests
.imgfmt
, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img
)
440 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
441 on_source_error
='ignore',
442 mode
='existing', target
=target_img
)
443 self
.assert_qmp(result
, 'return', {})
445 event
= self
.vm
.get_qmp_event(wait
=True)
446 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
447 self
.assert_qmp(event
, 'data/device', 'drive0')
448 self
.assert_qmp(event
, 'data/operation', 'read')
449 result
= self
.vm
.qmp('query-block-jobs')
450 self
.assert_qmp(result
, 'return[0]/paused', False)
451 self
.complete_and_wait()
454 # Detach blkdebug to compare images successfully
455 qemu_img('rebase', '-f', iotests
.imgfmt
, '-u', '-b', backing_img
, test_img
)
456 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
457 'target image does not match source after mirroring')
459 def test_stop_read(self
):
460 self
.assert_no_active_block_jobs()
462 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
463 target
=target_img
, on_source_error
='stop')
464 self
.assert_qmp(result
, 'return', {})
469 for event
in self
.vm
.get_qmp_events(wait
=True):
470 if event
['event'] == 'BLOCK_JOB_ERROR':
471 self
.assert_qmp(event
, 'data/device', 'drive0')
472 self
.assert_qmp(event
, 'data/operation', 'read')
474 result
= self
.vm
.qmp('query-block-jobs')
475 self
.assert_qmp(result
, 'return[0]/paused', True)
476 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
478 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
479 self
.assert_qmp(result
, 'return', {})
481 elif event
['event'] == 'BLOCK_JOB_READY':
482 self
.assertTrue(error
, 'job completed unexpectedly')
483 self
.assert_qmp(event
, 'data/device', 'drive0')
486 result
= self
.vm
.qmp('query-block-jobs')
487 self
.assert_qmp(result
, 'return[0]/paused', False)
488 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
490 self
.complete_and_wait(wait_ready
=False)
491 self
.assert_no_active_block_jobs()
494 class TestWriteErrors(iotests
.QMPTestCase
):
495 image_len
= 2 * 1024 * 1024 # MB
497 # this should be a multiple of twice the default granularity
498 # so that we hit this offset first in state 1
499 MIRROR_GRANULARITY
= 1024 * 1024
501 def create_blkdebug_file(self
, name
, event
, errno
):
502 file = open(name
, 'w')
521 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
/ 512, event
, event
))
525 self
.blkdebug_file
= target_img
+ ".blkdebug"
526 iotests
.create_image(backing_img
, TestWriteErrors
.image_len
)
527 self
.create_blkdebug_file(self
.blkdebug_file
, "write_aio", 5)
528 qemu_img('create', '-f', iotests
.imgfmt
, '-obacking_file=%s' %(backing_img), test_img
)
529 self
.vm
= iotests
.VM().add_drive(test_img
)
530 self
.target_img
= 'blkdebug:%s:%s' % (self
.blkdebug_file
, target_img
)
531 qemu_img('create', '-f', iotests
.imgfmt
, '-osize=%d' %(TestWriteErrors
.image_len
), target_img
)
537 os
.remove(backing_img
)
538 os
.remove(self
.blkdebug_file
)
540 def test_report_write(self
):
541 self
.assert_no_active_block_jobs()
543 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
544 mode
='existing', target
=self
.target_img
)
545 self
.assert_qmp(result
, 'return', {})
550 for event
in self
.vm
.get_qmp_events(wait
=True):
551 if event
['event'] == 'BLOCK_JOB_ERROR':
552 self
.assert_qmp(event
, 'data/device', 'drive0')
553 self
.assert_qmp(event
, 'data/operation', 'write')
555 elif event
['event'] == 'BLOCK_JOB_READY':
556 self
.assertTrue(False, 'job completed unexpectedly')
557 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
558 self
.assertTrue(error
, 'job completed unexpectedly')
559 self
.assert_qmp(event
, 'data/type', 'mirror')
560 self
.assert_qmp(event
, 'data/device', 'drive0')
561 self
.assert_qmp(event
, 'data/error', 'Input/output error')
564 self
.assert_no_active_block_jobs()
567 def test_ignore_write(self
):
568 self
.assert_no_active_block_jobs()
570 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
571 mode
='existing', target
=self
.target_img
,
572 on_target_error
='ignore')
573 self
.assert_qmp(result
, 'return', {})
575 event
= self
.vm
.get_qmp_event(wait
=True)
576 self
.assertEquals(event
['event'], 'BLOCK_JOB_ERROR')
577 self
.assert_qmp(event
, 'data/device', 'drive0')
578 self
.assert_qmp(event
, 'data/operation', 'write')
579 result
= self
.vm
.qmp('query-block-jobs')
580 self
.assert_qmp(result
, 'return[0]/paused', False)
581 self
.complete_and_wait()
584 def test_stop_write(self
):
585 self
.assert_no_active_block_jobs()
587 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
588 mode
='existing', target
=self
.target_img
,
589 on_target_error
='stop')
590 self
.assert_qmp(result
, 'return', {})
595 for event
in self
.vm
.get_qmp_events(wait
=True):
596 if event
['event'] == 'BLOCK_JOB_ERROR':
597 self
.assert_qmp(event
, 'data/device', 'drive0')
598 self
.assert_qmp(event
, 'data/operation', 'write')
600 result
= self
.vm
.qmp('query-block-jobs')
601 self
.assert_qmp(result
, 'return[0]/paused', True)
602 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
604 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
605 self
.assert_qmp(result
, 'return', {})
607 result
= self
.vm
.qmp('query-block-jobs')
608 self
.assert_qmp(result
, 'return[0]/paused', False)
609 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
611 elif event
['event'] == 'BLOCK_JOB_READY':
612 self
.assertTrue(error
, 'job completed unexpectedly')
613 self
.assert_qmp(event
, 'data/device', 'drive0')
616 self
.complete_and_wait(wait_ready
=False)
617 self
.assert_no_active_block_jobs()
620 class TestSetSpeed(iotests
.QMPTestCase
):
621 image_len
= 80 * 1024 * 1024 # MB
624 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
625 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
626 self
.vm
= iotests
.VM().add_drive(test_img
)
632 os
.remove(backing_img
)
633 os
.remove(target_img
)
635 def test_set_speed(self
):
636 self
.assert_no_active_block_jobs()
638 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
640 self
.assert_qmp(result
, 'return', {})
643 result
= self
.vm
.qmp('query-block-jobs')
644 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
645 self
.assert_qmp(result
, 'return[0]/speed', 0)
647 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
648 self
.assert_qmp(result
, 'return', {})
650 # Ensure the speed we set was accepted
651 result
= self
.vm
.qmp('query-block-jobs')
652 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
653 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
655 self
.wait_ready_and_cancel()
657 # Check setting speed in drive-mirror works
658 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
659 target
=target_img
, speed
=4*1024*1024)
660 self
.assert_qmp(result
, 'return', {})
662 result
= self
.vm
.qmp('query-block-jobs')
663 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
664 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
666 self
.wait_ready_and_cancel()
668 def test_set_speed_invalid(self
):
669 self
.assert_no_active_block_jobs()
671 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
672 target
=target_img
, speed
=-1)
673 self
.assert_qmp(result
, 'error/class', 'GenericError')
675 self
.assert_no_active_block_jobs()
677 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
679 self
.assert_qmp(result
, 'return', {})
681 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
682 self
.assert_qmp(result
, 'error/class', 'GenericError')
684 self
.wait_ready_and_cancel()
686 class TestUnbackedSource(iotests
.QMPTestCase
):
687 image_len
= 2 * 1024 * 1024 # MB
690 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
691 str(TestUnbackedSource
.image_len
))
692 self
.vm
= iotests
.VM().add_drive(test_img
)
698 os
.remove(target_img
)
700 def test_absolute_paths_full(self
):
701 self
.assert_no_active_block_jobs()
702 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
703 sync
='full', target
=target_img
,
704 mode
='absolute-paths')
705 self
.assert_qmp(result
, 'return', {})
706 self
.complete_and_wait()
707 self
.assert_no_active_block_jobs()
709 def test_absolute_paths_top(self
):
710 self
.assert_no_active_block_jobs()
711 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
712 sync
='top', target
=target_img
,
713 mode
='absolute-paths')
714 self
.assert_qmp(result
, 'return', {})
715 self
.complete_and_wait()
716 self
.assert_no_active_block_jobs()
718 def test_absolute_paths_none(self
):
719 self
.assert_no_active_block_jobs()
720 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
721 sync
='none', target
=target_img
,
722 mode
='absolute-paths')
723 self
.assert_qmp(result
, 'return', {})
724 self
.complete_and_wait()
725 self
.assert_no_active_block_jobs()
727 class TestGranularity(iotests
.QMPTestCase
):
728 image_len
= 10 * 1024 * 1024 # MB
731 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
732 str(TestGranularity
.image_len
))
733 qemu_io('-c', 'write 0 %d' % (self
.image_len
),
735 self
.vm
= iotests
.VM().add_drive(test_img
)
740 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
741 'target image does not match source after mirroring')
743 os
.remove(target_img
)
745 def test_granularity(self
):
746 self
.assert_no_active_block_jobs()
747 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
748 sync
='full', target
=target_img
,
749 mode
='absolute-paths', granularity
=8192)
750 self
.assert_qmp(result
, 'return', {})
751 event
= self
.vm
.get_qmp_event(wait
=60.0)
752 # Failures will manifest as COMPLETED/ERROR.
753 self
.assert_qmp(event
, 'event', 'BLOCK_JOB_READY')
754 self
.complete_and_wait(drive
='drive0', wait_ready
=False)
755 self
.assert_no_active_block_jobs()
757 class TestRepairQuorum(iotests
.QMPTestCase
):
758 """ This class test quorum file repair using drive-mirror.
759 It's mostly a fork of TestSingleDrive """
760 image_len
= 1 * 1024 * 1024 # MB
761 IMAGES
= [ quorum_img1
, quorum_img2
, quorum_img3
]
764 self
.vm
= iotests
.VM()
766 if iotests
.qemu_default_machine
== 'pc':
767 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
769 # Add each individual quorum images
770 for i
in self
.IMAGES
:
771 qemu_img('create', '-f', iotests
.imgfmt
, i
,
772 str(TestSingleDrive
.image_len
))
773 # Assign a node name to each quorum image in order to manipulate
775 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
776 self
.vm
= self
.vm
.add_drive(i
, opts
)
780 #assemble the quorum block device from the individual files
781 args
= { "driver": "quorum", "node-name": "quorum0",
782 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
783 if iotests
.supports_quorum():
784 result
= self
.vm
.qmp("blockdev-add", **args
)
785 self
.assert_qmp(result
, 'return', {})
790 for i
in self
.IMAGES
+ [ quorum_repair_img
]:
791 # Do a try/except because the test may have deleted some images
797 def test_complete(self
):
798 if not iotests
.supports_quorum():
801 self
.assert_no_active_block_jobs()
803 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
804 sync
='full', node_name
="repair0", replaces
="img1",
805 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
806 self
.assert_qmp(result
, 'return', {})
808 self
.complete_and_wait(drive
="job0")
809 self
.assert_has_block_node("repair0", quorum_repair_img
)
810 # TODO: a better test requiring some QEMU infrastructure will be added
811 # to check that this file is really driven by quorum
813 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
814 'target image does not match source after mirroring')
816 def test_cancel(self
):
817 if not iotests
.supports_quorum():
820 self
.assert_no_active_block_jobs()
822 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
823 sync
='full', node_name
="repair0", replaces
="img1",
824 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
825 self
.assert_qmp(result
, 'return', {})
827 self
.cancel_and_wait(drive
="job0", force
=True)
828 # here we check that the last registered quorum file has not been
829 # swapped out and unref
830 self
.assert_has_block_node(None, quorum_img3
)
833 def test_cancel_after_ready(self
):
834 if not iotests
.supports_quorum():
837 self
.assert_no_active_block_jobs()
839 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
840 sync
='full', node_name
="repair0", replaces
="img1",
841 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
842 self
.assert_qmp(result
, 'return', {})
844 self
.wait_ready_and_cancel(drive
="job0")
845 # here we check that the last registered quorum file has not been
846 # swapped out and unref
847 self
.assert_has_block_node(None, quorum_img3
)
849 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
850 'target image does not match source after mirroring')
852 def test_pause(self
):
853 if not iotests
.supports_quorum():
856 self
.assert_no_active_block_jobs()
858 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
859 sync
='full', node_name
="repair0", replaces
="img1",
860 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
861 self
.assert_qmp(result
, 'return', {})
863 result
= self
.vm
.qmp('block-job-pause', device
='job0')
864 self
.assert_qmp(result
, 'return', {})
867 result
= self
.vm
.qmp('query-block-jobs')
868 offset
= self
.dictpath(result
, 'return[0]/offset')
871 result
= self
.vm
.qmp('query-block-jobs')
872 self
.assert_qmp(result
, 'return[0]/offset', offset
)
874 result
= self
.vm
.qmp('block-job-resume', device
='job0')
875 self
.assert_qmp(result
, 'return', {})
877 self
.complete_and_wait(drive
="job0")
879 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
880 'target image does not match source after mirroring')
882 def test_medium_not_found(self
):
883 if not iotests
.supports_quorum():
886 if iotests
.qemu_default_machine
!= 'pc':
889 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='drive0', # CD-ROM
893 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
894 self
.assert_qmp(result
, 'error/class', 'GenericError')
896 def test_image_not_found(self
):
897 if not iotests
.supports_quorum():
900 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
901 sync
='full', node_name
='repair0', replaces
='img1',
902 mode
='existing', target
=quorum_repair_img
,
903 format
=iotests
.imgfmt
)
904 self
.assert_qmp(result
, 'error/class', 'GenericError')
906 def test_device_not_found(self
):
907 if not iotests
.supports_quorum():
910 result
= self
.vm
.qmp('drive-mirror', job_id
='job0',
911 device
='nonexistent', sync
='full',
914 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
915 self
.assert_qmp(result
, 'error/class', 'GenericError')
917 def test_wrong_sync_mode(self
):
918 if not iotests
.supports_quorum():
921 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', job_id
='job0',
924 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
925 self
.assert_qmp(result
, 'error/class', 'GenericError')
927 def test_no_node_name(self
):
928 if not iotests
.supports_quorum():
931 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
932 sync
='full', replaces
='img1',
933 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
934 self
.assert_qmp(result
, 'error/class', 'GenericError')
936 def test_nonexistent_replaces(self
):
937 if not iotests
.supports_quorum():
940 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
941 sync
='full', node_name
='repair0', replaces
='img77',
942 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
943 self
.assert_qmp(result
, 'error/class', 'GenericError')
945 def test_after_a_quorum_snapshot(self
):
946 if not iotests
.supports_quorum():
949 result
= self
.vm
.qmp('blockdev-snapshot-sync', node_name
='img1',
950 snapshot_file
=quorum_snapshot_file
,
951 snapshot_node_name
="snap1");
953 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
954 sync
='full', node_name
='repair0', replaces
="img1",
955 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
956 self
.assert_qmp(result
, 'error/class', 'GenericError')
958 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
959 sync
='full', node_name
='repair0', replaces
="snap1",
960 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
961 self
.assert_qmp(result
, 'return', {})
963 self
.complete_and_wait('job0')
964 self
.assert_has_block_node("repair0", quorum_repair_img
)
965 # TODO: a better test requiring some QEMU infrastructure will be added
966 # to check that this file is really driven by quorum
969 # Test mirroring with a source that does not have any parents (not even a
971 class TestOrphanedSource(iotests
.QMPTestCase
):
973 blk0
= { 'node-name': 'src',
974 'driver': 'null-co' }
976 blk1
= { 'node-name': 'dest',
977 'driver': 'null-co' }
979 blk2
= { 'node-name': 'dest-ro',
983 self
.vm
= iotests
.VM()
984 self
.vm
.add_blockdev(self
.qmp_to_opts(blk0
))
985 self
.vm
.add_blockdev(self
.qmp_to_opts(blk1
))
986 self
.vm
.add_blockdev(self
.qmp_to_opts(blk2
))
992 def test_no_job_id(self
):
993 self
.assert_no_active_block_jobs()
995 result
= self
.vm
.qmp('blockdev-mirror', device
='src', sync
='full',
997 self
.assert_qmp(result
, 'error/class', 'GenericError')
999 def test_success(self
):
1000 self
.assert_no_active_block_jobs()
1002 result
= self
.vm
.qmp('blockdev-mirror', job_id
='job', device
='src',
1003 sync
='full', target
='dest')
1004 self
.assert_qmp(result
, 'return', {})
1006 self
.complete_and_wait('job')
1008 def test_failing_permissions(self
):
1009 self
.assert_no_active_block_jobs()
1011 result
= self
.vm
.qmp('blockdev-mirror', device
='src', sync
='full',
1013 self
.assert_qmp(result
, 'error/class', 'GenericError')
1015 if __name__
== '__main__':
1016 iotests
.main(supported_fmts
=['qcow2', 'qed'])