]>
git.proxmox.com Git - mirror_qemu.git/blob - tests/qemu-iotests/041
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/>.
25 from iotests
import qemu_img
, qemu_io
27 backing_img
= os
.path
.join(iotests
.test_dir
, 'backing.img')
28 target_backing_img
= os
.path
.join(iotests
.test_dir
, 'target-backing.img')
29 test_img
= os
.path
.join(iotests
.test_dir
, 'test.img')
30 target_img
= os
.path
.join(iotests
.test_dir
, 'target.img')
32 quorum_img1
= os
.path
.join(iotests
.test_dir
, 'quorum1.img')
33 quorum_img2
= os
.path
.join(iotests
.test_dir
, 'quorum2.img')
34 quorum_img3
= os
.path
.join(iotests
.test_dir
, 'quorum3.img')
35 quorum_repair_img
= os
.path
.join(iotests
.test_dir
, 'quorum_repair.img')
36 quorum_snapshot_file
= os
.path
.join(iotests
.test_dir
, 'quorum_snapshot.img')
38 nbd_sock_path
= os
.path
.join(iotests
.sock_dir
, 'nbd.sock')
40 class TestSingleDrive(iotests
.QMPTestCase
):
41 image_len
= 1 * 1024 * 1024 # MB
42 qmp_cmd
= 'drive-mirror'
43 qmp_target
= target_img
46 iotests
.create_image(backing_img
, self
.image_len
)
47 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
48 self
.vm
= iotests
.VM().add_drive(test_img
, "node-name=top,backing.node-name=base")
49 if iotests
.qemu_default_machine
== 'pc':
50 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
56 os
.remove(backing_img
)
62 def test_complete(self
):
63 self
.assert_no_active_block_jobs()
65 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
66 target
=self
.qmp_target
)
67 self
.assert_qmp(result
, 'return', {})
69 self
.complete_and_wait()
70 result
= self
.vm
.qmp('query-block')
71 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
73 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
74 'target image does not match source after mirroring')
76 def test_cancel(self
):
77 self
.assert_no_active_block_jobs()
79 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
80 target
=self
.qmp_target
)
81 self
.assert_qmp(result
, 'return', {})
83 self
.cancel_and_wait(force
=True)
84 result
= self
.vm
.qmp('query-block')
85 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
87 def test_cancel_after_ready(self
):
88 self
.assert_no_active_block_jobs()
90 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
91 target
=self
.qmp_target
)
92 self
.assert_qmp(result
, 'return', {})
94 self
.wait_ready_and_cancel()
95 result
= self
.vm
.qmp('query-block')
96 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
98 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
99 'target image does not match source after mirroring')
101 def test_pause(self
):
102 self
.assert_no_active_block_jobs()
104 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
105 target
=self
.qmp_target
)
106 self
.assert_qmp(result
, 'return', {})
108 self
.pause_job('drive0')
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 # Tests that the insertion of the mirror_top filter node doesn't make a
173 # difference to query-block
174 def test_implicit_node(self
):
175 self
.assert_no_active_block_jobs()
177 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
178 target
=self
.qmp_target
)
179 self
.assert_qmp(result
, 'return', {})
181 result
= self
.vm
.qmp('query-block')
182 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
183 self
.assert_qmp(result
, 'return[0]/inserted/drv', iotests
.imgfmt
)
184 self
.assert_qmp(result
, 'return[0]/inserted/backing_file', backing_img
)
185 self
.assert_qmp(result
, 'return[0]/inserted/backing_file_depth', 1)
186 self
.assert_qmp(result
, 'return[0]/inserted/image/filename', test_img
)
187 self
.assert_qmp(result
, 'return[0]/inserted/image/backing-image/filename', backing_img
)
189 result
= self
.vm
.qmp('query-blockstats')
190 self
.assert_qmp(result
, 'return[0]/node-name', 'top')
191 self
.assert_qmp(result
, 'return[0]/backing/node-name', 'base')
193 self
.cancel_and_wait(force
=True)
194 result
= self
.vm
.qmp('query-block')
195 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
196 self
.assert_qmp(result
, 'return[0]/inserted/drv', iotests
.imgfmt
)
197 self
.assert_qmp(result
, 'return[0]/inserted/backing_file', backing_img
)
198 self
.assert_qmp(result
, 'return[0]/inserted/backing_file_depth', 1)
199 self
.assert_qmp(result
, 'return[0]/inserted/image/filename', test_img
)
200 self
.assert_qmp(result
, 'return[0]/inserted/image/backing-image/filename', backing_img
)
202 result
= self
.vm
.qmp('query-blockstats')
203 self
.assert_qmp(result
, 'return[0]/node-name', 'top')
204 self
.assert_qmp(result
, 'return[0]/backing/node-name', 'base')
206 def test_medium_not_found(self
):
207 if iotests
.qemu_default_machine
!= 'pc':
210 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='ide1-cd0', sync
='full',
211 target
=self
.qmp_target
)
212 self
.assert_qmp(result
, 'error/class', 'GenericError')
214 def test_image_not_found(self
):
215 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='drive0', sync
='full',
216 mode
='existing', target
=self
.qmp_target
)
217 self
.assert_qmp(result
, 'error/class', 'GenericError')
219 def test_device_not_found(self
):
220 result
= self
.vm
.qmp(self
.qmp_cmd
, device
='nonexistent', sync
='full',
221 target
=self
.qmp_target
)
222 self
.assert_qmp(result
, 'error/class', 'GenericError')
224 class TestSingleBlockdev(TestSingleDrive
):
225 qmp_cmd
= 'blockdev-mirror'
229 TestSingleDrive
.setUp(self
)
230 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
231 args
= {'driver': iotests
.imgfmt
,
232 'node-name': self
.qmp_target
,
233 'file': { 'filename': target_img
, 'driver': 'file' } }
234 result
= self
.vm
.qmp("blockdev-add", **args
)
235 self
.assert_qmp(result
, 'return', {})
237 def test_mirror_to_self(self
):
238 result
= self
.vm
.qmp(self
.qmp_cmd
, job_id
='job0',
239 device
=self
.qmp_target
, sync
='full',
240 target
=self
.qmp_target
)
241 self
.assert_qmp(result
, 'error/class', 'GenericError')
243 def do_test_resize(self
, device
, node
):
246 result
= self
.vm
.qmp('block_resize', device
=device
, size
=65536)
247 self
.assert_qmp(result
, 'error/class', 'GenericError')
249 result
= self
.vm
.qmp('block_resize', node_name
=node
, size
=65536)
250 self
.assert_qmp(result
, 'error/class', 'GenericError')
252 result
= self
.vm
.qmp(self
.qmp_cmd
, job_id
='job0', device
='drive0',
253 sync
='full', target
=self
.qmp_target
,
254 auto_finalize
=False, auto_dismiss
=False)
255 self
.assert_qmp(result
, 'return', {})
257 result
= self
.vm
.run_job('job0', auto_finalize
=False,
258 pre_finalize
=pre_finalize
)
259 self
.assertEqual(result
, None)
261 def test_source_resize(self
):
262 self
.do_test_resize('drive0', 'top')
264 def test_target_resize(self
):
265 self
.do_test_resize(None, self
.qmp_target
)
267 def do_test_target_size(self
, size
):
268 result
= self
.vm
.qmp('block_resize', node_name
=self
.qmp_target
,
270 self
.assert_qmp(result
, 'return', {})
272 result
= self
.vm
.qmp(self
.qmp_cmd
, job_id
='job0',
273 device
='drive0', sync
='full', auto_dismiss
=False,
274 target
=self
.qmp_target
)
275 self
.assert_qmp(result
, 'return', {})
277 result
= self
.vm
.run_job('job0')
278 self
.assertEqual(result
, 'Source and target image have different sizes')
280 # qed does not support shrinking
281 @iotests.skip_for_formats(('qed'))
282 def test_small_target(self
):
283 self
.do_test_target_size(self
.image_len
// 2)
285 def test_large_target(self
):
286 self
.do_test_target_size(self
.image_len
* 2)
288 test_large_cluster
= None
289 test_image_not_found
= None
290 test_small_buffer2
= None
292 class TestSingleDriveZeroLength(TestSingleDrive
):
294 test_small_buffer2
= None
295 test_large_cluster
= None
297 class TestSingleBlockdevZeroLength(TestSingleBlockdev
):
299 test_small_target
= None
300 test_large_target
= None
302 class TestSingleDriveUnalignedLength(TestSingleDrive
):
303 image_len
= 1025 * 1024
304 test_small_buffer2
= None
305 test_large_cluster
= None
307 class TestSingleBlockdevUnalignedLength(TestSingleBlockdev
):
308 image_len
= 1025 * 1024
310 class TestMirrorNoBacking(iotests
.QMPTestCase
):
311 image_len
= 2 * 1024 * 1024 # MB
314 iotests
.create_image(backing_img
, TestMirrorNoBacking
.image_len
)
315 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
316 self
.vm
= iotests
.VM().add_drive(test_img
)
322 os
.remove(backing_img
)
324 os
.remove(target_backing_img
)
327 os
.remove(target_img
)
329 def test_complete(self
):
330 self
.assert_no_active_block_jobs()
332 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
333 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
334 mode
='existing', target
=target_img
)
335 self
.assert_qmp(result
, 'return', {})
337 self
.complete_and_wait()
338 result
= self
.vm
.qmp('query-block')
339 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
341 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
342 'target image does not match source after mirroring')
344 def test_cancel(self
):
345 self
.assert_no_active_block_jobs()
347 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, target_img
)
348 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
349 mode
='existing', target
=target_img
)
350 self
.assert_qmp(result
, 'return', {})
352 self
.wait_ready_and_cancel()
353 result
= self
.vm
.qmp('query-block')
354 self
.assert_qmp(result
, 'return[0]/inserted/file', test_img
)
356 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
357 'target image does not match source after mirroring')
359 def test_large_cluster(self
):
360 self
.assert_no_active_block_jobs()
362 # qemu-img create fails if the image is not there
363 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'size=%d'
364 %(TestMirrorNoBacking
.image_len
), target_backing_img
)
365 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'cluster_size=%d,backing_file=%s'
366 % (TestMirrorNoBacking
.image_len
, target_backing_img
), target_img
)
368 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
369 mode
='existing', target
=target_img
)
370 self
.assert_qmp(result
, 'return', {})
372 self
.complete_and_wait()
373 result
= self
.vm
.qmp('query-block')
374 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
376 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
377 'target image does not match source after mirroring')
379 class TestMirrorResized(iotests
.QMPTestCase
):
380 backing_len
= 1 * 1024 * 1024 # MB
381 image_len
= 2 * 1024 * 1024 # MB
384 iotests
.create_image(backing_img
, TestMirrorResized
.backing_len
)
385 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
386 qemu_img('resize', test_img
, '2M')
387 self
.vm
= iotests
.VM().add_drive(test_img
)
393 os
.remove(backing_img
)
395 os
.remove(target_img
)
399 def test_complete_top(self
):
400 self
.assert_no_active_block_jobs()
402 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
404 self
.assert_qmp(result
, 'return', {})
406 self
.complete_and_wait()
407 result
= self
.vm
.qmp('query-block')
408 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
410 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
411 'target image does not match source after mirroring')
413 def test_complete_full(self
):
414 self
.assert_no_active_block_jobs()
416 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
418 self
.assert_qmp(result
, 'return', {})
420 self
.complete_and_wait()
421 result
= self
.vm
.qmp('query-block')
422 self
.assert_qmp(result
, 'return[0]/inserted/file', target_img
)
424 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
425 'target image does not match source after mirroring')
427 class TestReadErrors(iotests
.QMPTestCase
):
428 image_len
= 2 * 1024 * 1024 # MB
430 # this should be a multiple of twice the default granularity
431 # so that we hit this offset first in state 1
432 MIRROR_GRANULARITY
= 1024 * 1024
434 def create_blkdebug_file(self
, name
, event
, errno
):
435 file = open(name
, 'w')
454 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
// 512, event
, event
))
458 self
.blkdebug_file
= backing_img
+ ".blkdebug"
459 iotests
.create_image(backing_img
, TestReadErrors
.image_len
)
460 self
.create_blkdebug_file(self
.blkdebug_file
, "read_aio", 5)
461 qemu_img('create', '-f', iotests
.imgfmt
,
462 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
463 % (self
.blkdebug_file
, backing_img
),
465 # Write something for tests that use sync='top'
466 qemu_io('-c', 'write %d 512' % (self
.MIRROR_GRANULARITY
+ 65536),
468 self
.vm
= iotests
.VM().add_drive(test_img
)
474 os
.remove(target_img
)
475 os
.remove(backing_img
)
476 os
.remove(self
.blkdebug_file
)
478 def test_report_read(self
):
479 self
.assert_no_active_block_jobs()
481 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
483 self
.assert_qmp(result
, 'return', {})
488 for event
in self
.vm
.get_qmp_events(wait
=True):
489 if event
['event'] == 'BLOCK_JOB_ERROR':
490 self
.assert_qmp(event
, 'data/device', 'drive0')
491 self
.assert_qmp(event
, 'data/operation', 'read')
493 elif event
['event'] == 'BLOCK_JOB_READY':
494 self
.assertTrue(False, 'job completed unexpectedly')
495 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
496 self
.assertTrue(error
, 'job completed unexpectedly')
497 self
.assert_qmp(event
, 'data/type', 'mirror')
498 self
.assert_qmp(event
, 'data/device', 'drive0')
499 self
.assert_qmp(event
, 'data/error', 'Input/output error')
501 elif event
['event'] == 'JOB_STATUS_CHANGE':
502 self
.assert_qmp(event
, 'data/id', 'drive0')
504 self
.assert_no_active_block_jobs()
506 def test_ignore_read(self
):
507 self
.assert_no_active_block_jobs()
509 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
510 target
=target_img
, on_source_error
='ignore')
511 self
.assert_qmp(result
, 'return', {})
513 event
= self
.vm
.get_qmp_event(wait
=True)
514 while event
['event'] == 'JOB_STATUS_CHANGE':
515 self
.assert_qmp(event
, 'data/id', 'drive0')
516 event
= self
.vm
.get_qmp_event(wait
=True)
518 self
.assertEqual(event
['event'], 'BLOCK_JOB_ERROR')
519 self
.assert_qmp(event
, 'data/device', 'drive0')
520 self
.assert_qmp(event
, 'data/operation', 'read')
521 result
= self
.vm
.qmp('query-block-jobs')
522 self
.assert_qmp(result
, 'return[0]/paused', False)
523 self
.complete_and_wait()
525 def test_large_cluster(self
):
526 self
.assert_no_active_block_jobs()
528 # Test COW into the target image. The first half of the
529 # cluster at MIRROR_GRANULARITY has to be copied from
530 # backing_img, even though sync='top'.
531 qemu_img('create', '-f', iotests
.imgfmt
, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img
)
532 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='top',
533 on_source_error
='ignore',
534 mode
='existing', target
=target_img
)
535 self
.assert_qmp(result
, 'return', {})
537 event
= self
.vm
.get_qmp_event(wait
=True)
538 while event
['event'] == 'JOB_STATUS_CHANGE':
539 self
.assert_qmp(event
, 'data/id', 'drive0')
540 event
= self
.vm
.get_qmp_event(wait
=True)
542 self
.assertEqual(event
['event'], 'BLOCK_JOB_ERROR')
543 self
.assert_qmp(event
, 'data/device', 'drive0')
544 self
.assert_qmp(event
, 'data/operation', 'read')
545 result
= self
.vm
.qmp('query-block-jobs')
546 self
.assert_qmp(result
, 'return[0]/paused', False)
547 self
.complete_and_wait()
550 # Detach blkdebug to compare images successfully
551 qemu_img('rebase', '-f', iotests
.imgfmt
, '-u', '-b', backing_img
, test_img
)
552 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
553 'target image does not match source after mirroring')
555 def test_stop_read(self
):
556 self
.assert_no_active_block_jobs()
558 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
559 target
=target_img
, on_source_error
='stop')
560 self
.assert_qmp(result
, 'return', {})
565 for event
in self
.vm
.get_qmp_events(wait
=True):
566 if event
['event'] == 'BLOCK_JOB_ERROR':
567 self
.assert_qmp(event
, 'data/device', 'drive0')
568 self
.assert_qmp(event
, 'data/operation', 'read')
570 result
= self
.vm
.qmp('query-block-jobs')
571 self
.assert_qmp(result
, 'return[0]/paused', True)
572 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
574 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
575 self
.assert_qmp(result
, 'return', {})
577 elif event
['event'] == 'BLOCK_JOB_READY':
578 self
.assertTrue(error
, 'job completed unexpectedly')
579 self
.assert_qmp(event
, 'data/device', 'drive0')
582 result
= self
.vm
.qmp('query-block-jobs')
583 self
.assert_qmp(result
, 'return[0]/paused', False)
584 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
586 self
.complete_and_wait(wait_ready
=False)
587 self
.assert_no_active_block_jobs()
589 class TestWriteErrors(iotests
.QMPTestCase
):
590 image_len
= 2 * 1024 * 1024 # MB
592 # this should be a multiple of twice the default granularity
593 # so that we hit this offset first in state 1
594 MIRROR_GRANULARITY
= 1024 * 1024
596 def create_blkdebug_file(self
, name
, event
, errno
):
597 file = open(name
, 'w')
616 ''' % (event
, errno
, self
.MIRROR_GRANULARITY
// 512, event
, event
))
620 self
.blkdebug_file
= target_img
+ ".blkdebug"
621 iotests
.create_image(backing_img
, TestWriteErrors
.image_len
)
622 self
.create_blkdebug_file(self
.blkdebug_file
, "write_aio", 5)
623 qemu_img('create', '-f', iotests
.imgfmt
, '-obacking_file=%s' %(backing_img), test_img
)
624 self
.vm
= iotests
.VM().add_drive(test_img
)
625 self
.target_img
= 'blkdebug:%s:%s' % (self
.blkdebug_file
, target_img
)
626 qemu_img('create', '-f', iotests
.imgfmt
, '-osize=%d' %(TestWriteErrors
.image_len
), target_img
)
632 os
.remove(target_img
)
633 os
.remove(backing_img
)
634 os
.remove(self
.blkdebug_file
)
636 def test_report_write(self
):
637 self
.assert_no_active_block_jobs()
639 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
640 mode
='existing', target
=self
.target_img
)
641 self
.assert_qmp(result
, 'return', {})
646 for event
in self
.vm
.get_qmp_events(wait
=True):
647 if event
['event'] == 'BLOCK_JOB_ERROR':
648 self
.assert_qmp(event
, 'data/device', 'drive0')
649 self
.assert_qmp(event
, 'data/operation', 'write')
651 elif event
['event'] == 'BLOCK_JOB_READY':
652 self
.assertTrue(False, 'job completed unexpectedly')
653 elif event
['event'] == 'BLOCK_JOB_COMPLETED':
654 self
.assertTrue(error
, 'job completed unexpectedly')
655 self
.assert_qmp(event
, 'data/type', 'mirror')
656 self
.assert_qmp(event
, 'data/device', 'drive0')
657 self
.assert_qmp(event
, 'data/error', 'Input/output error')
660 self
.assert_no_active_block_jobs()
662 def test_ignore_write(self
):
663 self
.assert_no_active_block_jobs()
665 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
666 mode
='existing', target
=self
.target_img
,
667 on_target_error
='ignore')
668 self
.assert_qmp(result
, 'return', {})
670 event
= self
.vm
.event_wait(name
='BLOCK_JOB_ERROR')
671 self
.assertEqual(event
['event'], 'BLOCK_JOB_ERROR')
672 self
.assert_qmp(event
, 'data/device', 'drive0')
673 self
.assert_qmp(event
, 'data/operation', 'write')
674 result
= self
.vm
.qmp('query-block-jobs')
675 self
.assert_qmp(result
, 'return[0]/paused', False)
676 self
.complete_and_wait()
678 def test_stop_write(self
):
679 self
.assert_no_active_block_jobs()
681 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
682 mode
='existing', target
=self
.target_img
,
683 on_target_error
='stop')
684 self
.assert_qmp(result
, 'return', {})
689 for event
in self
.vm
.get_qmp_events(wait
=True):
690 if event
['event'] == 'BLOCK_JOB_ERROR':
691 self
.assert_qmp(event
, 'data/device', 'drive0')
692 self
.assert_qmp(event
, 'data/operation', 'write')
694 result
= self
.vm
.qmp('query-block-jobs')
695 self
.assert_qmp(result
, 'return[0]/paused', True)
696 self
.assert_qmp(result
, 'return[0]/io-status', 'failed')
698 result
= self
.vm
.qmp('block-job-resume', device
='drive0')
699 self
.assert_qmp(result
, 'return', {})
701 result
= self
.vm
.qmp('query-block-jobs')
702 self
.assert_qmp(result
, 'return[0]/paused', False)
703 self
.assert_qmp(result
, 'return[0]/io-status', 'ok')
705 elif event
['event'] == 'BLOCK_JOB_READY':
706 self
.assertTrue(error
, 'job completed unexpectedly')
707 self
.assert_qmp(event
, 'data/device', 'drive0')
710 self
.complete_and_wait(wait_ready
=False)
711 self
.assert_no_active_block_jobs()
713 class TestSetSpeed(iotests
.QMPTestCase
):
714 image_len
= 80 * 1024 * 1024 # MB
717 qemu_img('create', backing_img
, str(TestSetSpeed
.image_len
))
718 qemu_img('create', '-f', iotests
.imgfmt
, '-o', 'backing_file=%s' % backing_img
, test_img
)
719 self
.vm
= iotests
.VM().add_drive(test_img
)
725 os
.remove(backing_img
)
726 os
.remove(target_img
)
728 def test_set_speed(self
):
729 self
.assert_no_active_block_jobs()
731 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
733 self
.assert_qmp(result
, 'return', {})
736 result
= self
.vm
.qmp('query-block-jobs')
737 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
738 self
.assert_qmp(result
, 'return[0]/speed', 0)
740 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=8 * 1024 * 1024)
741 self
.assert_qmp(result
, 'return', {})
743 # Ensure the speed we set was accepted
744 result
= self
.vm
.qmp('query-block-jobs')
745 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
746 self
.assert_qmp(result
, 'return[0]/speed', 8 * 1024 * 1024)
748 self
.wait_ready_and_cancel()
750 # Check setting speed in drive-mirror works
751 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
752 target
=target_img
, speed
=4*1024*1024)
753 self
.assert_qmp(result
, 'return', {})
755 result
= self
.vm
.qmp('query-block-jobs')
756 self
.assert_qmp(result
, 'return[0]/device', 'drive0')
757 self
.assert_qmp(result
, 'return[0]/speed', 4 * 1024 * 1024)
759 self
.wait_ready_and_cancel()
761 def test_set_speed_invalid(self
):
762 self
.assert_no_active_block_jobs()
764 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
765 target
=target_img
, speed
=-1)
766 self
.assert_qmp(result
, 'error/class', 'GenericError')
768 self
.assert_no_active_block_jobs()
770 result
= self
.vm
.qmp('drive-mirror', device
='drive0', sync
='full',
772 self
.assert_qmp(result
, 'return', {})
774 result
= self
.vm
.qmp('block-job-set-speed', device
='drive0', speed
=-1)
775 self
.assert_qmp(result
, 'error/class', 'GenericError')
777 self
.wait_ready_and_cancel()
779 class TestUnbackedSource(iotests
.QMPTestCase
):
780 image_len
= 2 * 1024 * 1024 # MB
783 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
784 str(TestUnbackedSource
.image_len
))
785 self
.vm
= iotests
.VM()
787 result
= self
.vm
.qmp('blockdev-add', node_name
='drive0',
788 driver
=iotests
.imgfmt
,
791 'filename': test_img
,
793 self
.assert_qmp(result
, 'return', {})
798 os
.remove(target_img
)
800 def test_absolute_paths_full(self
):
801 self
.assert_no_active_block_jobs()
802 result
= self
.vm
.qmp('drive-mirror', job_id
='drive0', device
='drive0',
803 sync
='full', target
=target_img
,
804 mode
='absolute-paths')
805 self
.assert_qmp(result
, 'return', {})
806 self
.complete_and_wait()
807 self
.assert_no_active_block_jobs()
809 def test_absolute_paths_top(self
):
810 self
.assert_no_active_block_jobs()
811 result
= self
.vm
.qmp('drive-mirror', job_id
='drive0', device
='drive0',
812 sync
='top', target
=target_img
,
813 mode
='absolute-paths')
814 self
.assert_qmp(result
, 'return', {})
815 self
.complete_and_wait()
816 self
.assert_no_active_block_jobs()
818 def test_absolute_paths_none(self
):
819 self
.assert_no_active_block_jobs()
820 result
= self
.vm
.qmp('drive-mirror', job_id
='drive0', device
='drive0',
821 sync
='none', target
=target_img
,
822 mode
='absolute-paths')
823 self
.assert_qmp(result
, 'return', {})
824 self
.complete_and_wait()
825 self
.assert_no_active_block_jobs()
827 def test_existing_full(self
):
828 qemu_img('create', '-f', iotests
.imgfmt
, target_img
,
830 qemu_io('-c', 'write -P 42 0 64k', target_img
)
832 self
.assert_no_active_block_jobs()
833 result
= self
.vm
.qmp('drive-mirror', job_id
='drive0', device
='drive0',
834 sync
='full', target
=target_img
, mode
='existing')
835 self
.assert_qmp(result
, 'return', {})
836 self
.complete_and_wait()
837 self
.assert_no_active_block_jobs()
839 result
= self
.vm
.qmp('blockdev-del', node_name
='drive0')
840 self
.assert_qmp(result
, 'return', {})
842 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
843 'target image does not match source after mirroring')
845 def test_blockdev_full(self
):
846 qemu_img('create', '-f', iotests
.imgfmt
, target_img
,
848 qemu_io('-c', 'write -P 42 0 64k', target_img
)
850 result
= self
.vm
.qmp('blockdev-add', node_name
='target',
851 driver
=iotests
.imgfmt
,
854 'filename': target_img
,
856 self
.assert_qmp(result
, 'return', {})
858 self
.assert_no_active_block_jobs()
859 result
= self
.vm
.qmp('blockdev-mirror', job_id
='drive0', device
='drive0',
860 sync
='full', target
='target')
861 self
.assert_qmp(result
, 'return', {})
862 self
.complete_and_wait()
863 self
.assert_no_active_block_jobs()
865 result
= self
.vm
.qmp('blockdev-del', node_name
='drive0')
866 self
.assert_qmp(result
, 'return', {})
868 result
= self
.vm
.qmp('blockdev-del', node_name
='target')
869 self
.assert_qmp(result
, 'return', {})
871 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
872 'target image does not match source after mirroring')
874 class TestGranularity(iotests
.QMPTestCase
):
875 image_len
= 10 * 1024 * 1024 # MB
878 qemu_img('create', '-f', iotests
.imgfmt
, test_img
,
879 str(TestGranularity
.image_len
))
880 qemu_io('-c', 'write 0 %d' % (self
.image_len
),
882 self
.vm
= iotests
.VM().add_drive(test_img
)
887 self
.assertTrue(iotests
.compare_images(test_img
, target_img
),
888 'target image does not match source after mirroring')
890 os
.remove(target_img
)
892 def test_granularity(self
):
893 self
.assert_no_active_block_jobs()
894 result
= self
.vm
.qmp('drive-mirror', device
='drive0',
895 sync
='full', target
=target_img
,
896 mode
='absolute-paths', granularity
=8192)
897 self
.assert_qmp(result
, 'return', {})
899 event
= self
.vm
.get_qmp_event(wait
=60.0)
900 while event
['event'] == 'JOB_STATUS_CHANGE':
901 self
.assert_qmp(event
, 'data/id', 'drive0')
902 event
= self
.vm
.get_qmp_event(wait
=60.0)
904 # Failures will manifest as COMPLETED/ERROR.
905 self
.assert_qmp(event
, 'event', 'BLOCK_JOB_READY')
906 self
.complete_and_wait(drive
='drive0', wait_ready
=False)
907 self
.assert_no_active_block_jobs()
909 class TestRepairQuorum(iotests
.QMPTestCase
):
910 """ This class test quorum file repair using drive-mirror.
911 It's mostly a fork of TestSingleDrive """
912 image_len
= 1 * 1024 * 1024 # MB
913 IMAGES
= [ quorum_img1
, quorum_img2
, quorum_img3
]
915 @iotests.skip_if_unsupported(['quorum'])
917 self
.vm
= iotests
.VM()
919 if iotests
.qemu_default_machine
== 'pc':
920 self
.vm
.add_drive(None, 'media=cdrom', 'ide')
922 # Add each individual quorum images
923 for i
in self
.IMAGES
:
924 qemu_img('create', '-f', iotests
.imgfmt
, i
,
926 # Assign a node name to each quorum image in order to manipulate
928 opts
= "node-name=img%i" % self
.IMAGES
.index(i
)
929 opts
+= ',driver=%s' % iotests
.imgfmt
930 opts
+= ',file.driver=file'
931 opts
+= ',file.filename=%s' % i
932 self
.vm
= self
.vm
.add_blockdev(opts
)
936 #assemble the quorum block device from the individual files
937 args
= { "driver": "quorum", "node-name": "quorum0",
938 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
939 result
= self
.vm
.qmp("blockdev-add", **args
)
940 self
.assert_qmp(result
, 'return', {})
945 for i
in self
.IMAGES
+ [ quorum_repair_img
, quorum_snapshot_file
,
947 # Do a try/except because the test may have deleted some images
953 def test_complete(self
):
954 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
955 sync
='full', node_name
="repair0", replaces
="img1",
956 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
957 self
.assert_qmp(result
, 'return', {})
959 self
.complete_and_wait(drive
="job0")
960 self
.assert_has_block_node("repair0", quorum_repair_img
)
961 self
.vm
.assert_block_path('quorum0', '/children.1', 'repair0')
963 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
964 'target image does not match source after mirroring')
966 def test_cancel(self
):
967 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
968 sync
='full', node_name
="repair0", replaces
="img1",
969 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
970 self
.assert_qmp(result
, 'return', {})
972 self
.cancel_and_wait(drive
="job0", force
=True)
973 # here we check that the last registered quorum file has not been
974 # swapped out and unref
975 self
.assert_has_block_node(None, quorum_img3
)
977 def test_cancel_after_ready(self
):
978 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
979 sync
='full', node_name
="repair0", replaces
="img1",
980 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
981 self
.assert_qmp(result
, 'return', {})
983 self
.wait_ready_and_cancel(drive
="job0")
984 # here we check that the last registered quorum file has not been
985 # swapped out and unref
986 self
.assert_has_block_node(None, quorum_img3
)
988 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
989 'target image does not match source after mirroring')
991 def test_pause(self
):
992 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
993 sync
='full', node_name
="repair0", replaces
="img1",
994 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
995 self
.assert_qmp(result
, 'return', {})
997 self
.pause_job('job0')
999 result
= self
.vm
.qmp('query-block-jobs')
1000 offset
= self
.dictpath(result
, 'return[0]/offset')
1003 result
= self
.vm
.qmp('query-block-jobs')
1004 self
.assert_qmp(result
, 'return[0]/offset', offset
)
1006 result
= self
.vm
.qmp('block-job-resume', device
='job0')
1007 self
.assert_qmp(result
, 'return', {})
1009 self
.complete_and_wait(drive
="job0")
1011 self
.assertTrue(iotests
.compare_images(quorum_img2
, quorum_repair_img
),
1012 'target image does not match source after mirroring')
1014 def test_medium_not_found(self
):
1015 if iotests
.qemu_default_machine
!= 'pc':
1018 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='drive0', # CD-ROM
1020 node_name
='repair0',
1022 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1023 self
.assert_qmp(result
, 'error/class', 'GenericError')
1025 def test_image_not_found(self
):
1026 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
1027 sync
='full', node_name
='repair0', replaces
='img1',
1028 mode
='existing', target
=quorum_repair_img
,
1029 format
=iotests
.imgfmt
)
1030 self
.assert_qmp(result
, 'error/class', 'GenericError')
1032 def test_device_not_found(self
):
1033 result
= self
.vm
.qmp('drive-mirror', job_id
='job0',
1034 device
='nonexistent', sync
='full',
1035 node_name
='repair0',
1037 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1038 self
.assert_qmp(result
, 'error/class', 'GenericError')
1040 def test_wrong_sync_mode(self
):
1041 result
= self
.vm
.qmp('drive-mirror', device
='quorum0', job_id
='job0',
1042 node_name
='repair0',
1044 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1045 self
.assert_qmp(result
, 'error/class', 'GenericError')
1047 def test_no_node_name(self
):
1048 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
1049 sync
='full', replaces
='img1',
1050 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1051 self
.assert_qmp(result
, 'error/class', 'GenericError')
1053 def test_nonexistent_replaces(self
):
1054 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
1055 sync
='full', node_name
='repair0', replaces
='img77',
1056 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1057 self
.assert_qmp(result
, 'error/class', 'GenericError')
1059 def test_after_a_quorum_snapshot(self
):
1060 result
= self
.vm
.qmp('blockdev-snapshot-sync', node_name
='img1',
1061 snapshot_file
=quorum_snapshot_file
,
1062 snapshot_node_name
="snap1");
1064 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
1065 sync
='full', node_name
='repair0', replaces
="img1",
1066 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1067 self
.assert_qmp(result
, 'error/class', 'GenericError')
1069 result
= self
.vm
.qmp('drive-mirror', job_id
='job0', device
='quorum0',
1070 sync
='full', node_name
='repair0', replaces
="snap1",
1071 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1072 self
.assert_qmp(result
, 'return', {})
1074 self
.complete_and_wait('job0')
1075 self
.assert_has_block_node("repair0", quorum_repair_img
)
1076 self
.vm
.assert_block_path('quorum0', '/children.1', 'repair0')
1078 def test_with_other_parent(self
):
1080 Check that we cannot replace a Quorum child when it has other
1083 result
= self
.vm
.qmp('nbd-server-start',
1086 'data': {'path': nbd_sock_path
}
1088 self
.assert_qmp(result
, 'return', {})
1090 result
= self
.vm
.qmp('nbd-server-add', device
='img1')
1091 self
.assert_qmp(result
, 'return', {})
1093 result
= self
.vm
.qmp('drive-mirror', job_id
='mirror', device
='quorum0',
1094 sync
='full', node_name
='repair0', replaces
='img1',
1095 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1096 self
.assert_qmp(result
, 'error/desc',
1097 "Cannot replace 'img1' by a node mirrored from "
1098 "'quorum0', because it cannot be guaranteed that doing "
1099 "so would not lead to an abrupt change of visible data")
1101 def test_with_other_parents_after_mirror_start(self
):
1103 The same as test_with_other_parent(), but add the NBD server
1104 only when the mirror job is already running.
1106 result
= self
.vm
.qmp('nbd-server-start',
1109 'data': {'path': nbd_sock_path
}
1111 self
.assert_qmp(result
, 'return', {})
1113 result
= self
.vm
.qmp('drive-mirror', job_id
='mirror', device
='quorum0',
1114 sync
='full', node_name
='repair0', replaces
='img1',
1115 target
=quorum_repair_img
, format
=iotests
.imgfmt
)
1116 self
.assert_qmp(result
, 'return', {})
1118 result
= self
.vm
.qmp('nbd-server-add', device
='img1')
1119 self
.assert_qmp(result
, 'return', {})
1121 # The full error message goes to stderr, we will check it later
1122 self
.complete_and_wait('mirror',
1123 completion_error
='Operation not permitted')
1125 # Should not have been replaced
1126 self
.vm
.assert_block_path('quorum0', '/children.1', 'img1')
1128 # Check the full error message now
1130 log
= self
.vm
.get_log()
1131 log
= re
.sub(r
'^\[I \d+\.\d+\] OPENED\n', '', log
)
1132 log
= re
.sub(r
'^Formatting.*\n', '', log
)
1133 log
= re
.sub(r
'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log
)
1134 log
= re
.sub(r
'^%s: ' % os
.path
.basename(iotests
.qemu_prog
), '', log
)
1136 self
.assertEqual(log
,
1137 "Can no longer replace 'img1' by 'repair0', because " +
1138 "it can no longer be guaranteed that doing so would " +
1139 "not lead to an abrupt change of visible data")
1142 # Test mirroring with a source that does not have any parents (not even a
1144 class TestOrphanedSource(iotests
.QMPTestCase
):
1146 blk0
= { 'node-name': 'src',
1147 'driver': 'null-co' }
1149 blk1
= { 'node-name': 'dest',
1150 'driver': 'null-co' }
1152 blk2
= { 'node-name': 'dest-ro',
1153 'driver': 'null-co',
1156 self
.vm
= iotests
.VM()
1157 self
.vm
.add_blockdev(self
.vm
.qmp_to_opts(blk0
))
1158 self
.vm
.add_blockdev(self
.vm
.qmp_to_opts(blk1
))
1159 self
.vm
.add_blockdev(self
.vm
.qmp_to_opts(blk2
))
1165 def test_no_job_id(self
):
1166 self
.assert_no_active_block_jobs()
1168 result
= self
.vm
.qmp('blockdev-mirror', device
='src', sync
='full',
1170 self
.assert_qmp(result
, 'error/class', 'GenericError')
1172 def test_success(self
):
1173 self
.assert_no_active_block_jobs()
1175 result
= self
.vm
.qmp('blockdev-mirror', job_id
='job', device
='src',
1176 sync
='full', target
='dest')
1177 self
.assert_qmp(result
, 'return', {})
1179 self
.complete_and_wait('job')
1181 def test_failing_permissions(self
):
1182 self
.assert_no_active_block_jobs()
1184 result
= self
.vm
.qmp('blockdev-mirror', device
='src', sync
='full',
1186 self
.assert_qmp(result
, 'error/class', 'GenericError')
1188 def test_failing_permission_in_complete(self
):
1189 self
.assert_no_active_block_jobs()
1191 # Unshare consistent-read on the target
1192 # (The mirror job does not care)
1193 result
= self
.vm
.qmp('blockdev-add',
1195 node_name
='dest-perm',
1197 unshare_child_perms
=['consistent-read'])
1198 self
.assert_qmp(result
, 'return', {})
1200 result
= self
.vm
.qmp('blockdev-mirror', job_id
='job', device
='src',
1201 sync
='full', target
='dest',
1202 filter_node_name
='mirror-filter')
1203 self
.assert_qmp(result
, 'return', {})
1205 # Require consistent-read on the source
1206 # (We can only add this node once the job has started, or it
1207 # will complain that it does not want to run on non-root nodes)
1208 result
= self
.vm
.qmp('blockdev-add',
1210 node_name
='src-perm',
1212 take_child_perms
=['consistent-read'])
1213 self
.assert_qmp(result
, 'return', {})
1215 # While completing, mirror will attempt to replace src by
1216 # dest, which must fail because src-perm requires
1217 # consistent-read but dest-perm does not share it; thus
1218 # aborting the job when it is supposed to complete
1219 self
.complete_and_wait('job',
1220 completion_error
='Operation not permitted')
1222 # Assert that all of our nodes are still there (except for the
1223 # mirror filter, which should be gone despite the failure)
1224 nodes
= self
.vm
.qmp('query-named-block-nodes')['return']
1225 nodes
= [node
['node-name'] for node
in nodes
]
1227 for expect
in ('src', 'src-perm', 'dest', 'dest-perm'):
1228 self
.assertTrue(expect
in nodes
, '%s disappeared' % expect
)
1229 self
.assertFalse('mirror-filter' in nodes
,
1230 'Mirror filter node did not disappear')
1232 # Test cases for @replaces that do not necessarily involve Quorum
1233 class TestReplaces(iotests
.QMPTestCase
):
1234 # Each of these test cases needs their own block graph, so do not
1235 # create any nodes here
1237 self
.vm
= iotests
.VM()
1242 for img
in (test_img
, target_img
):
1248 @iotests.skip_if_unsupported(['copy-on-read'])
1249 def test_replace_filter(self
):
1251 Check that we can replace filter nodes.
1253 result
= self
.vm
.qmp('blockdev-add', **{
1254 'driver': 'copy-on-read',
1255 'node-name': 'filter0',
1257 'driver': 'copy-on-read',
1258 'node-name': 'filter1',
1264 self
.assert_qmp(result
, 'return', {})
1266 result
= self
.vm
.qmp('blockdev-add',
1267 node_name
='target', driver
='null-co')
1268 self
.assert_qmp(result
, 'return', {})
1270 result
= self
.vm
.qmp('blockdev-mirror', job_id
='mirror', device
='filter0',
1271 target
='target', sync
='full', replaces
='filter1')
1272 self
.assert_qmp(result
, 'return', {})
1274 self
.complete_and_wait('mirror')
1276 self
.vm
.assert_block_path('filter0', '/file', 'target')
1278 if __name__
== '__main__':
1279 iotests
.main(supported_fmts
=['qcow2', 'qed'],
1280 supported_protocols
=['file'],
1281 supported_platforms
=['linux', 'freebsd', 'netbsd', 'openbsd'])