cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

041 (58363B)


      1#!/usr/bin/env python3
      2# group: rw auto backing
      3#
      4# Tests for image mirroring.
      5#
      6# Copyright (C) 2012 Red Hat, Inc.
      7#
      8# This program is free software; you can redistribute it and/or modify
      9# it under the terms of the GNU General Public License as published by
     10# the Free Software Foundation; either version 2 of the License, or
     11# (at your option) any later version.
     12#
     13# This program is distributed in the hope that it will be useful,
     14# but WITHOUT ANY WARRANTY; without even the implied warranty of
     15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16# GNU General Public License for more details.
     17#
     18# You should have received a copy of the GNU General Public License
     19# along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20#
     21
     22import time
     23import os
     24import re
     25import json
     26import iotests
     27from iotests import qemu_img, qemu_img_pipe, qemu_io
     28
     29backing_img = os.path.join(iotests.test_dir, 'backing.img')
     30target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
     31test_img = os.path.join(iotests.test_dir, 'test.img')
     32target_img = os.path.join(iotests.test_dir, 'target.img')
     33
     34quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img')
     35quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img')
     36quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
     37quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
     38quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
     39
     40nbd_sock_path = os.path.join(iotests.sock_dir, 'nbd.sock')
     41
     42class TestSingleDrive(iotests.QMPTestCase):
     43    image_len = 1 * 1024 * 1024 # MB
     44    qmp_cmd = 'drive-mirror'
     45    qmp_target = target_img
     46
     47    def setUp(self):
     48        iotests.create_image(backing_img, self.image_len)
     49        qemu_img('create', '-f', iotests.imgfmt,
     50                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
     51        self.vm = iotests.VM().add_drive(test_img, "node-name=top,backing.node-name=base")
     52        if iotests.qemu_default_machine == 'pc':
     53            self.vm.add_drive(None, 'media=cdrom', 'ide')
     54        self.vm.launch()
     55
     56    def tearDown(self):
     57        self.vm.shutdown()
     58        os.remove(test_img)
     59        os.remove(backing_img)
     60        try:
     61            os.remove(target_img)
     62        except OSError:
     63            pass
     64
     65    def test_complete(self):
     66        self.assert_no_active_block_jobs()
     67
     68        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
     69                             target=self.qmp_target)
     70        self.assert_qmp(result, 'return', {})
     71
     72        self.complete_and_wait()
     73        result = self.vm.qmp('query-block')
     74        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
     75        self.vm.shutdown()
     76        self.assertTrue(iotests.compare_images(test_img, target_img),
     77                        'target image does not match source after mirroring')
     78
     79    def test_cancel(self):
     80        self.assert_no_active_block_jobs()
     81
     82        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
     83                             target=self.qmp_target)
     84        self.assert_qmp(result, 'return', {})
     85
     86        self.cancel_and_wait(force=True)
     87        result = self.vm.qmp('query-block')
     88        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
     89
     90    def test_cancel_after_ready(self):
     91        self.assert_no_active_block_jobs()
     92
     93        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
     94                             target=self.qmp_target)
     95        self.assert_qmp(result, 'return', {})
     96
     97        self.wait_ready_and_cancel()
     98        result = self.vm.qmp('query-block')
     99        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
    100        self.vm.shutdown()
    101        self.assertTrue(iotests.compare_images(test_img, target_img),
    102                        'target image does not match source after mirroring')
    103
    104    def test_pause(self):
    105        self.assert_no_active_block_jobs()
    106
    107        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    108                             target=self.qmp_target)
    109        self.assert_qmp(result, 'return', {})
    110
    111        self.pause_job('drive0')
    112
    113        result = self.vm.qmp('query-block-jobs')
    114        offset = self.dictpath(result, 'return[0]/offset')
    115
    116        time.sleep(0.5)
    117        result = self.vm.qmp('query-block-jobs')
    118        self.assert_qmp(result, 'return[0]/offset', offset)
    119
    120        result = self.vm.qmp('block-job-resume', device='drive0')
    121        self.assert_qmp(result, 'return', {})
    122
    123        self.complete_and_wait()
    124        self.vm.shutdown()
    125        self.assertTrue(iotests.compare_images(test_img, target_img),
    126                        'target image does not match source after mirroring')
    127
    128    def test_small_buffer(self):
    129        self.assert_no_active_block_jobs()
    130
    131        # A small buffer is rounded up automatically
    132        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    133                             buf_size=4096, target=self.qmp_target)
    134        self.assert_qmp(result, 'return', {})
    135
    136        self.complete_and_wait()
    137        result = self.vm.qmp('query-block')
    138        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    139        self.vm.shutdown()
    140        self.assertTrue(iotests.compare_images(test_img, target_img),
    141                        'target image does not match source after mirroring')
    142
    143    def test_small_buffer2(self):
    144        self.assert_no_active_block_jobs()
    145
    146        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d'
    147                        % (self.image_len, self.image_len), target_img)
    148        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    149                             buf_size=65536, mode='existing', target=self.qmp_target)
    150        self.assert_qmp(result, 'return', {})
    151
    152        self.complete_and_wait()
    153        result = self.vm.qmp('query-block')
    154        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    155        self.vm.shutdown()
    156        self.assertTrue(iotests.compare_images(test_img, target_img),
    157                        'target image does not match source after mirroring')
    158
    159    def test_large_cluster(self):
    160        self.assert_no_active_block_jobs()
    161
    162        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
    163                        % (self.image_len, backing_img),
    164                 '-F', 'raw', target_img)
    165        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    166                             mode='existing', target=self.qmp_target)
    167        self.assert_qmp(result, 'return', {})
    168
    169        self.complete_and_wait()
    170        result = self.vm.qmp('query-block')
    171        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    172        self.vm.shutdown()
    173        self.assertTrue(iotests.compare_images(test_img, target_img),
    174                        'target image does not match source after mirroring')
    175
    176    # Tests that the insertion of the mirror_top filter node doesn't make a
    177    # difference to query-block
    178    def test_implicit_node(self):
    179        self.assert_no_active_block_jobs()
    180
    181        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    182                             target=self.qmp_target)
    183        self.assert_qmp(result, 'return', {})
    184
    185        result = self.vm.qmp('query-block')
    186        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
    187        self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
    188        self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
    189        self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
    190        self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
    191        self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)
    192
    193        result = self.vm.qmp('query-blockstats')
    194        self.assert_qmp(result, 'return[0]/node-name', 'top')
    195        self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
    196
    197        self.cancel_and_wait(force=True)
    198        result = self.vm.qmp('query-block')
    199        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
    200        self.assert_qmp(result, 'return[0]/inserted/drv', iotests.imgfmt)
    201        self.assert_qmp(result, 'return[0]/inserted/backing_file', backing_img)
    202        self.assert_qmp(result, 'return[0]/inserted/backing_file_depth', 1)
    203        self.assert_qmp(result, 'return[0]/inserted/image/filename', test_img)
    204        self.assert_qmp(result, 'return[0]/inserted/image/backing-image/filename', backing_img)
    205
    206        result = self.vm.qmp('query-blockstats')
    207        self.assert_qmp(result, 'return[0]/node-name', 'top')
    208        self.assert_qmp(result, 'return[0]/backing/node-name', 'base')
    209
    210    def test_medium_not_found(self):
    211        if iotests.qemu_default_machine != 'pc':
    212            return
    213
    214        result = self.vm.qmp(self.qmp_cmd, device='ide1-cd0', sync='full',
    215                             target=self.qmp_target)
    216        self.assert_qmp(result, 'error/class', 'GenericError')
    217
    218    def test_image_not_found(self):
    219        result = self.vm.qmp(self.qmp_cmd, device='drive0', sync='full',
    220                             mode='existing', target=self.qmp_target)
    221        self.assert_qmp(result, 'error/class', 'GenericError')
    222
    223    def test_device_not_found(self):
    224        result = self.vm.qmp(self.qmp_cmd, device='nonexistent', sync='full',
    225                             target=self.qmp_target)
    226        self.assert_qmp(result, 'error/class', 'GenericError')
    227
    228class TestSingleBlockdev(TestSingleDrive):
    229    qmp_cmd = 'blockdev-mirror'
    230    qmp_target = 'node1'
    231
    232    def setUp(self):
    233        TestSingleDrive.setUp(self)
    234        qemu_img('create', '-f', iotests.imgfmt,
    235                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
    236        args = {'driver': iotests.imgfmt,
    237                'node-name': self.qmp_target,
    238                'file': { 'filename': target_img, 'driver': 'file' } }
    239        result = self.vm.qmp("blockdev-add", **args)
    240        self.assert_qmp(result, 'return', {})
    241
    242    def test_mirror_to_self(self):
    243        result = self.vm.qmp(self.qmp_cmd, job_id='job0',
    244                             device=self.qmp_target, sync='full',
    245                             target=self.qmp_target)
    246        self.assert_qmp(result, 'error/class', 'GenericError')
    247
    248    def do_test_resize(self, device, node):
    249        def pre_finalize():
    250            if device:
    251                result = self.vm.qmp('block_resize', device=device, size=65536)
    252                self.assert_qmp(result, 'error/class', 'GenericError')
    253
    254            result = self.vm.qmp('block_resize', node_name=node, size=65536)
    255            self.assert_qmp(result, 'error/class', 'GenericError')
    256
    257        result = self.vm.qmp(self.qmp_cmd, job_id='job0', device='drive0',
    258                             sync='full', target=self.qmp_target,
    259                             auto_finalize=False, auto_dismiss=False)
    260        self.assert_qmp(result, 'return', {})
    261
    262        result = self.vm.run_job('job0', auto_finalize=False,
    263                                 pre_finalize=pre_finalize)
    264        self.assertEqual(result, None)
    265
    266    def test_source_resize(self):
    267        self.do_test_resize('drive0', 'top')
    268
    269    def test_target_resize(self):
    270        self.do_test_resize(None, self.qmp_target)
    271
    272    def do_test_target_size(self, size):
    273        result = self.vm.qmp('block_resize', node_name=self.qmp_target,
    274                             size=size)
    275        self.assert_qmp(result, 'return', {})
    276
    277        result = self.vm.qmp(self.qmp_cmd, job_id='job0',
    278                             device='drive0', sync='full', auto_dismiss=False,
    279                             target=self.qmp_target)
    280        self.assert_qmp(result, 'return', {})
    281
    282        result = self.vm.run_job('job0')
    283        self.assertEqual(result, 'Source and target image have different sizes')
    284
    285    # qed does not support shrinking
    286    @iotests.skip_for_formats(('qed'))
    287    def test_small_target(self):
    288        self.do_test_target_size(self.image_len // 2)
    289
    290    def test_large_target(self):
    291        self.do_test_target_size(self.image_len * 2)
    292
    293    test_large_cluster = None
    294    test_image_not_found = None
    295    test_small_buffer2 = None
    296
    297class TestSingleDriveZeroLength(TestSingleDrive):
    298    image_len = 0
    299    test_small_buffer2 = None
    300    test_large_cluster = None
    301
    302class TestSingleBlockdevZeroLength(TestSingleBlockdev):
    303    image_len = 0
    304    test_small_target = None
    305    test_large_target = None
    306
    307class TestSingleDriveUnalignedLength(TestSingleDrive):
    308    image_len = 1025 * 1024
    309    test_small_buffer2 = None
    310    test_large_cluster = None
    311
    312class TestSingleBlockdevUnalignedLength(TestSingleBlockdev):
    313    image_len = 1025 * 1024
    314
    315class TestMirrorNoBacking(iotests.QMPTestCase):
    316    image_len = 2 * 1024 * 1024 # MB
    317
    318    def setUp(self):
    319        iotests.create_image(backing_img, TestMirrorNoBacking.image_len)
    320        qemu_img('create', '-f', iotests.imgfmt,
    321                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
    322        self.vm = iotests.VM().add_drive(test_img)
    323        self.vm.launch()
    324
    325    def tearDown(self):
    326        self.vm.shutdown()
    327        os.remove(test_img)
    328        os.remove(backing_img)
    329        try:
    330            os.remove(target_backing_img)
    331        except:
    332            pass
    333        os.remove(target_img)
    334
    335    def test_complete(self):
    336        self.assert_no_active_block_jobs()
    337
    338        qemu_img('create', '-f', iotests.imgfmt,
    339                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
    340        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    341                             mode='existing', target=target_img)
    342        self.assert_qmp(result, 'return', {})
    343
    344        self.complete_and_wait()
    345        result = self.vm.qmp('query-block')
    346        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    347        self.vm.shutdown()
    348        self.assertTrue(iotests.compare_images(test_img, target_img),
    349                        'target image does not match source after mirroring')
    350
    351    def test_cancel(self):
    352        self.assert_no_active_block_jobs()
    353
    354        qemu_img('create', '-f', iotests.imgfmt,
    355                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', target_img)
    356        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    357                             mode='existing', target=target_img)
    358        self.assert_qmp(result, 'return', {})
    359
    360        self.wait_ready_and_cancel()
    361        result = self.vm.qmp('query-block')
    362        self.assert_qmp(result, 'return[0]/inserted/file', test_img)
    363        self.vm.shutdown()
    364        self.assertTrue(iotests.compare_images(test_img, target_img),
    365                        'target image does not match source after mirroring')
    366
    367    def test_large_cluster(self):
    368        self.assert_no_active_block_jobs()
    369
    370        # qemu-img create fails if the image is not there
    371        qemu_img('create', '-f', iotests.imgfmt, '-o', 'size=%d'
    372                        %(TestMirrorNoBacking.image_len), target_backing_img)
    373        qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,backing_file=%s'
    374                        % (TestMirrorNoBacking.image_len, target_backing_img),
    375                 '-F', iotests.imgfmt, target_img)
    376
    377        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    378                             mode='existing', target=target_img)
    379        self.assert_qmp(result, 'return', {})
    380
    381        self.complete_and_wait()
    382        result = self.vm.qmp('query-block')
    383        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    384        self.vm.shutdown()
    385        self.assertTrue(iotests.compare_images(test_img, target_img),
    386                        'target image does not match source after mirroring')
    387
    388class TestMirrorResized(iotests.QMPTestCase):
    389    backing_len = 1 * 1024 * 1024 # MB
    390    image_len = 2 * 1024 * 1024 # MB
    391
    392    def setUp(self):
    393        iotests.create_image(backing_img, TestMirrorResized.backing_len)
    394        qemu_img('create', '-f', iotests.imgfmt,
    395                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
    396        qemu_img('resize', test_img, '2M')
    397        self.vm = iotests.VM().add_drive(test_img)
    398        self.vm.launch()
    399
    400    def tearDown(self):
    401        self.vm.shutdown()
    402        os.remove(test_img)
    403        os.remove(backing_img)
    404        try:
    405            os.remove(target_img)
    406        except OSError:
    407            pass
    408
    409    def test_complete_top(self):
    410        self.assert_no_active_block_jobs()
    411
    412        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
    413                             target=target_img)
    414        self.assert_qmp(result, 'return', {})
    415
    416        self.complete_and_wait()
    417        result = self.vm.qmp('query-block')
    418        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    419        self.vm.shutdown()
    420        self.assertTrue(iotests.compare_images(test_img, target_img),
    421                        'target image does not match source after mirroring')
    422
    423    def test_complete_full(self):
    424        self.assert_no_active_block_jobs()
    425
    426        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    427                             target=target_img)
    428        self.assert_qmp(result, 'return', {})
    429
    430        self.complete_and_wait()
    431        result = self.vm.qmp('query-block')
    432        self.assert_qmp(result, 'return[0]/inserted/file', target_img)
    433        self.vm.shutdown()
    434        self.assertTrue(iotests.compare_images(test_img, target_img),
    435                        'target image does not match source after mirroring')
    436
    437class TestReadErrors(iotests.QMPTestCase):
    438    image_len = 2 * 1024 * 1024 # MB
    439
    440    # this should be a multiple of twice the default granularity
    441    # so that we hit this offset first in state 1
    442    MIRROR_GRANULARITY = 1024 * 1024
    443
    444    def create_blkdebug_file(self, name, event, errno):
    445        file = open(name, 'w')
    446        file.write('''
    447[inject-error]
    448state = "1"
    449event = "%s"
    450errno = "%d"
    451immediately = "off"
    452once = "on"
    453sector = "%d"
    454
    455[set-state]
    456state = "1"
    457event = "%s"
    458new_state = "2"
    459
    460[set-state]
    461state = "2"
    462event = "%s"
    463new_state = "1"
    464''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event))
    465        file.close()
    466
    467    def setUp(self):
    468        self.blkdebug_file = backing_img + ".blkdebug"
    469        iotests.create_image(backing_img, TestReadErrors.image_len)
    470        self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5)
    471        qemu_img('create', '-f', iotests.imgfmt,
    472                 '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw'
    473                       % (self.blkdebug_file, backing_img),
    474                 test_img)
    475        # Write something for tests that use sync='top'
    476        qemu_io('-c', 'write %d 512' % (self.MIRROR_GRANULARITY + 65536),
    477                        test_img)
    478        self.vm = iotests.VM().add_drive(test_img)
    479        self.vm.launch()
    480
    481    def tearDown(self):
    482        self.vm.shutdown()
    483        os.remove(test_img)
    484        os.remove(target_img)
    485        os.remove(backing_img)
    486        os.remove(self.blkdebug_file)
    487
    488    def test_report_read(self):
    489        self.assert_no_active_block_jobs()
    490
    491        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    492                             target=target_img)
    493        self.assert_qmp(result, 'return', {})
    494
    495        completed = False
    496        error = False
    497        while not completed:
    498            for event in self.vm.get_qmp_events(wait=True):
    499                if event['event'] == 'BLOCK_JOB_ERROR':
    500                    self.assert_qmp(event, 'data/device', 'drive0')
    501                    self.assert_qmp(event, 'data/operation', 'read')
    502                    error = True
    503                elif event['event'] == 'BLOCK_JOB_READY':
    504                    self.assertTrue(False, 'job completed unexpectedly')
    505                elif event['event'] == 'BLOCK_JOB_COMPLETED':
    506                    self.assertTrue(error, 'job completed unexpectedly')
    507                    self.assert_qmp(event, 'data/type', 'mirror')
    508                    self.assert_qmp(event, 'data/device', 'drive0')
    509                    self.assert_qmp(event, 'data/error', 'Input/output error')
    510                    completed = True
    511                elif event['event'] == 'JOB_STATUS_CHANGE':
    512                    self.assert_qmp(event, 'data/id', 'drive0')
    513
    514        self.assert_no_active_block_jobs()
    515
    516    def test_ignore_read(self):
    517        self.assert_no_active_block_jobs()
    518
    519        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    520                             target=target_img, on_source_error='ignore')
    521        self.assert_qmp(result, 'return', {})
    522
    523        event = self.vm.get_qmp_event(wait=True)
    524        while event['event'] == 'JOB_STATUS_CHANGE':
    525            self.assert_qmp(event, 'data/id', 'drive0')
    526            event = self.vm.get_qmp_event(wait=True)
    527
    528        self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
    529        self.assert_qmp(event, 'data/device', 'drive0')
    530        self.assert_qmp(event, 'data/operation', 'read')
    531        result = self.vm.qmp('query-block-jobs')
    532        self.assert_qmp(result, 'return[0]/paused', False)
    533        self.complete_and_wait()
    534
    535    def test_large_cluster(self):
    536        self.assert_no_active_block_jobs()
    537
    538        # Test COW into the target image.  The first half of the
    539        # cluster at MIRROR_GRANULARITY has to be copied from
    540        # backing_img, even though sync='top'.
    541        qemu_img('create', '-f', iotests.imgfmt,
    542                 '-ocluster_size=131072,backing_file=%s' %(backing_img),
    543                 '-F', 'raw', target_img)
    544        result = self.vm.qmp('drive-mirror', device='drive0', sync='top',
    545                             on_source_error='ignore',
    546                             mode='existing', target=target_img)
    547        self.assert_qmp(result, 'return', {})
    548
    549        event = self.vm.get_qmp_event(wait=True)
    550        while event['event'] == 'JOB_STATUS_CHANGE':
    551            self.assert_qmp(event, 'data/id', 'drive0')
    552            event = self.vm.get_qmp_event(wait=True)
    553
    554        self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
    555        self.assert_qmp(event, 'data/device', 'drive0')
    556        self.assert_qmp(event, 'data/operation', 'read')
    557        result = self.vm.qmp('query-block-jobs')
    558        self.assert_qmp(result, 'return[0]/paused', False)
    559        self.complete_and_wait()
    560        self.vm.shutdown()
    561
    562        # Detach blkdebug to compare images successfully
    563        qemu_img('rebase', '-f', iotests.imgfmt, '-u', '-b', backing_img,
    564                 '-F', 'raw', test_img)
    565        self.assertTrue(iotests.compare_images(test_img, target_img),
    566                        'target image does not match source after mirroring')
    567
    568    def test_stop_read(self):
    569        self.assert_no_active_block_jobs()
    570
    571        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    572                             target=target_img, on_source_error='stop')
    573        self.assert_qmp(result, 'return', {})
    574
    575        error = False
    576        ready = False
    577        while not ready:
    578            for event in self.vm.get_qmp_events(wait=True):
    579                if event['event'] == 'BLOCK_JOB_ERROR':
    580                    self.assert_qmp(event, 'data/device', 'drive0')
    581                    self.assert_qmp(event, 'data/operation', 'read')
    582
    583                    result = self.vm.qmp('query-block-jobs')
    584                    self.assert_qmp(result, 'return[0]/paused', True)
    585                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
    586
    587                    result = self.vm.qmp('block-job-resume', device='drive0')
    588                    self.assert_qmp(result, 'return', {})
    589                    error = True
    590                elif event['event'] == 'BLOCK_JOB_READY':
    591                    self.assertTrue(error, 'job completed unexpectedly')
    592                    self.assert_qmp(event, 'data/device', 'drive0')
    593                    ready = True
    594
    595        result = self.vm.qmp('query-block-jobs')
    596        self.assert_qmp(result, 'return[0]/paused', False)
    597        self.assert_qmp(result, 'return[0]/io-status', 'ok')
    598
    599        self.complete_and_wait(wait_ready=False)
    600        self.assert_no_active_block_jobs()
    601
    602class TestWriteErrors(iotests.QMPTestCase):
    603    image_len = 2 * 1024 * 1024 # MB
    604
    605    # this should be a multiple of twice the default granularity
    606    # so that we hit this offset first in state 1
    607    MIRROR_GRANULARITY = 1024 * 1024
    608
    609    def create_blkdebug_file(self, name, event, errno):
    610        file = open(name, 'w')
    611        file.write('''
    612[inject-error]
    613state = "1"
    614event = "%s"
    615errno = "%d"
    616immediately = "off"
    617once = "on"
    618sector = "%d"
    619
    620[set-state]
    621state = "1"
    622event = "%s"
    623new_state = "2"
    624
    625[set-state]
    626state = "2"
    627event = "%s"
    628new_state = "1"
    629''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event))
    630        file.close()
    631
    632    def setUp(self):
    633        self.blkdebug_file = target_img + ".blkdebug"
    634        iotests.create_image(backing_img, TestWriteErrors.image_len)
    635        self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
    636        qemu_img('create', '-f', iotests.imgfmt,
    637                 '-obacking_file=%s' %(backing_img), '-F', 'raw', test_img)
    638        self.vm = iotests.VM().add_drive(test_img)
    639        self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
    640        qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
    641        self.vm.launch()
    642
    643    def tearDown(self):
    644        self.vm.shutdown()
    645        os.remove(test_img)
    646        os.remove(target_img)
    647        os.remove(backing_img)
    648        os.remove(self.blkdebug_file)
    649
    650    def test_report_write(self):
    651        self.assert_no_active_block_jobs()
    652
    653        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    654                             mode='existing', target=self.target_img)
    655        self.assert_qmp(result, 'return', {})
    656
    657        completed = False
    658        error = False
    659        while not completed:
    660            for event in self.vm.get_qmp_events(wait=True):
    661                if event['event'] == 'BLOCK_JOB_ERROR':
    662                    self.assert_qmp(event, 'data/device', 'drive0')
    663                    self.assert_qmp(event, 'data/operation', 'write')
    664                    error = True
    665                elif event['event'] == 'BLOCK_JOB_READY':
    666                    self.assertTrue(False, 'job completed unexpectedly')
    667                elif event['event'] == 'BLOCK_JOB_COMPLETED':
    668                    self.assertTrue(error, 'job completed unexpectedly')
    669                    self.assert_qmp(event, 'data/type', 'mirror')
    670                    self.assert_qmp(event, 'data/device', 'drive0')
    671                    self.assert_qmp(event, 'data/error', 'Input/output error')
    672                    completed = True
    673
    674        self.assert_no_active_block_jobs()
    675
    676    def test_ignore_write(self):
    677        self.assert_no_active_block_jobs()
    678
    679        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    680                             mode='existing', target=self.target_img,
    681                             on_target_error='ignore')
    682        self.assert_qmp(result, 'return', {})
    683
    684        event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
    685        self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
    686        self.assert_qmp(event, 'data/device', 'drive0')
    687        self.assert_qmp(event, 'data/operation', 'write')
    688        result = self.vm.qmp('query-block-jobs')
    689        self.assert_qmp(result, 'return[0]/paused', False)
    690        self.complete_and_wait()
    691
    692    def test_stop_write(self):
    693        self.assert_no_active_block_jobs()
    694
    695        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    696                             mode='existing', target=self.target_img,
    697                             on_target_error='stop')
    698        self.assert_qmp(result, 'return', {})
    699
    700        error = False
    701        ready = False
    702        while not ready:
    703            for event in self.vm.get_qmp_events(wait=True):
    704                if event['event'] == 'BLOCK_JOB_ERROR':
    705                    self.assert_qmp(event, 'data/device', 'drive0')
    706                    self.assert_qmp(event, 'data/operation', 'write')
    707
    708                    result = self.vm.qmp('query-block-jobs')
    709                    self.assert_qmp(result, 'return[0]/paused', True)
    710                    self.assert_qmp(result, 'return[0]/io-status', 'failed')
    711
    712                    result = self.vm.qmp('block-job-resume', device='drive0')
    713                    self.assert_qmp(result, 'return', {})
    714
    715                    result = self.vm.qmp('query-block-jobs')
    716                    self.assert_qmp(result, 'return[0]/paused', False)
    717                    self.assert_qmp(result, 'return[0]/io-status', 'ok')
    718                    error = True
    719                elif event['event'] == 'BLOCK_JOB_READY':
    720                    self.assertTrue(error, 'job completed unexpectedly')
    721                    self.assert_qmp(event, 'data/device', 'drive0')
    722                    ready = True
    723
    724        self.complete_and_wait(wait_ready=False)
    725        self.assert_no_active_block_jobs()
    726
    727class TestSetSpeed(iotests.QMPTestCase):
    728    image_len = 80 * 1024 * 1024 # MB
    729
    730    def setUp(self):
    731        qemu_img('create', backing_img, str(TestSetSpeed.image_len))
    732        qemu_img('create', '-f', iotests.imgfmt,
    733                 '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
    734        self.vm = iotests.VM().add_drive(test_img)
    735        self.vm.launch()
    736
    737    def tearDown(self):
    738        self.vm.shutdown()
    739        os.remove(test_img)
    740        os.remove(backing_img)
    741        os.remove(target_img)
    742
    743    def test_set_speed(self):
    744        self.assert_no_active_block_jobs()
    745
    746        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    747                             target=target_img)
    748        self.assert_qmp(result, 'return', {})
    749
    750        # Default speed is 0
    751        result = self.vm.qmp('query-block-jobs')
    752        self.assert_qmp(result, 'return[0]/device', 'drive0')
    753        self.assert_qmp(result, 'return[0]/speed', 0)
    754
    755        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
    756        self.assert_qmp(result, 'return', {})
    757
    758        # Ensure the speed we set was accepted
    759        result = self.vm.qmp('query-block-jobs')
    760        self.assert_qmp(result, 'return[0]/device', 'drive0')
    761        self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
    762
    763        self.wait_ready_and_cancel()
    764
    765        # Check setting speed in drive-mirror works
    766        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    767                             target=target_img, speed=4*1024*1024)
    768        self.assert_qmp(result, 'return', {})
    769
    770        result = self.vm.qmp('query-block-jobs')
    771        self.assert_qmp(result, 'return[0]/device', 'drive0')
    772        self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
    773
    774        self.wait_ready_and_cancel()
    775
    776    def test_set_speed_invalid(self):
    777        self.assert_no_active_block_jobs()
    778
    779        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    780                             target=target_img, speed=-1)
    781        self.assert_qmp(result, 'error/class', 'GenericError')
    782
    783        self.assert_no_active_block_jobs()
    784
    785        result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    786                             target=target_img)
    787        self.assert_qmp(result, 'return', {})
    788
    789        result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
    790        self.assert_qmp(result, 'error/class', 'GenericError')
    791
    792        self.wait_ready_and_cancel()
    793
    794class TestUnbackedSource(iotests.QMPTestCase):
    795    image_len = 2 * 1024 * 1024 # MB
    796
    797    def setUp(self):
    798        qemu_img('create', '-f', iotests.imgfmt, test_img,
    799                 str(TestUnbackedSource.image_len))
    800        self.vm = iotests.VM()
    801        self.vm.launch()
    802        result = self.vm.qmp('blockdev-add', node_name='drive0',
    803                             driver=iotests.imgfmt,
    804                             file={
    805                                 'driver': 'file',
    806                                 'filename': test_img,
    807                             })
    808        self.assert_qmp(result, 'return', {})
    809
    810    def tearDown(self):
    811        self.vm.shutdown()
    812        os.remove(test_img)
    813        os.remove(target_img)
    814
    815    def test_absolute_paths_full(self):
    816        self.assert_no_active_block_jobs()
    817        result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    818                             sync='full', target=target_img,
    819                             mode='absolute-paths')
    820        self.assert_qmp(result, 'return', {})
    821        self.complete_and_wait()
    822        self.assert_no_active_block_jobs()
    823
    824    def test_absolute_paths_top(self):
    825        self.assert_no_active_block_jobs()
    826        result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    827                             sync='top', target=target_img,
    828                             mode='absolute-paths')
    829        self.assert_qmp(result, 'return', {})
    830        self.complete_and_wait()
    831        self.assert_no_active_block_jobs()
    832
    833    def test_absolute_paths_none(self):
    834        self.assert_no_active_block_jobs()
    835        result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    836                             sync='none', target=target_img,
    837                             mode='absolute-paths')
    838        self.assert_qmp(result, 'return', {})
    839        self.complete_and_wait()
    840        self.assert_no_active_block_jobs()
    841
    842    def test_existing_full(self):
    843        qemu_img('create', '-f', iotests.imgfmt, target_img,
    844                 str(self.image_len))
    845        qemu_io('-c', 'write -P 42 0 64k', target_img)
    846
    847        self.assert_no_active_block_jobs()
    848        result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    849                             sync='full', target=target_img, mode='existing')
    850        self.assert_qmp(result, 'return', {})
    851        self.complete_and_wait()
    852        self.assert_no_active_block_jobs()
    853
    854        result = self.vm.qmp('blockdev-del', node_name='drive0')
    855        self.assert_qmp(result, 'return', {})
    856
    857        self.assertTrue(iotests.compare_images(test_img, target_img),
    858                        'target image does not match source after mirroring')
    859
    860    def test_blockdev_full(self):
    861        qemu_img('create', '-f', iotests.imgfmt, target_img,
    862                 str(self.image_len))
    863        qemu_io('-c', 'write -P 42 0 64k', target_img)
    864
    865        result = self.vm.qmp('blockdev-add', node_name='target',
    866                             driver=iotests.imgfmt,
    867                             file={
    868                                 'driver': 'file',
    869                                 'filename': target_img,
    870                             })
    871        self.assert_qmp(result, 'return', {})
    872
    873        self.assert_no_active_block_jobs()
    874        result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0',
    875                             sync='full', target='target')
    876        self.assert_qmp(result, 'return', {})
    877        self.complete_and_wait()
    878        self.assert_no_active_block_jobs()
    879
    880        result = self.vm.qmp('blockdev-del', node_name='drive0')
    881        self.assert_qmp(result, 'return', {})
    882
    883        result = self.vm.qmp('blockdev-del', node_name='target')
    884        self.assert_qmp(result, 'return', {})
    885
    886        self.assertTrue(iotests.compare_images(test_img, target_img),
    887                        'target image does not match source after mirroring')
    888
    889class TestGranularity(iotests.QMPTestCase):
    890    image_len = 10 * 1024 * 1024 # MB
    891
    892    def setUp(self):
    893        qemu_img('create', '-f', iotests.imgfmt, test_img,
    894                 str(TestGranularity.image_len))
    895        qemu_io('-c', 'write 0 %d' % (self.image_len),
    896                test_img)
    897        self.vm = iotests.VM().add_drive(test_img)
    898        self.vm.launch()
    899
    900    def tearDown(self):
    901        self.vm.shutdown()
    902        self.assertTrue(iotests.compare_images(test_img, target_img),
    903                        'target image does not match source after mirroring')
    904        os.remove(test_img)
    905        os.remove(target_img)
    906
    907    def test_granularity(self):
    908        self.assert_no_active_block_jobs()
    909        result = self.vm.qmp('drive-mirror', device='drive0',
    910                             sync='full', target=target_img,
    911                             mode='absolute-paths', granularity=8192)
    912        self.assert_qmp(result, 'return', {})
    913
    914        event = self.vm.get_qmp_event(wait=60.0)
    915        while event['event'] == 'JOB_STATUS_CHANGE':
    916            self.assert_qmp(event, 'data/id', 'drive0')
    917            event = self.vm.get_qmp_event(wait=60.0)
    918
    919        # Failures will manifest as COMPLETED/ERROR.
    920        self.assert_qmp(event, 'event', 'BLOCK_JOB_READY')
    921        self.complete_and_wait(drive='drive0', wait_ready=False)
    922        self.assert_no_active_block_jobs()
    923
    924class TestRepairQuorum(iotests.QMPTestCase):
    925    """ This class test quorum file repair using drive-mirror.
    926        It's mostly a fork of TestSingleDrive """
    927    image_len = 1 * 1024 * 1024 # MB
    928    IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
    929
    930    @iotests.skip_if_unsupported(['quorum'])
    931    def setUp(self):
    932        self.vm = iotests.VM()
    933
    934        if iotests.qemu_default_machine == 'pc':
    935            self.vm.add_drive(None, 'media=cdrom', 'ide')
    936
    937        # Add each individual quorum images
    938        for i in self.IMAGES:
    939            qemu_img('create', '-f', iotests.imgfmt, i,
    940                     str(self.image_len))
    941            # Assign a node name to each quorum image in order to manipulate
    942            # them
    943            opts = "node-name=img%i" % self.IMAGES.index(i)
    944            opts += ',driver=%s' % iotests.imgfmt
    945            opts += ',file.driver=file'
    946            opts += ',file.filename=%s' % i
    947            self.vm = self.vm.add_blockdev(opts)
    948
    949        self.vm.launch()
    950
    951        #assemble the quorum block device from the individual files
    952        args = { "driver": "quorum", "node-name": "quorum0",
    953                 "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
    954        result = self.vm.qmp("blockdev-add", **args)
    955        self.assert_qmp(result, 'return', {})
    956
    957
    958    def tearDown(self):
    959        self.vm.shutdown()
    960        for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file,
    961                                 nbd_sock_path ]:
    962            # Do a try/except because the test may have deleted some images
    963            try:
    964                os.remove(i)
    965            except OSError:
    966                pass
    967
    968    def test_complete(self):
    969        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
    970                             sync='full', node_name="repair0", replaces="img1",
    971                             target=quorum_repair_img, format=iotests.imgfmt)
    972        self.assert_qmp(result, 'return', {})
    973
    974        self.complete_and_wait(drive="job0")
    975        self.assert_has_block_node("repair0", quorum_repair_img)
    976        self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
    977        self.vm.shutdown()
    978        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
    979                        'target image does not match source after mirroring')
    980
    981    def test_cancel(self):
    982        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
    983                             sync='full', node_name="repair0", replaces="img1",
    984                             target=quorum_repair_img, format=iotests.imgfmt)
    985        self.assert_qmp(result, 'return', {})
    986
    987        self.cancel_and_wait(drive="job0", force=True)
    988        # here we check that the last registered quorum file has not been
    989        # swapped out and unref
    990        self.assert_has_block_node(None, quorum_img3)
    991
    992    def test_cancel_after_ready(self):
    993        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
    994                             sync='full', node_name="repair0", replaces="img1",
    995                             target=quorum_repair_img, format=iotests.imgfmt)
    996        self.assert_qmp(result, 'return', {})
    997
    998        self.wait_ready_and_cancel(drive="job0")
    999        # here we check that the last registered quorum file has not been
   1000        # swapped out and unref
   1001        self.assert_has_block_node(None, quorum_img3)
   1002        self.vm.shutdown()
   1003        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
   1004                        'target image does not match source after mirroring')
   1005
   1006    def test_pause(self):
   1007        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1008                             sync='full', node_name="repair0", replaces="img1",
   1009                             target=quorum_repair_img, format=iotests.imgfmt)
   1010        self.assert_qmp(result, 'return', {})
   1011
   1012        self.pause_job('job0')
   1013
   1014        result = self.vm.qmp('query-block-jobs')
   1015        offset = self.dictpath(result, 'return[0]/offset')
   1016
   1017        time.sleep(0.5)
   1018        result = self.vm.qmp('query-block-jobs')
   1019        self.assert_qmp(result, 'return[0]/offset', offset)
   1020
   1021        result = self.vm.qmp('block-job-resume', device='job0')
   1022        self.assert_qmp(result, 'return', {})
   1023
   1024        self.complete_and_wait(drive="job0")
   1025        self.vm.shutdown()
   1026        self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
   1027                        'target image does not match source after mirroring')
   1028
   1029    def test_medium_not_found(self):
   1030        if iotests.qemu_default_machine != 'pc':
   1031            return
   1032
   1033        result = self.vm.qmp('drive-mirror', job_id='job0', device='drive0', # CD-ROM
   1034                             sync='full',
   1035                             node_name='repair0',
   1036                             replaces='img1',
   1037                             target=quorum_repair_img, format=iotests.imgfmt)
   1038        self.assert_qmp(result, 'error/class', 'GenericError')
   1039
   1040    def test_image_not_found(self):
   1041        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1042                             sync='full', node_name='repair0', replaces='img1',
   1043                             mode='existing', target=quorum_repair_img,
   1044                             format=iotests.imgfmt)
   1045        self.assert_qmp(result, 'error/class', 'GenericError')
   1046
   1047    def test_device_not_found(self):
   1048        result = self.vm.qmp('drive-mirror', job_id='job0',
   1049                             device='nonexistent', sync='full',
   1050                             node_name='repair0',
   1051                             replaces='img1',
   1052                             target=quorum_repair_img, format=iotests.imgfmt)
   1053        self.assert_qmp(result, 'error/class', 'GenericError')
   1054
   1055    def test_wrong_sync_mode(self):
   1056        result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0',
   1057                             node_name='repair0',
   1058                             replaces='img1',
   1059                             target=quorum_repair_img, format=iotests.imgfmt)
   1060        self.assert_qmp(result, 'error/class', 'GenericError')
   1061
   1062    def test_no_node_name(self):
   1063        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1064                             sync='full', replaces='img1',
   1065                             target=quorum_repair_img, format=iotests.imgfmt)
   1066        self.assert_qmp(result, 'error/class', 'GenericError')
   1067
   1068    def test_nonexistent_replaces(self):
   1069        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1070                             sync='full', node_name='repair0', replaces='img77',
   1071                             target=quorum_repair_img, format=iotests.imgfmt)
   1072        self.assert_qmp(result, 'error/class', 'GenericError')
   1073
   1074    def test_after_a_quorum_snapshot(self):
   1075        result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
   1076                             snapshot_file=quorum_snapshot_file,
   1077                             snapshot_node_name="snap1");
   1078
   1079        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1080                             sync='full', node_name='repair0', replaces="img1",
   1081                             target=quorum_repair_img, format=iotests.imgfmt)
   1082        self.assert_qmp(result, 'error/class', 'GenericError')
   1083
   1084        result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1085                             sync='full', node_name='repair0', replaces="snap1",
   1086                             target=quorum_repair_img, format=iotests.imgfmt)
   1087        self.assert_qmp(result, 'return', {})
   1088
   1089        self.complete_and_wait('job0')
   1090        self.assert_has_block_node("repair0", quorum_repair_img)
   1091        self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
   1092
   1093    def test_with_other_parent(self):
   1094        """
   1095        Check that we cannot replace a Quorum child when it has other
   1096        parents.
   1097        """
   1098        result = self.vm.qmp('nbd-server-start',
   1099                             addr={
   1100                                 'type': 'unix',
   1101                                 'data': {'path': nbd_sock_path}
   1102                             })
   1103        self.assert_qmp(result, 'return', {})
   1104
   1105        result = self.vm.qmp('nbd-server-add', device='img1')
   1106        self.assert_qmp(result, 'return', {})
   1107
   1108        result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
   1109                             sync='full', node_name='repair0', replaces='img1',
   1110                             target=quorum_repair_img, format=iotests.imgfmt)
   1111        self.assert_qmp(result, 'error/desc',
   1112                        "Cannot replace 'img1' by a node mirrored from "
   1113                        "'quorum0', because it cannot be guaranteed that doing "
   1114                        "so would not lead to an abrupt change of visible data")
   1115
   1116    def test_with_other_parents_after_mirror_start(self):
   1117        """
   1118        The same as test_with_other_parent(), but add the NBD server
   1119        only when the mirror job is already running.
   1120        """
   1121        result = self.vm.qmp('nbd-server-start',
   1122                             addr={
   1123                                 'type': 'unix',
   1124                                 'data': {'path': nbd_sock_path}
   1125                             })
   1126        self.assert_qmp(result, 'return', {})
   1127
   1128        result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
   1129                             sync='full', node_name='repair0', replaces='img1',
   1130                             target=quorum_repair_img, format=iotests.imgfmt)
   1131        self.assert_qmp(result, 'return', {})
   1132
   1133        result = self.vm.qmp('nbd-server-add', device='img1')
   1134        self.assert_qmp(result, 'return', {})
   1135
   1136        # The full error message goes to stderr, we will check it later
   1137        self.complete_and_wait('mirror',
   1138                               completion_error='Operation not permitted')
   1139
   1140        # Should not have been replaced
   1141        self.vm.assert_block_path('quorum0', '/children.1', 'img1')
   1142
   1143        # Check the full error message now
   1144        self.vm.shutdown()
   1145        log = self.vm.get_log()
   1146        log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
   1147        log = re.sub(r'^Formatting.*\n', '', log)
   1148        log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
   1149        log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
   1150
   1151        self.assertEqual(log,
   1152                         "Can no longer replace 'img1' by 'repair0', because " +
   1153                         "it can no longer be guaranteed that doing so would " +
   1154                         "not lead to an abrupt change of visible data")
   1155
   1156
   1157# Test mirroring with a source that does not have any parents (not even a
   1158# BlockBackend)
   1159class TestOrphanedSource(iotests.QMPTestCase):
   1160    def setUp(self):
   1161        blk0 = { 'node-name': 'src',
   1162                 'driver': 'null-co' }
   1163
   1164        blk1 = { 'node-name': 'dest',
   1165                 'driver': 'null-co' }
   1166
   1167        blk2 = { 'node-name': 'dest-ro',
   1168                 'driver': 'null-co',
   1169                 'read-only': 'on' }
   1170
   1171        self.vm = iotests.VM()
   1172        self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
   1173        self.vm.add_blockdev(self.vm.qmp_to_opts(blk1))
   1174        self.vm.add_blockdev(self.vm.qmp_to_opts(blk2))
   1175        self.vm.launch()
   1176
   1177    def tearDown(self):
   1178        self.vm.shutdown()
   1179
   1180    def test_no_job_id(self):
   1181        self.assert_no_active_block_jobs()
   1182
   1183        result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
   1184                             target='dest')
   1185        self.assert_qmp(result, 'error/class', 'GenericError')
   1186
   1187    def test_success(self):
   1188        self.assert_no_active_block_jobs()
   1189
   1190        result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
   1191                             sync='full', target='dest')
   1192        self.assert_qmp(result, 'return', {})
   1193
   1194        self.complete_and_wait('job')
   1195
   1196    def test_failing_permissions(self):
   1197        self.assert_no_active_block_jobs()
   1198
   1199        result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
   1200                             target='dest-ro')
   1201        self.assert_qmp(result, 'error/class', 'GenericError')
   1202
   1203    def test_failing_permission_in_complete(self):
   1204        self.assert_no_active_block_jobs()
   1205
   1206        # Unshare consistent-read on the target
   1207        # (The mirror job does not care)
   1208        result = self.vm.qmp('blockdev-add',
   1209                             driver='blkdebug',
   1210                             node_name='dest-perm',
   1211                             image='dest',
   1212                             unshare_child_perms=['consistent-read'])
   1213        self.assert_qmp(result, 'return', {})
   1214
   1215        result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
   1216                             sync='full', target='dest',
   1217                             filter_node_name='mirror-filter')
   1218        self.assert_qmp(result, 'return', {})
   1219
   1220        # Require consistent-read on the source
   1221        # (We can only add this node once the job has started, or it
   1222        # will complain that it does not want to run on non-root nodes)
   1223        result = self.vm.qmp('blockdev-add',
   1224                             driver='blkdebug',
   1225                             node_name='src-perm',
   1226                             image='src',
   1227                             take_child_perms=['consistent-read'])
   1228        self.assert_qmp(result, 'return', {})
   1229
   1230        # While completing, mirror will attempt to replace src by
   1231        # dest, which must fail because src-perm requires
   1232        # consistent-read but dest-perm does not share it; thus
   1233        # aborting the job when it is supposed to complete
   1234        self.complete_and_wait('job',
   1235                               completion_error='Operation not permitted')
   1236
   1237        # Assert that all of our nodes are still there (except for the
   1238        # mirror filter, which should be gone despite the failure)
   1239        nodes = self.vm.qmp('query-named-block-nodes')['return']
   1240        nodes = [node['node-name'] for node in nodes]
   1241
   1242        for expect in ('src', 'src-perm', 'dest', 'dest-perm'):
   1243            self.assertTrue(expect in nodes, '%s disappeared' % expect)
   1244        self.assertFalse('mirror-filter' in nodes,
   1245                         'Mirror filter node did not disappear')
   1246
   1247# Test cases for @replaces that do not necessarily involve Quorum
   1248class TestReplaces(iotests.QMPTestCase):
   1249    # Each of these test cases needs their own block graph, so do not
   1250    # create any nodes here
   1251    def setUp(self):
   1252        self.vm = iotests.VM()
   1253        self.vm.launch()
   1254
   1255    def tearDown(self):
   1256        self.vm.shutdown()
   1257        for img in (test_img, target_img):
   1258            try:
   1259                os.remove(img)
   1260            except OSError:
   1261                pass
   1262
   1263    @iotests.skip_if_unsupported(['copy-on-read'])
   1264    def test_replace_filter(self):
   1265        """
   1266        Check that we can replace filter nodes.
   1267        """
   1268        result = self.vm.qmp('blockdev-add', **{
   1269                                 'driver': 'copy-on-read',
   1270                                 'node-name': 'filter0',
   1271                                 'file': {
   1272                                     'driver': 'copy-on-read',
   1273                                     'node-name': 'filter1',
   1274                                     'file': {
   1275                                         'driver': 'null-co'
   1276                                     }
   1277                                 }
   1278                             })
   1279        self.assert_qmp(result, 'return', {})
   1280
   1281        result = self.vm.qmp('blockdev-add',
   1282                             node_name='target', driver='null-co')
   1283        self.assert_qmp(result, 'return', {})
   1284
   1285        result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0',
   1286                             target='target', sync='full', replaces='filter1')
   1287        self.assert_qmp(result, 'return', {})
   1288
   1289        self.complete_and_wait('mirror')
   1290
   1291        self.vm.assert_block_path('filter0', '/file', 'target')
   1292
   1293# Tests for mirror with filters (and how the mirror filter behaves, as
   1294# an example for an implicit filter)
   1295class TestFilters(iotests.QMPTestCase):
   1296    def setUp(self):
   1297        qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
   1298        qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
   1299                 '-F', iotests.imgfmt, test_img)
   1300        qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
   1301                 '-F', iotests.imgfmt, target_img)
   1302
   1303        qemu_io('-c', 'write -P 1 0 512k', backing_img)
   1304        qemu_io('-c', 'write -P 2 512k 512k', test_img)
   1305
   1306        self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi')
   1307        self.vm.launch()
   1308
   1309        result = self.vm.qmp('blockdev-add', **{
   1310                                'node-name': 'target',
   1311                                'driver': iotests.imgfmt,
   1312                                'file': {
   1313                                    'driver': 'file',
   1314                                    'filename': target_img
   1315                                },
   1316                                'backing': None
   1317                            })
   1318        self.assert_qmp(result, 'return', {})
   1319
   1320        self.filterless_chain = {
   1321                'node-name': 'source',
   1322                'driver': iotests.imgfmt,
   1323                'file': {
   1324                    'driver': 'file',
   1325                    'filename': test_img
   1326                },
   1327                'backing': {
   1328                    'node-name': 'backing',
   1329                    'driver': iotests.imgfmt,
   1330                    'file': {
   1331                        'driver': 'file',
   1332                        'filename': backing_img
   1333                    }
   1334                }
   1335            }
   1336
   1337    def tearDown(self):
   1338        self.vm.shutdown()
   1339
   1340        os.remove(test_img)
   1341        os.remove(target_img)
   1342        os.remove(backing_img)
   1343
   1344    def test_cor(self):
   1345        result = self.vm.qmp('blockdev-add', **{
   1346                                'node-name': 'filter',
   1347                                'driver': 'copy-on-read',
   1348                                'file': self.filterless_chain
   1349                            })
   1350        self.assert_qmp(result, 'return', {})
   1351
   1352        result = self.vm.qmp('blockdev-mirror',
   1353                             job_id='mirror',
   1354                             device='filter',
   1355                             target='target',
   1356                             sync='top')
   1357        self.assert_qmp(result, 'return', {})
   1358
   1359        self.complete_and_wait('mirror')
   1360
   1361        self.vm.qmp('blockdev-del', node_name='target')
   1362
   1363        target_map = qemu_img_pipe('map', '--output=json', target_img)
   1364        target_map = json.loads(target_map)
   1365
   1366        assert target_map[0]['start'] == 0
   1367        assert target_map[0]['length'] == 512 * 1024
   1368        assert target_map[0]['depth'] == 1
   1369
   1370        assert target_map[1]['start'] == 512 * 1024
   1371        assert target_map[1]['length'] == 512 * 1024
   1372        assert target_map[1]['depth'] == 0
   1373
   1374    def test_implicit_mirror_filter(self):
   1375        result = self.vm.qmp('blockdev-add', **self.filterless_chain)
   1376        self.assert_qmp(result, 'return', {})
   1377
   1378        # We need this so we can query from above the mirror node
   1379        result = self.vm.qmp('device_add',
   1380                             driver='scsi-hd',
   1381                             id='virtio',
   1382                             bus='vio-scsi.0',
   1383                             drive='source')
   1384        self.assert_qmp(result, 'return', {})
   1385
   1386        result = self.vm.qmp('blockdev-mirror',
   1387                             job_id='mirror',
   1388                             device='source',
   1389                             target='target',
   1390                             sync='top')
   1391        self.assert_qmp(result, 'return', {})
   1392
   1393        # The mirror filter is now an implicit node, so it should be
   1394        # invisible when querying the backing chain
   1395        blockdevs = self.vm.qmp('query-block')['return']
   1396        device_info = next(dev for dev in blockdevs if dev['qdev'] == 'virtio')
   1397
   1398        assert device_info['inserted']['node-name'] == 'source'
   1399
   1400        image_info = device_info['inserted']['image']
   1401        assert image_info['filename'] == test_img
   1402        assert image_info['backing-image']['filename'] == backing_img
   1403
   1404        self.complete_and_wait('mirror')
   1405
   1406    def test_explicit_mirror_filter(self):
   1407        # Same test as above, but this time we give the mirror filter
   1408        # a node-name so it will not be invisible
   1409        result = self.vm.qmp('blockdev-add', **self.filterless_chain)
   1410        self.assert_qmp(result, 'return', {})
   1411
   1412        # We need this so we can query from above the mirror node
   1413        result = self.vm.qmp('device_add',
   1414                             driver='scsi-hd',
   1415                             id='virtio',
   1416                             bus='vio-scsi.0',
   1417                             drive='source')
   1418        self.assert_qmp(result, 'return', {})
   1419
   1420        result = self.vm.qmp('blockdev-mirror',
   1421                             job_id='mirror',
   1422                             device='source',
   1423                             target='target',
   1424                             sync='top',
   1425                             filter_node_name='mirror-filter')
   1426        self.assert_qmp(result, 'return', {})
   1427
   1428        # With a node-name given to it, the mirror filter should now
   1429        # be visible
   1430        blockdevs = self.vm.qmp('query-block')['return']
   1431        device_info = next(dev for dev in blockdevs if dev['qdev'] == 'virtio')
   1432
   1433        assert device_info['inserted']['node-name'] == 'mirror-filter'
   1434
   1435        self.complete_and_wait('mirror')
   1436
   1437
   1438if __name__ == '__main__':
   1439    iotests.main(supported_fmts=['qcow2', 'qed'],
   1440                 supported_protocols=['file'],
   1441                 supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd'])