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