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