These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / tests / qemu-iotests / 041
1 #!/usr/bin/env python
2 #
3 # Tests for image mirroring.
4 #
5 # Copyright (C) 2012 Red Hat, Inc.
6 #
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.
11 #
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.
16 #
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/>.
19 #
20
21 import time
22 import os
23 import iotests
24 from iotests import qemu_img, qemu_io
25
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')
30
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')
36
37 class TestSingleDrive(iotests.QMPTestCase):
38     image_len = 1 * 1024 * 1024 # MB
39     qmp_cmd = 'drive-mirror'
40     qmp_target = target_img
41     not_found_error = 'DeviceNotFound'
42
43     def setUp(self):
44         iotests.create_image(backing_img, self.image_len)
45         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
46         self.vm = iotests.VM().add_drive(test_img)
47         if iotests.qemu_default_machine == 'pc':
48             self.vm.add_drive(None, 'media=cdrom', 'ide')
49         self.vm.launch()
50
51     def tearDown(self):
52         self.vm.shutdown()
53         os.remove(test_img)
54         os.remove(backing_img)
55         try:
56             os.remove(target_img)
57         except OSError:
58             pass
59
60     def test_complete(self):
61         self.assert_no_active_block_jobs()
62
63         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
64                              target=self.qmp_target)
65         self.assert_qmp(result, 'return', {})
66
67         self.complete_and_wait()
68         result = self.vm.qmp('query-block')
69         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
70         self.vm.shutdown()
71         self.assertTrue(iotests.compare_images(test_img, target_img),
72                         'target image does not match source after mirroring')
73
74     def test_cancel(self):
75         self.assert_no_active_block_jobs()
76
77         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
78                              target=self.qmp_target)
79         self.assert_qmp(result, 'return', {})
80
81         self.cancel_and_wait(force=True)
82         result = self.vm.qmp('query-block')
83         self.assert_qmp(result, 'return[0]/inserted/file', test_img)
84         self.vm.shutdown()
85
86     def test_cancel_after_ready(self):
87         self.assert_no_active_block_jobs()
88
89         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
90                              target=self.qmp_target)
91         self.assert_qmp(result, 'return', {})
92
93         self.wait_ready_and_cancel()
94         result = self.vm.qmp('query-block')
95         self.assert_qmp(result, 'return[0]/inserted/file', test_img)
96         self.vm.shutdown()
97         self.assertTrue(iotests.compare_images(test_img, target_img),
98                         'target image does not match source after mirroring')
99
100     def test_pause(self):
101         self.assert_no_active_block_jobs()
102
103         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
104                              target=self.qmp_target)
105         self.assert_qmp(result, 'return', {})
106
107         result = self.vm.qmp('block-job-pause', device='drive0')
108         self.assert_qmp(result, 'return', {})
109
110         time.sleep(1)
111         result = self.vm.qmp('query-block-jobs')
112         offset = self.dictpath(result, 'return[0]/offset')
113
114         time.sleep(1)
115         result = self.vm.qmp('query-block-jobs')
116         self.assert_qmp(result, 'return[0]/offset', offset)
117
118         result = self.vm.qmp('block-job-resume', device='drive0')
119         self.assert_qmp(result, 'return', {})
120
121         self.complete_and_wait()
122         self.vm.shutdown()
123         self.assertTrue(iotests.compare_images(test_img, target_img),
124                         'target image does not match source after mirroring')
125
126     def test_small_buffer(self):
127         self.assert_no_active_block_jobs()
128
129         # A small buffer is rounded up automatically
130         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
131                              buf_size=4096, target=self.qmp_target)
132         self.assert_qmp(result, 'return', {})
133
134         self.complete_and_wait()
135         result = self.vm.qmp('query-block')
136         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
137         self.vm.shutdown()
138         self.assertTrue(iotests.compare_images(test_img, target_img),
139                         'target image does not match source after mirroring')
140
141     def test_small_buffer2(self):
142         self.assert_no_active_block_jobs()
143
144         qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
145                         % (self.image_len, self.image_len), target_img)
146         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
147                              buf_size=65536, mode='existing', target=self.qmp_target)
148         self.assert_qmp(result, 'return', {})
149
150         self.complete_and_wait()
151         result = self.vm.qmp('query-block')
152         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
153         self.vm.shutdown()
154         self.assertTrue(iotests.compare_images(test_img, target_img),
155                         'target image does not match source after mirroring')
156
157     def test_large_cluster(self):
158         self.assert_no_active_block_jobs()
159
160         qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
161                         % (self.image_len, backing_img), target_img)
162         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
163                              mode='existing', target=self.qmp_target)
164         self.assert_qmp(result, 'return', {})
165
166         self.complete_and_wait()
167         result = self.vm.qmp('query-block')
168         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
169         self.vm.shutdown()
170         self.assertTrue(iotests.compare_images(test_img, target_img),
171                         'target image does not match source after mirroring')
172
173     def test_medium_not_found(self):
174         if iotests.qemu_default_machine != 'pc':
175             return
176
177         result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full',
178                              target=self.qmp_target)
179         self.assert_qmp(result, 'error/class', self.not_found_error)
180
181     def test_image_not_found(self):
182         result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
183                              mode='existing', target=self.qmp_target)
184         self.assert_qmp(result, 'error/class', 'GenericError')
185
186     def test_device_not_found(self):
187         result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full',
188                              target=self.qmp_target)
189         self.assert_qmp(result, 'error/class', self.not_found_error)
190
191 class TestSingleBlockdev(TestSingleDrive):
192     qmp_cmd = 'blockdev-mirror'
193     qmp_target = 'node1'
194     not_found_error = 'GenericError'
195
196     def setUp(self):
197         TestSingleDrive.setUp(self)
198         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
199         args = {'options':
200                     {'driver': iotests.imgfmt,
201                      'node-name': self.qmp_target,
202                      'file': { 'filename': target_img, 'driver': 'file' } } }
203         result = self.vm.qmp("blockdev-add", **args)
204         self.assert_qmp(result, 'return', {})
205
206     test_large_cluster = None
207     test_image_not_found = None
208     test_small_buffer2 = None
209
210 class TestBlockdevAttached(iotests.QMPTestCase):
211     image_len = 1 * 1024 * 1024 # MB
212
213     def setUp(self):
214         iotests.create_image(backing_img, self.image_len)
215         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
216         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
217         self.vm = iotests.VM().add_drive(test_img)
218         self.vm.launch()
219
220     def tearDown(self):
221         self.vm.shutdown()
222         os.remove(test_img)
223         os.remove(target_img)
224
225     def test_blockdev_attached(self):
226         self.assert_no_active_block_jobs()
227         args = {'options':
228                     {'driver': iotests.imgfmt,
229                      'id': 'drive1',
230                      'file': { 'filename': target_img, 'driver': 'file' } } }
231         result = self.vm.qmp("blockdev-add", **args)
232         self.assert_qmp(result, 'return', {})
233         result = self.vm.qmp('blockdev-mirror', device='drive0', sync='full',
234                              target='drive1')
235         self.assert_qmp(result, 'error/class', 'GenericError')
236
237 class TestSingleDriveZeroLength(TestSingleDrive):
238     image_len = 0
239     test_small_buffer2 = None
240     test_large_cluster = None
241
242 class TestSingleBlockdevZeroLength(TestSingleBlockdev):
243     image_len = 0
244
245 class TestSingleDriveUnalignedLength(TestSingleDrive):
246     image_len = 1025 * 1024
247     test_small_buffer2 = None
248     test_large_cluster = None
249
250 class TestSingleBlockdevUnalignedLength(TestSingleBlockdev):
251     image_len = 1025 * 1024
252
253 class TestMirrorNoBacking(iotests.QMPTestCase):
254     image_len = 2 * 1024 * 1024 # MB
255
256     def setUp(self):
257         iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
258         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
259         self.vm = iotests.VM().add_drive(test_img)
260         self.vm.launch()
261
262     def tearDown(self):
263         self.vm.shutdown()
264         os.remove(test_img)
265         os.remove(backing_img)
266         try:
267             os.remove(target_backing_img)
268         except:
269             pass
270         os.remove(target_img)
271
272     def test_complete(self):
273         self.assert_no_active_block_jobs()
274
275         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
276         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
277                              mode='existing', target=target_img)
278         self.assert_qmp(result, 'return', {})
279
280         self.complete_and_wait()
281         result = self.vm.qmp('query-block')
282         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
283         self.vm.shutdown()
284         self.assertTrue(iotests.compare_images(test_img, target_img),
285                         'target image does not match source after mirroring')
286
287     def test_cancel(self):
288         self.assert_no_active_block_jobs()
289
290         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
291         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
292                              mode='existing', target=target_img)
293         self.assert_qmp(result, 'return', {})
294
295         self.wait_ready_and_cancel()
296         result = self.vm.qmp('query-block')
297         self.assert_qmp(result, 'return[0]/inserted/file', test_img)
298         self.vm.shutdown()
299         self.assertTrue(iotests.compare_images(test_img, target_img),
300                         'target image does not match source after mirroring')
301
302     def test_large_cluster(self):
303         self.assert_no_active_block_jobs()
304
305         # qemu-img create fails if the image is not there
306         qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
307                         %(TestMirrorNoBacking.image_len), target_backing_img)
308         qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
309                         % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
310
311         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
312                              mode='existing', target=target_img)
313         self.assert_qmp(result, 'return', {})
314
315         self.complete_and_wait()
316         result = self.vm.qmp('query-block')
317         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
318         self.vm.shutdown()
319         self.assertTrue(iotests.compare_images(test_img, target_img),
320                         'target image does not match source after mirroring')
321
322 class TestMirrorResized(iotests.QMPTestCase):
323     backing_len = 1 * 1024 * 1024 # MB
324     image_len = 2 * 1024 * 1024 # MB
325
326     def setUp(self):
327         iotests.create_image(backing_img, TestMirrorResized.backing_len)
328         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
329         qemu_img('resize', test_img, '2M')
330         self.vm = iotests.VM().add_drive(test_img)
331         self.vm.launch()
332
333     def tearDown(self):
334         self.vm.shutdown()
335         os.remove(test_img)
336         os.remove(backing_img)
337         try:
338             os.remove(target_img)
339         except OSError:
340             pass
341
342     def test_complete_top(self):
343         self.assert_no_active_block_jobs()
344
345         result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
346                              target=target_img)
347         self.assert_qmp(result, 'return', {})
348
349         self.complete_and_wait()
350         result = self.vm.qmp('query-block')
351         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
352         self.vm.shutdown()
353         self.assertTrue(iotests.compare_images(test_img, target_img),
354                         'target image does not match source after mirroring')
355
356     def test_complete_full(self):
357         self.assert_no_active_block_jobs()
358
359         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
360                              target=target_img)
361         self.assert_qmp(result, 'return', {})
362
363         self.complete_and_wait()
364         result = self.vm.qmp('query-block')
365         self.assert_qmp(result, 'return[0]/inserted/file', target_img)
366         self.vm.shutdown()
367         self.assertTrue(iotests.compare_images(test_img, target_img),
368                         'target image does not match source after mirroring')
369
370 class TestReadErrors(iotests.QMPTestCase):
371     image_len = 2 * 1024 * 1024 # MB
372
373     # this should be a multiple of twice the default granularity
374     # so that we hit this offset first in state 1
375     MIRROR_GRANULARITY = 1024 * 1024
376
377     def create_blkdebug_file(self, name, event, errno):
378         file = open(name, 'w')
379         file.write('''
380 [inject-error]
381 state = "1"
382 event = "%s"
383 errno = "%d"
384 immediately = "off"
385 once = "on"
386 sector = "%d"
387
388 [set-state]
389 state = "1"
390 event = "%s"
391 new_state = "2"
392
393 [set-state]
394 state = "2"
395 event = "%s"
396 new_state = "1"
397 ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
398         file.close()
399
400     def setUp(self):
401         self.blkdebug_file = backing_img + ".blkdebug"
402         iotests.create_image(backing_img, TestReadErrors.image_len)
403         self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
404         qemu_img('create', '-f', iotests.imgfmt,
405                  '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
406                        % (self.blkdebug_file, backing_img),
407                  test_img)
408         # Write something for tests that use sync='top'
409         qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
410                         test_img)
411         self.vm = iotests.VM().add_drive(test_img)
412         self.vm.launch()
413
414     def tearDown(self):
415         self.vm.shutdown()
416         os.remove(test_img)
417         os.remove(backing_img)
418         os.remove(self.blkdebug_file)
419
420     def test_report_read(self):
421         self.assert_no_active_block_jobs()
422
423         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
424                              target=target_img)
425         self.assert_qmp(result, 'return', {})
426
427         completed = False
428         error = False
429         while not completed:
430             for event in self.vm.get_qmp_events(wait=True):
431                 if event['event'] == 'BLOCK_JOB_ERROR':
432                     self.assert_qmp(event, 'data/device', 'drive0')
433                     self.assert_qmp(event, 'data/operation', 'read')
434                     error = True
435                 elif event['event'] == 'BLOCK_JOB_READY':
436                     self.assertTrue(False, 'job completed unexpectedly')
437                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
438                     self.assertTrue(error, 'job completed unexpectedly')
439                     self.assert_qmp(event, 'data/type', 'mirror')
440                     self.assert_qmp(event, 'data/device', 'drive0')
441                     self.assert_qmp(event, 'data/error', 'Input/output error')
442                     completed = True
443
444         self.assert_no_active_block_jobs()
445         self.vm.shutdown()
446
447     def test_ignore_read(self):
448         self.assert_no_active_block_jobs()
449
450         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
451                              target=target_img, on_source_error='ignore')
452         self.assert_qmp(result, 'return', {})
453
454         event = self.vm.get_qmp_event(wait=True)
455         self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
456         self.assert_qmp(event, 'data/device', 'drive0')
457         self.assert_qmp(event, 'data/operation', 'read')
458         result = self.vm.qmp('query-block-jobs')
459         self.assert_qmp(result, 'return[0]/paused', False)
460         self.complete_and_wait()
461         self.vm.shutdown()
462
463     def test_large_cluster(self):
464         self.assert_no_active_block_jobs()
465
466         # Test COW into the target image.  The first half of the
467         # cluster at MIRROR_GRANULARITY has to be copied from
468         # backing_img, even though sync='top'.
469         qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img)
470         result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
471                              on_source_error='ignore',
472                              mode='existing', target=target_img)
473         self.assert_qmp(result, 'return', {})
474
475         event = self.vm.get_qmp_event(wait=True)
476         self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
477         self.assert_qmp(event, 'data/device', 'drive0')
478         self.assert_qmp(event, 'data/operation', 'read')
479         result = self.vm.qmp('query-block-jobs')
480         self.assert_qmp(result, 'return[0]/paused', False)
481         self.complete_and_wait()
482         self.vm.shutdown()
483
484         # Detach blkdebug to compare images successfully
485         qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
486         self.assertTrue(iotests.compare_images(test_img, target_img),
487                         'target image does not match source after mirroring')
488
489     def test_stop_read(self):
490         self.assert_no_active_block_jobs()
491
492         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
493                              target=target_img, on_source_error='stop')
494         self.assert_qmp(result, 'return', {})
495
496         error = False
497         ready = False
498         while not ready:
499             for event in self.vm.get_qmp_events(wait=True):
500                 if event['event'] == 'BLOCK_JOB_ERROR':
501                     self.assert_qmp(event, 'data/device', 'drive0')
502                     self.assert_qmp(event, 'data/operation', 'read')
503
504                     result = self.vm.qmp('query-block-jobs')
505                     self.assert_qmp(result, 'return[0]/paused', True)
506                     self.assert_qmp(result, 'return[0]/io-status', 'failed')
507
508                     result = self.vm.qmp('block-job-resume', device='drive0')
509                     self.assert_qmp(result, 'return', {})
510                     error = True
511                 elif event['event'] == 'BLOCK_JOB_READY':
512                     self.assertTrue(error, 'job completed unexpectedly')
513                     self.assert_qmp(event, 'data/device', 'drive0')
514                     ready = True
515
516         result = self.vm.qmp('query-block-jobs')
517         self.assert_qmp(result, 'return[0]/paused', False)
518         self.assert_qmp(result, 'return[0]/io-status', 'ok')
519
520         self.complete_and_wait(wait_ready=False)
521         self.assert_no_active_block_jobs()
522         self.vm.shutdown()
523
524 class TestWriteErrors(iotests.QMPTestCase):
525     image_len = 2 * 1024 * 1024 # MB
526
527     # this should be a multiple of twice the default granularity
528     # so that we hit this offset first in state 1
529     MIRROR_GRANULARITY = 1024 * 1024
530
531     def create_blkdebug_file(self, name, event, errno):
532         file = open(name, 'w')
533         file.write('''
534 [inject-error]
535 state = "1"
536 event = "%s"
537 errno = "%d"
538 immediately = "off"
539 once = "on"
540 sector = "%d"
541
542 [set-state]
543 state = "1"
544 event = "%s"
545 new_state = "2"
546
547 [set-state]
548 state = "2"
549 event = "%s"
550 new_state = "1"
551 ''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
552         file.close()
553
554     def setUp(self):
555         self.blkdebug_file = target_img + ".blkdebug"
556         iotests.create_image(backing_img, TestWriteErrors.image_len)
557         self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
558         qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
559         self.vm = iotests.VM().add_drive(test_img)
560         self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
561         qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
562         self.vm.launch()
563
564     def tearDown(self):
565         self.vm.shutdown()
566         os.remove(test_img)
567         os.remove(backing_img)
568         os.remove(self.blkdebug_file)
569
570     def test_report_write(self):
571         self.assert_no_active_block_jobs()
572
573         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
574                              mode='existing', target=self.target_img)
575         self.assert_qmp(result, 'return', {})
576
577         completed = False
578         error = False
579         while not completed:
580             for event in self.vm.get_qmp_events(wait=True):
581                 if event['event'] == 'BLOCK_JOB_ERROR':
582                     self.assert_qmp(event, 'data/device', 'drive0')
583                     self.assert_qmp(event, 'data/operation', 'write')
584                     error = True
585                 elif event['event'] == 'BLOCK_JOB_READY':
586                     self.assertTrue(False, 'job completed unexpectedly')
587                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
588                     self.assertTrue(error, 'job completed unexpectedly')
589                     self.assert_qmp(event, 'data/type', 'mirror')
590                     self.assert_qmp(event, 'data/device', 'drive0')
591                     self.assert_qmp(event, 'data/error', 'Input/output error')
592                     completed = True
593
594         self.assert_no_active_block_jobs()
595         self.vm.shutdown()
596
597     def test_ignore_write(self):
598         self.assert_no_active_block_jobs()
599
600         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
601                              mode='existing', target=self.target_img,
602                              on_target_error='ignore')
603         self.assert_qmp(result, 'return', {})
604
605         event = self.vm.get_qmp_event(wait=True)
606         self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
607         self.assert_qmp(event, 'data/device', 'drive0')
608         self.assert_qmp(event, 'data/operation', 'write')
609         result = self.vm.qmp('query-block-jobs')
610         self.assert_qmp(result, 'return[0]/paused', False)
611         self.complete_and_wait()
612         self.vm.shutdown()
613
614     def test_stop_write(self):
615         self.assert_no_active_block_jobs()
616
617         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
618                              mode='existing', target=self.target_img,
619                              on_target_error='stop')
620         self.assert_qmp(result, 'return', {})
621
622         error = False
623         ready = False
624         while not ready:
625             for event in self.vm.get_qmp_events(wait=True):
626                 if event['event'] == 'BLOCK_JOB_ERROR':
627                     self.assert_qmp(event, 'data/device', 'drive0')
628                     self.assert_qmp(event, 'data/operation', 'write')
629
630                     result = self.vm.qmp('query-block-jobs')
631                     self.assert_qmp(result, 'return[0]/paused', True)
632                     self.assert_qmp(result, 'return[0]/io-status', 'failed')
633
634                     result = self.vm.qmp('block-job-resume', device='drive0')
635                     self.assert_qmp(result, 'return', {})
636
637                     result = self.vm.qmp('query-block-jobs')
638                     self.assert_qmp(result, 'return[0]/paused', False)
639                     self.assert_qmp(result, 'return[0]/io-status', 'ok')
640                     error = True
641                 elif event['event'] == 'BLOCK_JOB_READY':
642                     self.assertTrue(error, 'job completed unexpectedly')
643                     self.assert_qmp(event, 'data/device', 'drive0')
644                     ready = True
645
646         self.complete_and_wait(wait_ready=False)
647         self.assert_no_active_block_jobs()
648         self.vm.shutdown()
649
650 class TestSetSpeed(iotests.QMPTestCase):
651     image_len = 80 * 1024 * 1024 # MB
652
653     def setUp(self):
654         qemu_img('create', backing_img, str(TestSetSpeed.image_len))
655         qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
656         self.vm = iotests.VM().add_drive(test_img)
657         self.vm.launch()
658
659     def tearDown(self):
660         self.vm.shutdown()
661         os.remove(test_img)
662         os.remove(backing_img)
663         os.remove(target_img)
664
665     def test_set_speed(self):
666         self.assert_no_active_block_jobs()
667
668         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
669                              target=target_img)
670         self.assert_qmp(result, 'return', {})
671
672         # Default speed is 0
673         result = self.vm.qmp('query-block-jobs')
674         self.assert_qmp(result, 'return[0]/device', 'drive0')
675         self.assert_qmp(result, 'return[0]/speed', 0)
676
677         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
678         self.assert_qmp(result, 'return', {})
679
680         # Ensure the speed we set was accepted
681         result = self.vm.qmp('query-block-jobs')
682         self.assert_qmp(result, 'return[0]/device', 'drive0')
683         self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
684
685         self.wait_ready_and_cancel()
686
687         # Check setting speed in drive-mirror works
688         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
689                              target=target_img, speed=4*1024*1024)
690         self.assert_qmp(result, 'return', {})
691
692         result = self.vm.qmp('query-block-jobs')
693         self.assert_qmp(result, 'return[0]/device', 'drive0')
694         self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
695
696         self.wait_ready_and_cancel()
697
698     def test_set_speed_invalid(self):
699         self.assert_no_active_block_jobs()
700
701         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
702                              target=target_img, speed=-1)
703         self.assert_qmp(result, 'error/class', 'GenericError')
704
705         self.assert_no_active_block_jobs()
706
707         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
708                              target=target_img)
709         self.assert_qmp(result, 'return', {})
710
711         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
712         self.assert_qmp(result, 'error/class', 'GenericError')
713
714         self.wait_ready_and_cancel()
715
716 class TestUnbackedSource(iotests.QMPTestCase):
717     image_len = 2 * 1024 * 1024 # MB
718
719     def setUp(self):
720         qemu_img('create', '-f', iotests.imgfmt, test_img,
721                  str(TestUnbackedSource.image_len))
722         self.vm = iotests.VM().add_drive(test_img)
723         self.vm.launch()
724
725     def tearDown(self):
726         self.vm.shutdown()
727         os.remove(test_img)
728         os.remove(target_img)
729
730     def test_absolute_paths_full(self):
731         self.assert_no_active_block_jobs()
732         result = self.vm.qmp('drive-mirror', device='drive0',
733                              sync='full', target=target_img,
734                              mode='absolute-paths')
735         self.assert_qmp(result, 'return', {})
736         self.complete_and_wait()
737         self.assert_no_active_block_jobs()
738
739     def test_absolute_paths_top(self):
740         self.assert_no_active_block_jobs()
741         result = self.vm.qmp('drive-mirror', device='drive0',
742                              sync='top', target=target_img,
743                              mode='absolute-paths')
744         self.assert_qmp(result, 'return', {})
745         self.complete_and_wait()
746         self.assert_no_active_block_jobs()
747
748     def test_absolute_paths_none(self):
749         self.assert_no_active_block_jobs()
750         result = self.vm.qmp('drive-mirror', device='drive0',
751                              sync='none', target=target_img,
752                              mode='absolute-paths')
753         self.assert_qmp(result, 'return', {})
754         self.complete_and_wait()
755         self.assert_no_active_block_jobs()
756
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 ]
762
763     def has_quorum(self):
764         return 'quorum' in iotests.qemu_img_pipe('--help')
765
766     def setUp(self):
767         self.vm = iotests.VM()
768
769         if iotests.qemu_default_machine == 'pc':
770             self.vm.add_drive(None, 'media=cdrom', 'ide')
771
772         # Add each individual quorum images
773         for i in self.IMAGES:
774             qemu_img('create', '-f', iotests.imgfmt, i,
775                      str(TestSingleDrive.image_len))
776             # Assign a node name to each quorum image in order to manipulate
777             # them
778             opts = "node-name=img%i" % self.IMAGES.index(i)
779             self.vm = self.vm.add_drive(i, opts)
780
781         self.vm.launch()
782
783         #assemble the quorum block device from the individual files
784         args = { "options" : { "driver": "quorum", "id": "quorum0",
785                  "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] } }
786         if self.has_quorum():
787             result = self.vm.qmp("blockdev-add", **args)
788             self.assert_qmp(result, 'return', {})
789
790
791     def tearDown(self):
792         self.vm.shutdown()
793         for i in self.IMAGES + [ quorum_repair_img ]:
794             # Do a try/except because the test may have deleted some images
795             try:
796                 os.remove(i)
797             except OSError:
798                 pass
799
800     def test_complete(self):
801         if not self.has_quorum():
802             return
803
804         self.assert_no_active_block_jobs()
805
806         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
807                              node_name="repair0",
808                              replaces="img1",
809                              target=quorum_repair_img, format=iotests.imgfmt)
810         self.assert_qmp(result, 'return', {})
811
812         self.complete_and_wait(drive="quorum0")
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
816         self.vm.shutdown()
817         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
818                         'target image does not match source after mirroring')
819
820     def test_cancel(self):
821         if not self.has_quorum():
822             return
823
824         self.assert_no_active_block_jobs()
825
826         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
827                              node_name="repair0",
828                              replaces="img1",
829                              target=quorum_repair_img, format=iotests.imgfmt)
830         self.assert_qmp(result, 'return', {})
831
832         self.cancel_and_wait(drive="quorum0", force=True)
833         # here we check that the last registered quorum file has not been
834         # swapped out and unref
835         self.assert_has_block_node(None, quorum_img3)
836         self.vm.shutdown()
837
838     def test_cancel_after_ready(self):
839         if not self.has_quorum():
840             return
841
842         self.assert_no_active_block_jobs()
843
844         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
845                              node_name="repair0",
846                              replaces="img1",
847                              target=quorum_repair_img, format=iotests.imgfmt)
848         self.assert_qmp(result, 'return', {})
849
850         self.wait_ready_and_cancel(drive="quorum0")
851         # here we check that the last registered quorum file has not been
852         # swapped out and unref
853         self.assert_has_block_node(None, quorum_img3)
854         self.vm.shutdown()
855         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
856                         'target image does not match source after mirroring')
857
858     def test_pause(self):
859         if not self.has_quorum():
860             return
861
862         self.assert_no_active_block_jobs()
863
864         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
865                              node_name="repair0",
866                              replaces="img1",
867                              target=quorum_repair_img, format=iotests.imgfmt)
868         self.assert_qmp(result, 'return', {})
869
870         result = self.vm.qmp('block-job-pause', device='quorum0')
871         self.assert_qmp(result, 'return', {})
872
873         time.sleep(1)
874         result = self.vm.qmp('query-block-jobs')
875         offset = self.dictpath(result, 'return[0]/offset')
876
877         time.sleep(1)
878         result = self.vm.qmp('query-block-jobs')
879         self.assert_qmp(result, 'return[0]/offset', offset)
880
881         result = self.vm.qmp('block-job-resume', device='quorum0')
882         self.assert_qmp(result, 'return', {})
883
884         self.complete_and_wait(drive="quorum0")
885         self.vm.shutdown()
886         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
887                         'target image does not match source after mirroring')
888
889     def test_medium_not_found(self):
890         if not self.has_quorum():
891             return
892
893         if iotests.qemu_default_machine != 'pc':
894             return
895
896         result = self.vm.qmp('drive-mirror', device='drive0', # CD-ROM
897                              sync='full',
898                              node_name='repair0',
899                              replaces='img1',
900                              target=quorum_repair_img, format=iotests.imgfmt)
901         self.assert_qmp(result, 'error/class', 'GenericError')
902
903     def test_image_not_found(self):
904         if not self.has_quorum():
905             return
906
907         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
908                              node_name='repair0',
909                              replaces='img1',
910                              mode='existing',
911                              target=quorum_repair_img, format=iotests.imgfmt)
912         self.assert_qmp(result, 'error/class', 'GenericError')
913
914     def test_device_not_found(self):
915         if not self.has_quorum():
916             return
917
918         result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
919                              node_name='repair0',
920                              replaces='img1',
921                              target=quorum_repair_img, format=iotests.imgfmt)
922         self.assert_qmp(result, 'error/class', 'DeviceNotFound')
923
924     def test_wrong_sync_mode(self):
925         if not self.has_quorum():
926             return
927
928         result = self.vm.qmp('drive-mirror', device='quorum0',
929                              node_name='repair0',
930                              replaces='img1',
931                              target=quorum_repair_img, format=iotests.imgfmt)
932         self.assert_qmp(result, 'error/class', 'GenericError')
933
934     def test_no_node_name(self):
935         if not self.has_quorum():
936             return
937
938         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
939                              replaces='img1',
940                              target=quorum_repair_img, format=iotests.imgfmt)
941         self.assert_qmp(result, 'error/class', 'GenericError')
942
943     def test_nonexistent_replaces(self):
944         if not self.has_quorum():
945             return
946
947         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
948                              node_name='repair0',
949                              replaces='img77',
950                              target=quorum_repair_img, format=iotests.imgfmt)
951         self.assert_qmp(result, 'error/class', 'GenericError')
952
953     def test_after_a_quorum_snapshot(self):
954         if not self.has_quorum():
955             return
956
957         result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
958                              snapshot_file=quorum_snapshot_file,
959                              snapshot_node_name="snap1");
960
961         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
962                              node_name='repair0',
963                              replaces="img1",
964                              target=quorum_repair_img, format=iotests.imgfmt)
965         self.assert_qmp(result, 'error/class', 'GenericError')
966
967         result = self.vm.qmp('drive-mirror', device='quorum0', sync='full',
968                              node_name='repair0',
969                              replaces="snap1",
970                              target=quorum_repair_img, format=iotests.imgfmt)
971         self.assert_qmp(result, 'return', {})
972
973         self.complete_and_wait(drive="quorum0")
974         self.assert_has_block_node("repair0", quorum_repair_img)
975         # TODO: a better test requiring some QEMU infrastructure will be added
976         #       to check that this file is really driven by quorum
977         self.vm.shutdown()
978
979 if __name__ == '__main__':
980     iotests.main(supported_fmts=['qcow2', 'qed'])