xref: /openbmc/qemu/tests/qemu-iotests/041 (revision 8917c3bd)
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 TestMirrorResized(ImageMirroringTestCase):
348    backing_len = 1 * 1024 * 1024 # MB
349    image_len = 2 * 1024 * 1024 # MB
350
351    def setUp(self):
352        self.create_image(backing_img, TestMirrorResized.backing_len)
353        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
354        qemu_img('resize', test_img, '2M')
355        self.vm = iotests.VM().add_drive(test_img)
356        self.vm.launch()
357
358    def tearDown(self):
359        self.vm.shutdown()
360        os.remove(test_img)
361        os.remove(backing_img)
362        try:
363            os.remove(target_img)
364        except OSError:
365            pass
366
367    def test_complete_top(self):
368        self.assert_no_active_mirrors()
369
370        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
371                             target=target_img)
372        self.assert_qmp(result, 'return', {})
373
374        self.complete_and_wait()
375        result = self.vm.qmp('query-block')
376        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
377        self.vm.shutdown()
378        self.assertTrue(self.compare_images(test_img, target_img),
379                        'target image does not match source after mirroring')
380
381    def test_complete_full(self):
382        self.assert_no_active_mirrors()
383
384        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
385                             target=target_img)
386        self.assert_qmp(result, 'return', {})
387
388        self.complete_and_wait()
389        result = self.vm.qmp('query-block')
390        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
391        self.vm.shutdown()
392        self.assertTrue(self.compare_images(test_img, target_img),
393                        'target image does not match source after mirroring')
394
395class TestReadErrors(ImageMirroringTestCase):
396    image_len = 2 * 1024 * 1024 # MB
397
398    # this should be a multiple of twice the default granularity
399    # so that we hit this offset first in state 1
400    MIRROR_GRANULARITY = 1024 * 1024
401
402    def create_blkdebug_file(self, name, event, errno):
403        file = open(name, 'w')
404        file.write('''
405[inject-error]
406state = "1"
407event = "%s"
408errno = "%d"
409immediately = "off"
410once = "on"
411sector = "%d"
412
413[set-state]
414state = "1"
415event = "%s"
416new_state = "2"
417
418[set-state]
419state = "2"
420event = "%s"
421new_state = "1"
422''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
423        file.close()
424
425    def setUp(self):
426        self.blkdebug_file = backing_img + ".blkdebug"
427        self.create_image(backing_img, TestReadErrors.image_len)
428        self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
429        qemu_img('create', '-f', iotests.imgfmt,
430                 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
431                       % (self.blkdebug_file, backing_img),
432                 test_img)
433        # Write something for tests that use sync='top'
434        qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
435                        test_img)
436        self.vm = iotests.VM().add_drive(test_img)
437        self.vm.launch()
438
439    def tearDown(self):
440        self.vm.shutdown()
441        os.remove(test_img)
442        os.remove(backing_img)
443        os.remove(self.blkdebug_file)
444
445    def test_report_read(self):
446        self.assert_no_active_mirrors()
447
448        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
449                             target=target_img)
450        self.assert_qmp(result, 'return', {})
451
452        completed = False
453        error = False
454        while not completed:
455            for event in self.vm.get_qmp_events(wait=True):
456                if event['event'] == 'BLOCK_JOB_ERROR':
457                    self.assert_qmp(event, 'data/device', 'drive0')
458                    self.assert_qmp(event, 'data/operation', 'read')
459                    error = True
460                elif event['event'] == 'BLOCK_JOB_READY':
461                    self.assertTrue(False, 'job completed unexpectedly')
462                elif event['event'] == 'BLOCK_JOB_COMPLETED':
463                    self.assertTrue(error, 'job completed unexpectedly')
464                    self.assert_qmp(event, 'data/type', 'mirror')
465                    self.assert_qmp(event, 'data/device', 'drive0')
466                    self.assert_qmp(event, 'data/error', 'Input/output error')
467                    self.assert_qmp(event, 'data/len', self.image_len)
468                    completed = True
469
470        self.assert_no_active_mirrors()
471        self.vm.shutdown()
472
473    def test_ignore_read(self):
474        self.assert_no_active_mirrors()
475
476        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
477                             target=target_img, on_source_error='ignore')
478        self.assert_qmp(result, 'return', {})
479
480        event = self.vm.get_qmp_event(wait=True)
481        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
482        self.assert_qmp(event, 'data/device', 'drive0')
483        self.assert_qmp(event, 'data/operation', 'read')
484        result = self.vm.qmp('query-block-jobs')
485        self.assert_qmp(result, 'return[0]/paused', False)
486        self.complete_and_wait()
487        self.vm.shutdown()
488
489    def test_large_cluster(self):
490        self.assert_no_active_mirrors()
491
492        # Test COW into the target image.  The first half of the
493        # cluster at MIRROR_GRANULARITY has to be copied from
494        # backing_img, even though sync='top'.
495        qemu_img('create', '-f', iotests.imgfmt, '-ocluster_size=131072,backing_file=%s' %(backing_img), target_img)
496        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
497                             on_source_error='ignore',
498                             mode='existing', target=target_img)
499        self.assert_qmp(result, 'return', {})
500
501        event = self.vm.get_qmp_event(wait=True)
502        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
503        self.assert_qmp(event, 'data/device', 'drive0')
504        self.assert_qmp(event, 'data/operation', 'read')
505        result = self.vm.qmp('query-block-jobs')
506        self.assert_qmp(result, 'return[0]/paused', False)
507        self.complete_and_wait()
508        self.vm.shutdown()
509
510        # Detach blkdebug to compare images successfully
511        qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img, test_img)
512        self.assertTrue(self.compare_images(test_img, target_img),
513                        'target image does not match source after mirroring')
514
515    def test_stop_read(self):
516        self.assert_no_active_mirrors()
517
518        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
519                             target=target_img, on_source_error='stop')
520        self.assert_qmp(result, 'return', {})
521
522        error = False
523        ready = False
524        while not ready:
525            for event in self.vm.get_qmp_events(wait=True):
526                if event['event'] == 'BLOCK_JOB_ERROR':
527                    self.assert_qmp(event, 'data/device', 'drive0')
528                    self.assert_qmp(event, 'data/operation', 'read')
529
530                    result = self.vm.qmp('query-block-jobs')
531                    self.assert_qmp(result, 'return[0]/paused', True)
532                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
533
534                    result = self.vm.qmp('block-job-resume', device='drive0')
535                    self.assert_qmp(result, 'return', {})
536                    error = True
537                elif event['event'] == 'BLOCK_JOB_READY':
538                    self.assertTrue(error, 'job completed unexpectedly')
539                    self.assert_qmp(event, 'data/device', 'drive0')
540                    ready = True
541
542        result = self.vm.qmp('query-block-jobs')
543        self.assert_qmp(result, 'return[0]/paused', False)
544        self.assert_qmp(result, 'return[0]/io-status', 'ok')
545
546        self.complete_and_wait(wait_ready=False)
547        self.assert_no_active_mirrors()
548        self.vm.shutdown()
549
550class TestWriteErrors(ImageMirroringTestCase):
551    image_len = 2 * 1024 * 1024 # MB
552
553    # this should be a multiple of twice the default granularity
554    # so that we hit this offset first in state 1
555    MIRROR_GRANULARITY = 1024 * 1024
556
557    def create_blkdebug_file(self, name, event, errno):
558        file = open(name, 'w')
559        file.write('''
560[inject-error]
561state = "1"
562event = "%s"
563errno = "%d"
564immediately = "off"
565once = "on"
566sector = "%d"
567
568[set-state]
569state = "1"
570event = "%s"
571new_state = "2"
572
573[set-state]
574state = "2"
575event = "%s"
576new_state = "1"
577''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event))
578        file.close()
579
580    def setUp(self):
581        self.blkdebug_file = target_img + ".blkdebug"
582        self.create_image(backing_img, TestWriteErrors.image_len)
583        self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
584        qemu_img('create', '-f', iotests.imgfmt, '-obacking_file=%s' %(backing_img), test_img)
585        self.vm = iotests.VM().add_drive(test_img)
586        self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
587        qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
588        self.vm.launch()
589
590    def tearDown(self):
591        self.vm.shutdown()
592        os.remove(test_img)
593        os.remove(backing_img)
594        os.remove(self.blkdebug_file)
595
596    def test_report_write(self):
597        self.assert_no_active_mirrors()
598
599        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
600                             mode='existing', target=self.target_img)
601        self.assert_qmp(result, 'return', {})
602
603        completed = False
604        error = False
605        while not completed:
606            for event in self.vm.get_qmp_events(wait=True):
607                if event['event'] == 'BLOCK_JOB_ERROR':
608                    self.assert_qmp(event, 'data/device', 'drive0')
609                    self.assert_qmp(event, 'data/operation', 'write')
610                    error = True
611                elif event['event'] == 'BLOCK_JOB_READY':
612                    self.assertTrue(False, 'job completed unexpectedly')
613                elif event['event'] == 'BLOCK_JOB_COMPLETED':
614                    self.assertTrue(error, 'job completed unexpectedly')
615                    self.assert_qmp(event, 'data/type', 'mirror')
616                    self.assert_qmp(event, 'data/device', 'drive0')
617                    self.assert_qmp(event, 'data/error', 'Input/output error')
618                    self.assert_qmp(event, 'data/len', self.image_len)
619                    completed = True
620
621        self.assert_no_active_mirrors()
622        self.vm.shutdown()
623
624    def test_ignore_write(self):
625        self.assert_no_active_mirrors()
626
627        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
628                             mode='existing', target=self.target_img,
629                             on_target_error='ignore')
630        self.assert_qmp(result, 'return', {})
631
632        event = self.vm.get_qmp_event(wait=True)
633        self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
634        self.assert_qmp(event, 'data/device', 'drive0')
635        self.assert_qmp(event, 'data/operation', 'write')
636        result = self.vm.qmp('query-block-jobs')
637        self.assert_qmp(result, 'return[0]/paused', False)
638        self.complete_and_wait()
639        self.vm.shutdown()
640
641    def test_stop_write(self):
642        self.assert_no_active_mirrors()
643
644        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
645                             mode='existing', target=self.target_img,
646                             on_target_error='stop')
647        self.assert_qmp(result, 'return', {})
648
649        error = False
650        ready = False
651        while not ready:
652            for event in self.vm.get_qmp_events(wait=True):
653                if event['event'] == 'BLOCK_JOB_ERROR':
654                    self.assert_qmp(event, 'data/device', 'drive0')
655                    self.assert_qmp(event, 'data/operation', 'write')
656
657                    result = self.vm.qmp('query-block-jobs')
658                    self.assert_qmp(result, 'return[0]/paused', True)
659                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
660
661                    result = self.vm.qmp('block-job-resume', device='drive0')
662                    self.assert_qmp(result, 'return', {})
663
664                    result = self.vm.qmp('query-block-jobs')
665                    self.assert_qmp(result, 'return[0]/paused', False)
666                    self.assert_qmp(result, 'return[0]/io-status', 'ok')
667                    error = True
668                elif event['event'] == 'BLOCK_JOB_READY':
669                    self.assertTrue(error, 'job completed unexpectedly')
670                    self.assert_qmp(event, 'data/device', 'drive0')
671                    ready = True
672
673        self.complete_and_wait(wait_ready=False)
674        self.assert_no_active_mirrors()
675        self.vm.shutdown()
676
677class TestSetSpeed(ImageMirroringTestCase):
678    image_len = 80 * 1024 * 1024 # MB
679
680    def setUp(self):
681        qemu_img('create', backing_img, str(TestSetSpeed.image_len))
682        qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % backing_img, test_img)
683        self.vm = iotests.VM().add_drive(test_img)
684        self.vm.launch()
685
686    def tearDown(self):
687        self.vm.shutdown()
688        os.remove(test_img)
689        os.remove(backing_img)
690        os.remove(target_img)
691
692    def test_set_speed(self):
693        self.assert_no_active_mirrors()
694
695        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
696                             target=target_img)
697        self.assert_qmp(result, 'return', {})
698
699        # Default speed is 0
700        result = self.vm.qmp('query-block-jobs')
701        self.assert_qmp(result, 'return[0]/device', 'drive0')
702        self.assert_qmp(result, 'return[0]/speed', 0)
703
704        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
705        self.assert_qmp(result, 'return', {})
706
707        # Ensure the speed we set was accepted
708        result = self.vm.qmp('query-block-jobs')
709        self.assert_qmp(result, 'return[0]/device', 'drive0')
710        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
711
712        self.cancel_and_wait()
713
714        # Check setting speed in drive-mirror works
715        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
716                             target=target_img, speed=4*1024*1024)
717        self.assert_qmp(result, 'return', {})
718
719        result = self.vm.qmp('query-block-jobs')
720        self.assert_qmp(result, 'return[0]/device', 'drive0')
721        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
722
723        self.cancel_and_wait()
724
725    def test_set_speed_invalid(self):
726        self.assert_no_active_mirrors()
727
728        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
729                             target=target_img, speed=-1)
730        self.assert_qmp(result, 'error/class', 'GenericError')
731
732        self.assert_no_active_mirrors()
733
734        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
735                             target=target_img)
736        self.assert_qmp(result, 'return', {})
737
738        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
739        self.assert_qmp(result, 'error/class', 'GenericError')
740
741        self.cancel_and_wait()
742
743if __name__ == '__main__':
744    iotests.main(supported_fmts=['qcow2', 'qed'])
745