xref: /openbmc/qemu/tests/qemu-iotests/041 (revision 88f62c2b)
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
21import time
22import os
23import iotests
24from iotests import qemu_img, qemu_io
25
26backing_img = os.path.join(iotests.test_dir, 'backing.img')
27target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
28test_img = os.path.join(iotests.test_dir, 'test.img')
29target_img = os.path.join(iotests.test_dir, 'target.img')
30
31class ImageMirroringTestCase(iotests.QMPTestCase):
32    '''Abstract base class for image mirroring test cases'''
33
34    def wait_ready(self, drive='drive0'):
35        '''Wait until a block job BLOCK_JOB_READY event'''
36        ready = False
37        while not ready:
38            for event in self.vm.get_qmp_events(wait=True):
39                if event['event'] == 'BLOCK_JOB_READY':
40                    self.assert_qmp(event, 'data/type', 'mirror')
41                    self.assert_qmp(event, 'data/device', drive)
42                    ready = True
43
44    def wait_ready_and_cancel(self, drive='drive0'):
45        self.wait_ready(drive)
46        event = self.cancel_and_wait()
47        self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED')
48        self.assert_qmp(event, 'data/type', 'mirror')
49        self.assert_qmp(event, 'data/offset', self.image_len)
50        self.assert_qmp(event, 'data/len', self.image_len)
51
52    def complete_and_wait(self, drive='drive0', wait_ready=True):
53        '''Complete a block job and wait for it to finish'''
54        if wait_ready:
55            self.wait_ready()
56
57        result = self.vm.qmp('block-job-complete', device=drive)
58        self.assert_qmp(result, 'return', {})
59
60        completed = False
61        while not completed:
62            for event in self.vm.get_qmp_events(wait=True):
63                if event['event'] == 'BLOCK_JOB_COMPLETED':
64                    self.assert_qmp(event, 'data/type', 'mirror')
65                    self.assert_qmp(event, 'data/device', drive)
66                    self.assert_qmp_absent(event, 'data/error')
67                    self.assert_qmp(event, 'data/offset', self.image_len)
68                    self.assert_qmp(event, 'data/len', self.image_len)
69                    completed = True
70
71        self.assert_no_active_block_jobs()
72
73class TestSingleDrive(ImageMirroringTestCase):
74    image_len = 1 * 1024 * 1024 # MB
75
76    def setUp(self):
77        iotests.create_image(backing_img, TestSingleDrive.image_len)
78        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
79        self.vm = iotests.VM().add_drive(test_img)
80        self.vm.launch()
81
82    def tearDown(self):
83        self.vm.shutdown()
84        os.remove(test_img)
85        os.remove(backing_img)
86        try:
87            os.remove(target_img)
88        except OSError:
89            pass
90
91    def test_complete(self):
92        self.assert_no_active_block_jobs()
93
94        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
95                             target=target_img)
96        self.assert_qmp(result, 'return', {})
97
98        self.complete_and_wait()
99        result = self.vm.qmp('query-block')
100        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
101        self.vm.shutdown()
102        self.assertTrue(iotests.compare_images(test_img, target_img),
103                        'target image does not match source after mirroring')
104
105    def test_cancel(self):
106        self.assert_no_active_block_jobs()
107
108        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
109                             target=target_img)
110        self.assert_qmp(result, 'return', {})
111
112        self.cancel_and_wait(force=True)
113        result = self.vm.qmp('query-block')
114        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
115        self.vm.shutdown()
116
117    def test_cancel_after_ready(self):
118        self.assert_no_active_block_jobs()
119
120        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
121                             target=target_img)
122        self.assert_qmp(result, 'return', {})
123
124        self.wait_ready_and_cancel()
125        result = self.vm.qmp('query-block')
126        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
127        self.vm.shutdown()
128        self.assertTrue(iotests.compare_images(test_img, target_img),
129                        'target image does not match source after mirroring')
130
131    def test_pause(self):
132        self.assert_no_active_block_jobs()
133
134        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
135                             target=target_img)
136        self.assert_qmp(result, 'return', {})
137
138        result = self.vm.qmp('block-job-pause', device='drive0')
139        self.assert_qmp(result, 'return', {})
140
141        time.sleep(1)
142        result = self.vm.qmp('query-block-jobs')
143        offset = self.dictpath(result, 'return[0]/offset')
144
145        time.sleep(1)
146        result = self.vm.qmp('query-block-jobs')
147        self.assert_qmp(result, 'return[0]/offset', offset)
148
149        result = self.vm.qmp('block-job-resume', device='drive0')
150        self.assert_qmp(result, 'return', {})
151
152        self.complete_and_wait()
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_small_buffer(self):
158        self.assert_no_active_block_jobs()
159
160        # A small buffer is rounded up automatically
161        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
162                             buf_size=4096, target=target_img)
163        self.assert_qmp(result, 'return', {})
164
165        self.complete_and_wait()
166        result = self.vm.qmp('query-block')
167        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
168        self.vm.shutdown()
169        self.assertTrue(iotests.compare_images(test_img, target_img),
170                        'target image does not match source after mirroring')
171
172    def test_small_buffer2(self):
173        self.assert_no_active_block_jobs()
174
175        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
176                        % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img)
177        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
178                             buf_size=65536, mode='existing', target=target_img)
179        self.assert_qmp(result, 'return', {})
180
181        self.complete_and_wait()
182        result = self.vm.qmp('query-block')
183        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
184        self.vm.shutdown()
185        self.assertTrue(iotests.compare_images(test_img, target_img),
186                        'target image does not match source after mirroring')
187
188    def test_large_cluster(self):
189        self.assert_no_active_block_jobs()
190
191        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
192                        % (TestSingleDrive.image_len, backing_img), target_img)
193        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
194                             mode='existing', target=target_img)
195        self.assert_qmp(result, 'return', {})
196
197        self.complete_and_wait()
198        result = self.vm.qmp('query-block')
199        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
200        self.vm.shutdown()
201        self.assertTrue(iotests.compare_images(test_img, target_img),
202                        'target image does not match source after mirroring')
203
204    def test_medium_not_found(self):
205        result = self.vm.qmp('drive-mirror', device='ide1-cd0', sync='full',
206                             target=target_img)
207        self.assert_qmp(result, 'error/class', 'GenericError')
208
209    def test_image_not_found(self):
210        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
211                             mode='existing', target=target_img)
212        self.assert_qmp(result, 'error/class', 'GenericError')
213
214    def test_device_not_found(self):
215        result = self.vm.qmp('drive-mirror', device='nonexistent', sync='full',
216                             target=target_img)
217        self.assert_qmp(result, 'error/class', 'DeviceNotFound')
218
219class TestMirrorNoBacking(ImageMirroringTestCase):
220    image_len = 2 * 1024 * 1024 # MB
221
222    def complete_and_wait(self, drive='drive0', wait_ready=True):
223        iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
224        return ImageMirroringTestCase.complete_and_wait(self, drive, wait_ready)
225
226    def compare_images(self, img1, img2):
227        iotests.create_image(target_backing_img, TestMirrorNoBacking.image_len)
228        return iotests.compare_images(img1, img2)
229
230    def setUp(self):
231        iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
232        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
233        self.vm = iotests.VM().add_drive(test_img)
234        self.vm.launch()
235
236    def tearDown(self):
237        self.vm.shutdown()
238        os.remove(test_img)
239        os.remove(backing_img)
240        os.remove(target_backing_img)
241        os.remove(target_img)
242
243    def test_complete(self):
244        self.assert_no_active_block_jobs()
245
246        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
247        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
248                             mode='existing', target=target_img)
249        self.assert_qmp(result, 'return', {})
250
251        self.complete_and_wait()
252        result = self.vm.qmp('query-block')
253        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
254        self.vm.shutdown()
255        self.assertTrue(self.compare_images(test_img, target_img),
256                        'target image does not match source after mirroring')
257
258    def test_cancel(self):
259        self.assert_no_active_block_jobs()
260
261        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, target_img)
262        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
263                             mode='existing', target=target_img)
264        self.assert_qmp(result, 'return', {})
265
266        self.wait_ready_and_cancel()
267        result = self.vm.qmp('query-block')
268        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
269        self.vm.shutdown()
270        self.assertTrue(self.compare_images(test_img, target_img),
271                        'target image does not match source after mirroring')
272
273    def test_large_cluster(self):
274        self.assert_no_active_block_jobs()
275
276        # qemu-img create fails if the image is not there
277        qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
278                        %(TestMirrorNoBacking.image_len), target_backing_img)
279        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
280                        % (TestMirrorNoBacking.image_len, target_backing_img), target_img)
281        os.remove(target_backing_img)
282
283        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
284                             mode='existing', target=target_img)
285        self.assert_qmp(result, 'return', {})
286
287        self.complete_and_wait()
288        result = self.vm.qmp('query-block')
289        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
290        self.vm.shutdown()
291        self.assertTrue(self.compare_images(test_img, target_img),
292                        'target image does not match source after mirroring')
293
294class TestMirrorResized(ImageMirroringTestCase):
295    backing_len = 1 * 1024 * 1024 # MB
296    image_len = 2 * 1024 * 1024 # MB
297
298    def setUp(self):
299        iotests.create_image(backing_img, TestMirrorResized.backing_len)
300        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
301        qemu_img('resize', test_img, '2M')
302        self.vm = iotests.VM().add_drive(test_img)
303        self.vm.launch()
304
305    def tearDown(self):
306        self.vm.shutdown()
307        os.remove(test_img)
308        os.remove(backing_img)
309        try:
310            os.remove(target_img)
311        except OSError:
312            pass
313
314    def test_complete_top(self):
315        self.assert_no_active_block_jobs()
316
317        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
318                             target=target_img)
319        self.assert_qmp(result, 'return', {})
320
321        self.complete_and_wait()
322        result = self.vm.qmp('query-block')
323        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
324        self.vm.shutdown()
325        self.assertTrue(iotests.compare_images(test_img, target_img),
326                        'target image does not match source after mirroring')
327
328    def test_complete_full(self):
329        self.assert_no_active_block_jobs()
330
331        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
332                             target=target_img)
333        self.assert_qmp(result, 'return', {})
334
335        self.complete_and_wait()
336        result = self.vm.qmp('query-block')
337        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
338        self.vm.shutdown()
339        self.assertTrue(iotests.compare_images(test_img, target_img),
340                        'target image does not match source after mirroring')
341
342class TestReadErrors(ImageMirroringTestCase):
343    image_len = 2 * 1024 * 1024 # MB
344
345    # this should be a multiple of twice the default granularity
346    # so that we hit this offset first in state 1
347    MIRROR_GRANULARITY = 1024 * 1024
348
349    def create_blkdebug_file(self, name, event, errno):
350        file = open(name, 'w')
351        file.write('''
352[inject-error]
353state = "1"
354event = "%s"
355errno = "%d"
356immediately = "off"
357once = "on"
358sector = "%d"
359
360[set-state]
361state = "1"
362event = "%s"
363new_state = "2"
364
365[set-state]
366state = "2"
367event = "%s"
368new_state = "1"
369''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
370        file.close()
371
372    def setUp(self):
373        self.blkdebug_file = backing_img + ".blkdebug"
374        iotests.create_image(backing_img, TestReadErrors.image_len)
375        self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
376        qemu_img('create', '-f', iotests.imgfmt,
377                 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
378                       % (self.blkdebug_file, backing_img),
379                 test_img)
380        # Write something for tests that use sync='top'
381        qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
382                        test_img)
383        self.vm = iotests.VM().add_drive(test_img)
384        self.vm.launch()
385
386    def tearDown(self):
387        self.vm.shutdown()
388        os.remove(test_img)
389        os.remove(backing_img)
390        os.remove(self.blkdebug_file)
391
392    def test_report_read(self):
393        self.assert_no_active_block_jobs()
394
395        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
396                             target=target_img)
397        self.assert_qmp(result, 'return', {})
398
399        completed = False
400        error = False
401        while not completed:
402            for event in self.vm.get_qmp_events(wait=True):
403                if event['event'] == 'BLOCK_JOB_ERROR':
404                    self.assert_qmp(event, 'data/device', 'drive0')
405                    self.assert_qmp(event, 'data/operation', 'read')
406                    error = True
407                elif event['event'] == 'BLOCK_JOB_READY':
408                    self.assertTrue(False, 'job completed unexpectedly')
409                elif event['event'] == 'BLOCK_JOB_COMPLETED':
410                    self.assertTrue(error, 'job completed unexpectedly')
411                    self.assert_qmp(event, 'data/type', 'mirror')
412                    self.assert_qmp(event, 'data/device', 'drive0')
413                    self.assert_qmp(event, 'data/error', 'Input/output error')
414                    self.assert_qmp(event, 'data/len', self.image_len)
415                    completed = True
416
417        self.assert_no_active_block_jobs()
418        self.vm.shutdown()
419
420    def test_ignore_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, on_source_error='ignore')
425        self.assert_qmp(result, 'return', {})
426
427        event = self.vm.get_qmp_event(wait=True)
428        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
429        self.assert_qmp(event, 'data/device', 'drive0')
430        self.assert_qmp(event, 'data/operation', 'read')
431        result = self.vm.qmp('query-block-jobs')
432        self.assert_qmp(result, 'return[0]/paused', False)
433        self.complete_and_wait()
434        self.vm.shutdown()
435
436    def test_large_cluster(self):
437        self.assert_no_active_block_jobs()
438
439        # Test COW into the target image.  The first half of the
440        # cluster at MIRROR_GRANULARITY has to be copied from
441        # backing_img, even though sync='top'.
442        qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img)
443        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
444                             on_source_error='ignore',
445                             mode='existing', target=target_img)
446        self.assert_qmp(result, 'return', {})
447
448        event = self.vm.get_qmp_event(wait=True)
449        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
450        self.assert_qmp(event, 'data/device', 'drive0')
451        self.assert_qmp(event, 'data/operation', 'read')
452        result = self.vm.qmp('query-block-jobs')
453        self.assert_qmp(result, 'return[0]/paused', False)
454        self.complete_and_wait()
455        self.vm.shutdown()
456
457        # Detach blkdebug to compare images successfully
458        qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
459        self.assertTrue(iotests.compare_images(test_img, target_img),
460                        'target image does not match source after mirroring')
461
462    def test_stop_read(self):
463        self.assert_no_active_block_jobs()
464
465        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
466                             target=target_img, on_source_error='stop')
467        self.assert_qmp(result, 'return', {})
468
469        error = False
470        ready = False
471        while not ready:
472            for event in self.vm.get_qmp_events(wait=True):
473                if event['event'] == 'BLOCK_JOB_ERROR':
474                    self.assert_qmp(event, 'data/device', 'drive0')
475                    self.assert_qmp(event, 'data/operation', 'read')
476
477                    result = self.vm.qmp('query-block-jobs')
478                    self.assert_qmp(result, 'return[0]/paused', True)
479                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
480
481                    result = self.vm.qmp('block-job-resume', device='drive0')
482                    self.assert_qmp(result, 'return', {})
483                    error = True
484                elif event['event'] == 'BLOCK_JOB_READY':
485                    self.assertTrue(error, 'job completed unexpectedly')
486                    self.assert_qmp(event, 'data/device', 'drive0')
487                    ready = True
488
489        result = self.vm.qmp('query-block-jobs')
490        self.assert_qmp(result, 'return[0]/paused', False)
491        self.assert_qmp(result, 'return[0]/io-status', 'ok')
492
493        self.complete_and_wait(wait_ready=False)
494        self.assert_no_active_block_jobs()
495        self.vm.shutdown()
496
497class TestWriteErrors(ImageMirroringTestCase):
498    image_len = 2 * 1024 * 1024 # MB
499
500    # this should be a multiple of twice the default granularity
501    # so that we hit this offset first in state 1
502    MIRROR_GRANULARITY = 1024 * 1024
503
504    def create_blkdebug_file(self, name, event, errno):
505        file = open(name, 'w')
506        file.write('''
507[inject-error]
508state = "1"
509event = "%s"
510errno = "%d"
511immediately = "off"
512once = "on"
513sector = "%d"
514
515[set-state]
516state = "1"
517event = "%s"
518new_state = "2"
519
520[set-state]
521state = "2"
522event = "%s"
523new_state = "1"
524''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
525        file.close()
526
527    def setUp(self):
528        self.blkdebug_file = target_img + ".blkdebug"
529        iotests.create_image(backing_img, TestWriteErrors.image_len)
530        self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
531        qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
532        self.vm = iotests.VM().add_drive(test_img)
533        self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
534        qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
535        self.vm.launch()
536
537    def tearDown(self):
538        self.vm.shutdown()
539        os.remove(test_img)
540        os.remove(backing_img)
541        os.remove(self.blkdebug_file)
542
543    def test_report_write(self):
544        self.assert_no_active_block_jobs()
545
546        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
547                             mode='existing', target=self.target_img)
548        self.assert_qmp(result, 'return', {})
549
550        completed = False
551        error = False
552        while not completed:
553            for event in self.vm.get_qmp_events(wait=True):
554                if event['event'] == 'BLOCK_JOB_ERROR':
555                    self.assert_qmp(event, 'data/device', 'drive0')
556                    self.assert_qmp(event, 'data/operation', 'write')
557                    error = True
558                elif event['event'] == 'BLOCK_JOB_READY':
559                    self.assertTrue(False, 'job completed unexpectedly')
560                elif event['event'] == 'BLOCK_JOB_COMPLETED':
561                    self.assertTrue(error, 'job completed unexpectedly')
562                    self.assert_qmp(event, 'data/type', 'mirror')
563                    self.assert_qmp(event, 'data/device', 'drive0')
564                    self.assert_qmp(event, 'data/error', 'Input/output error')
565                    self.assert_qmp(event, 'data/len', self.image_len)
566                    completed = True
567
568        self.assert_no_active_block_jobs()
569        self.vm.shutdown()
570
571    def test_ignore_write(self):
572        self.assert_no_active_block_jobs()
573
574        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
575                             mode='existing', target=self.target_img,
576                             on_target_error='ignore')
577        self.assert_qmp(result, 'return', {})
578
579        event = self.vm.get_qmp_event(wait=True)
580        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
581        self.assert_qmp(event, 'data/device', 'drive0')
582        self.assert_qmp(event, 'data/operation', 'write')
583        result = self.vm.qmp('query-block-jobs')
584        self.assert_qmp(result, 'return[0]/paused', False)
585        self.complete_and_wait()
586        self.vm.shutdown()
587
588    def test_stop_write(self):
589        self.assert_no_active_block_jobs()
590
591        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
592                             mode='existing', target=self.target_img,
593                             on_target_error='stop')
594        self.assert_qmp(result, 'return', {})
595
596        error = False
597        ready = False
598        while not ready:
599            for event in self.vm.get_qmp_events(wait=True):
600                if event['event'] == 'BLOCK_JOB_ERROR':
601                    self.assert_qmp(event, 'data/device', 'drive0')
602                    self.assert_qmp(event, 'data/operation', 'write')
603
604                    result = self.vm.qmp('query-block-jobs')
605                    self.assert_qmp(result, 'return[0]/paused', True)
606                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
607
608                    result = self.vm.qmp('block-job-resume', device='drive0')
609                    self.assert_qmp(result, 'return', {})
610
611                    result = self.vm.qmp('query-block-jobs')
612                    self.assert_qmp(result, 'return[0]/paused', False)
613                    self.assert_qmp(result, 'return[0]/io-status', 'ok')
614                    error = True
615                elif event['event'] == 'BLOCK_JOB_READY':
616                    self.assertTrue(error, 'job completed unexpectedly')
617                    self.assert_qmp(event, 'data/device', 'drive0')
618                    ready = True
619
620        self.complete_and_wait(wait_ready=False)
621        self.assert_no_active_block_jobs()
622        self.vm.shutdown()
623
624class TestSetSpeed(ImageMirroringTestCase):
625    image_len = 80 * 1024 * 1024 # MB
626
627    def setUp(self):
628        qemu_img('create', backing_img, str(TestSetSpeed.image_len))
629        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
630        self.vm = iotests.VM().add_drive(test_img)
631        self.vm.launch()
632
633    def tearDown(self):
634        self.vm.shutdown()
635        os.remove(test_img)
636        os.remove(backing_img)
637        os.remove(target_img)
638
639    def test_set_speed(self):
640        self.assert_no_active_block_jobs()
641
642        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
643                             target=target_img)
644        self.assert_qmp(result, 'return', {})
645
646        # Default speed is 0
647        result = self.vm.qmp('query-block-jobs')
648        self.assert_qmp(result, 'return[0]/device', 'drive0')
649        self.assert_qmp(result, 'return[0]/speed', 0)
650
651        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
652        self.assert_qmp(result, 'return', {})
653
654        # Ensure the speed we set was accepted
655        result = self.vm.qmp('query-block-jobs')
656        self.assert_qmp(result, 'return[0]/device', 'drive0')
657        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
658
659        self.wait_ready_and_cancel()
660
661        # Check setting speed in drive-mirror works
662        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
663                             target=target_img, speed=4*1024*1024)
664        self.assert_qmp(result, 'return', {})
665
666        result = self.vm.qmp('query-block-jobs')
667        self.assert_qmp(result, 'return[0]/device', 'drive0')
668        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
669
670        self.wait_ready_and_cancel()
671
672    def test_set_speed_invalid(self):
673        self.assert_no_active_block_jobs()
674
675        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
676                             target=target_img, speed=-1)
677        self.assert_qmp(result, 'error/class', 'GenericError')
678
679        self.assert_no_active_block_jobs()
680
681        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
682                             target=target_img)
683        self.assert_qmp(result, 'return', {})
684
685        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
686        self.assert_qmp(result, 'error/class', 'GenericError')
687
688        self.wait_ready_and_cancel()
689
690if __name__ == '__main__':
691    iotests.main(supported_fmts=['qcow2', 'qed'])
692