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