qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

041 (58949B)


      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 
     22 import time
     23 import os
     24 import re
     25 import json
     26 import iotests
     27 from iotests import qemu_img, qemu_img_map, qemu_io
     28 
     29 backing_img = os.path.join(iotests.test_dir, 'backing.img')
     30 target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img')
     31 test_img = os.path.join(iotests.test_dir, 'test.img')
     32 target_img = os.path.join(iotests.test_dir, 'target.img')
     33 
     34 quorum_img1 = os.path.join(iotests.test_dir, 'quorum1.img')
     35 quorum_img2 = os.path.join(iotests.test_dir, 'quorum2.img')
     36 quorum_img3 = os.path.join(iotests.test_dir, 'quorum3.img')
     37 quorum_repair_img = os.path.join(iotests.test_dir, 'quorum_repair.img')
     38 quorum_snapshot_file = os.path.join(iotests.test_dir, 'quorum_snapshot.img')
     39 
     40 nbd_sock_path = os.path.join(iotests.sock_dir, 'nbd.sock')
     41 
     42 class 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 
    228 class 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 
    297 class TestSingleDriveZeroLength(TestSingleDrive):
    298     image_len = 0
    299     test_small_buffer2 = None
    300     test_large_cluster = None
    301 
    302 class TestSingleBlockdevZeroLength(TestSingleBlockdev):
    303     image_len = 0
    304     test_small_target = None
    305     test_large_target = None
    306 
    307 class TestSingleDriveUnalignedLength(TestSingleDrive):
    308     image_len = 1025 * 1024
    309     test_small_buffer2 = None
    310     test_large_cluster = None
    311 
    312 class TestSingleBlockdevUnalignedLength(TestSingleBlockdev):
    313     image_len = 1025 * 1024
    314 
    315 class 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 
    388 class 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 
    437 class 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]
    448 state = "1"
    449 event = "%s"
    450 errno = "%d"
    451 immediately = "off"
    452 once = "on"
    453 sector = "%d"
    454 
    455 [set-state]
    456 state = "1"
    457 event = "%s"
    458 new_state = "2"
    459 
    460 [set-state]
    461 state = "2"
    462 event = "%s"
    463 new_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.assertIn(result['return'][0]['status'], ['running', 'ready'])
    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.assertIn(result['return'][0]['status'], ['running', 'ready'])
    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                     if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused':
    584                         self.vm.events_wait([(
    585                             'JOB_STATUS_CHANGE',
    586                             {'data': {'id': 'drive0', 'status': 'paused'}}
    587                         )])
    588 
    589                     result = self.vm.qmp('query-block-jobs')
    590                     self.assert_qmp(result, 'return[0]/status', 'paused')
    591                     self.assert_qmp(result, 'return[0]/io-status', 'failed')
    592 
    593                     result = self.vm.qmp('block-job-resume', device='drive0')
    594                     self.assert_qmp(result, 'return', {})
    595                     error = True
    596                 elif event['event'] == 'BLOCK_JOB_READY':
    597                     self.assertTrue(error, 'job completed unexpectedly')
    598                     self.assert_qmp(event, 'data/device', 'drive0')
    599                     ready = True
    600 
    601         result = self.vm.qmp('query-block-jobs')
    602         self.assert_qmp(result, 'return[0]/status', 'ready')
    603         self.assert_qmp(result, 'return[0]/io-status', 'ok')
    604 
    605         self.complete_and_wait(wait_ready=False)
    606         self.assert_no_active_block_jobs()
    607 
    608 class TestWriteErrors(iotests.QMPTestCase):
    609     image_len = 2 * 1024 * 1024 # MB
    610 
    611     # this should be a multiple of twice the default granularity
    612     # so that we hit this offset first in state 1
    613     MIRROR_GRANULARITY = 1024 * 1024
    614 
    615     def create_blkdebug_file(self, name, event, errno):
    616         file = open(name, 'w')
    617         file.write('''
    618 [inject-error]
    619 state = "1"
    620 event = "%s"
    621 errno = "%d"
    622 immediately = "off"
    623 once = "on"
    624 sector = "%d"
    625 
    626 [set-state]
    627 state = "1"
    628 event = "%s"
    629 new_state = "2"
    630 
    631 [set-state]
    632 state = "2"
    633 event = "%s"
    634 new_state = "1"
    635 ''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event))
    636         file.close()
    637 
    638     def setUp(self):
    639         self.blkdebug_file = target_img + ".blkdebug"
    640         iotests.create_image(backing_img, TestWriteErrors.image_len)
    641         self.create_blkdebug_file(self.blkdebug_file, "write_aio", 5)
    642         qemu_img('create', '-f', iotests.imgfmt,
    643                  '-obacking_file=%s' %(backing_img), '-F', 'raw', test_img)
    644         self.vm = iotests.VM().add_drive(test_img)
    645         self.target_img = 'blkdebug:%s:%s' % (self.blkdebug_file, target_img)
    646         qemu_img('create', '-f', iotests.imgfmt, '-osize=%d' %(TestWriteErrors.image_len), target_img)
    647         self.vm.launch()
    648 
    649     def tearDown(self):
    650         self.vm.shutdown()
    651         os.remove(test_img)
    652         os.remove(target_img)
    653         os.remove(backing_img)
    654         os.remove(self.blkdebug_file)
    655 
    656     def test_report_write(self):
    657         self.assert_no_active_block_jobs()
    658 
    659         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    660                              mode='existing', target=self.target_img)
    661         self.assert_qmp(result, 'return', {})
    662 
    663         completed = False
    664         error = False
    665         while not completed:
    666             for event in self.vm.get_qmp_events(wait=True):
    667                 if event['event'] == 'BLOCK_JOB_ERROR':
    668                     self.assert_qmp(event, 'data/device', 'drive0')
    669                     self.assert_qmp(event, 'data/operation', 'write')
    670                     error = True
    671                 elif event['event'] == 'BLOCK_JOB_READY':
    672                     self.assertTrue(False, 'job completed unexpectedly')
    673                 elif event['event'] == 'BLOCK_JOB_COMPLETED':
    674                     self.assertTrue(error, 'job completed unexpectedly')
    675                     self.assert_qmp(event, 'data/type', 'mirror')
    676                     self.assert_qmp(event, 'data/device', 'drive0')
    677                     self.assert_qmp(event, 'data/error', 'Input/output error')
    678                     completed = True
    679 
    680         self.assert_no_active_block_jobs()
    681 
    682     def test_ignore_write(self):
    683         self.assert_no_active_block_jobs()
    684 
    685         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    686                              mode='existing', target=self.target_img,
    687                              on_target_error='ignore')
    688         self.assert_qmp(result, 'return', {})
    689 
    690         event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
    691         self.assertEqual(event['event'], 'BLOCK_JOB_ERROR')
    692         self.assert_qmp(event, 'data/device', 'drive0')
    693         self.assert_qmp(event, 'data/operation', 'write')
    694         result = self.vm.qmp('query-block-jobs')
    695         self.assertIn(result['return'][0]['status'], ['running', 'ready'])
    696         self.complete_and_wait()
    697 
    698     def test_stop_write(self):
    699         self.assert_no_active_block_jobs()
    700 
    701         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    702                              mode='existing', target=self.target_img,
    703                              on_target_error='stop')
    704         self.assert_qmp(result, 'return', {})
    705 
    706         error = False
    707         ready = False
    708         while not ready:
    709             for event in self.vm.get_qmp_events(wait=True):
    710                 if event['event'] == 'BLOCK_JOB_ERROR':
    711                     self.assert_qmp(event, 'data/device', 'drive0')
    712                     self.assert_qmp(event, 'data/operation', 'write')
    713 
    714                     if self.vm.qmp('query-block-jobs')['return'][0]['status'] != 'paused':
    715                         self.vm.events_wait([(
    716                             'JOB_STATUS_CHANGE',
    717                             {'data': {'id': 'drive0', 'status': 'paused'}}
    718                         )])
    719 
    720                     result = self.vm.qmp('query-block-jobs')
    721                     self.assert_qmp(result, 'return[0]/status', 'paused')
    722                     self.assert_qmp(result, 'return[0]/io-status', 'failed')
    723 
    724                     result = self.vm.qmp('block-job-resume', device='drive0')
    725                     self.assert_qmp(result, 'return', {})
    726 
    727                     result = self.vm.qmp('query-block-jobs')
    728                     self.assertIn(result['return'][0]['status'], ['running', 'ready'])
    729                     self.assert_qmp(result, 'return[0]/io-status', 'ok')
    730                     error = True
    731                 elif event['event'] == 'BLOCK_JOB_READY':
    732                     self.assertTrue(error, 'job completed unexpectedly')
    733                     self.assert_qmp(event, 'data/device', 'drive0')
    734                     ready = True
    735 
    736         self.complete_and_wait(wait_ready=False)
    737         self.assert_no_active_block_jobs()
    738 
    739 class TestSetSpeed(iotests.QMPTestCase):
    740     image_len = 80 * 1024 * 1024 # MB
    741 
    742     def setUp(self):
    743         qemu_img('create', backing_img, str(TestSetSpeed.image_len))
    744         qemu_img('create', '-f', iotests.imgfmt,
    745                  '-o', 'backing_file=%s' % backing_img, '-F', 'raw', test_img)
    746         self.vm = iotests.VM().add_drive(test_img)
    747         self.vm.launch()
    748 
    749     def tearDown(self):
    750         self.vm.shutdown()
    751         os.remove(test_img)
    752         os.remove(backing_img)
    753         os.remove(target_img)
    754 
    755     def test_set_speed(self):
    756         self.assert_no_active_block_jobs()
    757 
    758         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    759                              target=target_img)
    760         self.assert_qmp(result, 'return', {})
    761 
    762         # Default speed is 0
    763         result = self.vm.qmp('query-block-jobs')
    764         self.assert_qmp(result, 'return[0]/device', 'drive0')
    765         self.assert_qmp(result, 'return[0]/speed', 0)
    766 
    767         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=8 * 1024 * 1024)
    768         self.assert_qmp(result, 'return', {})
    769 
    770         # Ensure the speed we set was accepted
    771         result = self.vm.qmp('query-block-jobs')
    772         self.assert_qmp(result, 'return[0]/device', 'drive0')
    773         self.assert_qmp(result, 'return[0]/speed', 8 * 1024 * 1024)
    774 
    775         self.wait_ready_and_cancel()
    776 
    777         # Check setting speed in drive-mirror works
    778         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    779                              target=target_img, speed=4*1024*1024)
    780         self.assert_qmp(result, 'return', {})
    781 
    782         result = self.vm.qmp('query-block-jobs')
    783         self.assert_qmp(result, 'return[0]/device', 'drive0')
    784         self.assert_qmp(result, 'return[0]/speed', 4 * 1024 * 1024)
    785 
    786         self.wait_ready_and_cancel()
    787 
    788     def test_set_speed_invalid(self):
    789         self.assert_no_active_block_jobs()
    790 
    791         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    792                              target=target_img, speed=-1)
    793         self.assert_qmp(result, 'error/class', 'GenericError')
    794 
    795         self.assert_no_active_block_jobs()
    796 
    797         result = self.vm.qmp('drive-mirror', device='drive0', sync='full',
    798                              target=target_img)
    799         self.assert_qmp(result, 'return', {})
    800 
    801         result = self.vm.qmp('block-job-set-speed', device='drive0', speed=-1)
    802         self.assert_qmp(result, 'error/class', 'GenericError')
    803 
    804         self.wait_ready_and_cancel()
    805 
    806 class TestUnbackedSource(iotests.QMPTestCase):
    807     image_len = 2 * 1024 * 1024 # MB
    808 
    809     def setUp(self):
    810         qemu_img('create', '-f', iotests.imgfmt, test_img,
    811                  str(TestUnbackedSource.image_len))
    812         self.vm = iotests.VM()
    813         self.vm.launch()
    814         result = self.vm.qmp('blockdev-add', node_name='drive0',
    815                              driver=iotests.imgfmt,
    816                              file={
    817                                  'driver': 'file',
    818                                  'filename': test_img,
    819                              })
    820         self.assert_qmp(result, 'return', {})
    821 
    822     def tearDown(self):
    823         self.vm.shutdown()
    824         os.remove(test_img)
    825         os.remove(target_img)
    826 
    827     def test_absolute_paths_full(self):
    828         self.assert_no_active_block_jobs()
    829         result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    830                              sync='full', target=target_img,
    831                              mode='absolute-paths')
    832         self.assert_qmp(result, 'return', {})
    833         self.complete_and_wait()
    834         self.assert_no_active_block_jobs()
    835 
    836     def test_absolute_paths_top(self):
    837         self.assert_no_active_block_jobs()
    838         result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    839                              sync='top', target=target_img,
    840                              mode='absolute-paths')
    841         self.assert_qmp(result, 'return', {})
    842         self.complete_and_wait()
    843         self.assert_no_active_block_jobs()
    844 
    845     def test_absolute_paths_none(self):
    846         self.assert_no_active_block_jobs()
    847         result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    848                              sync='none', target=target_img,
    849                              mode='absolute-paths')
    850         self.assert_qmp(result, 'return', {})
    851         self.complete_and_wait()
    852         self.assert_no_active_block_jobs()
    853 
    854     def test_existing_full(self):
    855         qemu_img('create', '-f', iotests.imgfmt, target_img,
    856                  str(self.image_len))
    857         qemu_io('-c', 'write -P 42 0 64k', target_img)
    858 
    859         self.assert_no_active_block_jobs()
    860         result = self.vm.qmp('drive-mirror', job_id='drive0', device='drive0',
    861                              sync='full', target=target_img, mode='existing')
    862         self.assert_qmp(result, 'return', {})
    863         self.complete_and_wait()
    864         self.assert_no_active_block_jobs()
    865 
    866         result = self.vm.qmp('blockdev-del', node_name='drive0')
    867         self.assert_qmp(result, 'return', {})
    868 
    869         self.assertTrue(iotests.compare_images(test_img, target_img),
    870                         'target image does not match source after mirroring')
    871 
    872     def test_blockdev_full(self):
    873         qemu_img('create', '-f', iotests.imgfmt, target_img,
    874                  str(self.image_len))
    875         qemu_io('-c', 'write -P 42 0 64k', target_img)
    876 
    877         result = self.vm.qmp('blockdev-add', node_name='target',
    878                              driver=iotests.imgfmt,
    879                              file={
    880                                  'driver': 'file',
    881                                  'filename': target_img,
    882                              })
    883         self.assert_qmp(result, 'return', {})
    884 
    885         self.assert_no_active_block_jobs()
    886         result = self.vm.qmp('blockdev-mirror', job_id='drive0', device='drive0',
    887                              sync='full', target='target')
    888         self.assert_qmp(result, 'return', {})
    889         self.complete_and_wait()
    890         self.assert_no_active_block_jobs()
    891 
    892         result = self.vm.qmp('blockdev-del', node_name='drive0')
    893         self.assert_qmp(result, 'return', {})
    894 
    895         result = self.vm.qmp('blockdev-del', node_name='target')
    896         self.assert_qmp(result, 'return', {})
    897 
    898         self.assertTrue(iotests.compare_images(test_img, target_img),
    899                         'target image does not match source after mirroring')
    900 
    901 class TestGranularity(iotests.QMPTestCase):
    902     image_len = 10 * 1024 * 1024 # MB
    903 
    904     def setUp(self):
    905         qemu_img('create', '-f', iotests.imgfmt, test_img,
    906                  str(TestGranularity.image_len))
    907         qemu_io('-c', 'write 0 %d' % (self.image_len),
    908                 test_img)
    909         self.vm = iotests.VM().add_drive(test_img)
    910         self.vm.launch()
    911 
    912     def tearDown(self):
    913         self.vm.shutdown()
    914         self.assertTrue(iotests.compare_images(test_img, target_img),
    915                         'target image does not match source after mirroring')
    916         os.remove(test_img)
    917         os.remove(target_img)
    918 
    919     def test_granularity(self):
    920         self.assert_no_active_block_jobs()
    921         result = self.vm.qmp('drive-mirror', device='drive0',
    922                              sync='full', target=target_img,
    923                              mode='absolute-paths', granularity=8192)
    924         self.assert_qmp(result, 'return', {})
    925 
    926         event = self.vm.get_qmp_event(wait=60.0)
    927         while event['event'] == 'JOB_STATUS_CHANGE':
    928             self.assert_qmp(event, 'data/id', 'drive0')
    929             event = self.vm.get_qmp_event(wait=60.0)
    930 
    931         # Failures will manifest as COMPLETED/ERROR.
    932         self.assert_qmp(event, 'event', 'BLOCK_JOB_READY')
    933         self.complete_and_wait(drive='drive0', wait_ready=False)
    934         self.assert_no_active_block_jobs()
    935 
    936 class TestRepairQuorum(iotests.QMPTestCase):
    937     """ This class test quorum file repair using drive-mirror.
    938         It's mostly a fork of TestSingleDrive """
    939     image_len = 1 * 1024 * 1024 # MB
    940     IMAGES = [ quorum_img1, quorum_img2, quorum_img3 ]
    941 
    942     @iotests.skip_if_unsupported(['quorum'])
    943     def setUp(self):
    944         self.vm = iotests.VM()
    945 
    946         if iotests.qemu_default_machine == 'pc':
    947             self.vm.add_drive(None, 'media=cdrom', 'ide')
    948 
    949         # Add each individual quorum images
    950         for i in self.IMAGES:
    951             qemu_img('create', '-f', iotests.imgfmt, i,
    952                      str(self.image_len))
    953             # Assign a node name to each quorum image in order to manipulate
    954             # them
    955             opts = "node-name=img%i" % self.IMAGES.index(i)
    956             opts += ',driver=%s' % iotests.imgfmt
    957             opts += ',file.driver=file'
    958             opts += ',file.filename=%s' % i
    959             self.vm = self.vm.add_blockdev(opts)
    960 
    961         self.vm.launch()
    962 
    963         #assemble the quorum block device from the individual files
    964         args = { "driver": "quorum", "node-name": "quorum0",
    965                  "vote-threshold": 2, "children": [ "img0", "img1", "img2" ] }
    966         result = self.vm.qmp("blockdev-add", **args)
    967         self.assert_qmp(result, 'return', {})
    968 
    969 
    970     def tearDown(self):
    971         self.vm.shutdown()
    972         for i in self.IMAGES + [ quorum_repair_img, quorum_snapshot_file,
    973                                  nbd_sock_path ]:
    974             # Do a try/except because the test may have deleted some images
    975             try:
    976                 os.remove(i)
    977             except OSError:
    978                 pass
    979 
    980     def test_complete(self):
    981         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
    982                              sync='full', node_name="repair0", replaces="img1",
    983                              target=quorum_repair_img, format=iotests.imgfmt)
    984         self.assert_qmp(result, 'return', {})
    985 
    986         self.complete_and_wait(drive="job0")
    987         self.assert_has_block_node("repair0", quorum_repair_img)
    988         self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
    989         self.vm.shutdown()
    990         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
    991                         'target image does not match source after mirroring')
    992 
    993     def test_cancel(self):
    994         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
    995                              sync='full', node_name="repair0", replaces="img1",
    996                              target=quorum_repair_img, format=iotests.imgfmt)
    997         self.assert_qmp(result, 'return', {})
    998 
    999         self.cancel_and_wait(drive="job0", force=True)
   1000         # here we check that the last registered quorum file has not been
   1001         # swapped out and unref
   1002         self.assert_has_block_node(None, quorum_img3)
   1003 
   1004     def test_cancel_after_ready(self):
   1005         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1006                              sync='full', node_name="repair0", replaces="img1",
   1007                              target=quorum_repair_img, format=iotests.imgfmt)
   1008         self.assert_qmp(result, 'return', {})
   1009 
   1010         self.wait_ready_and_cancel(drive="job0")
   1011         # here we check that the last registered quorum file has not been
   1012         # swapped out and unref
   1013         self.assert_has_block_node(None, quorum_img3)
   1014         self.vm.shutdown()
   1015         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
   1016                         'target image does not match source after mirroring')
   1017 
   1018     def test_pause(self):
   1019         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1020                              sync='full', node_name="repair0", replaces="img1",
   1021                              target=quorum_repair_img, format=iotests.imgfmt)
   1022         self.assert_qmp(result, 'return', {})
   1023 
   1024         self.pause_job('job0')
   1025 
   1026         result = self.vm.qmp('query-block-jobs')
   1027         offset = self.dictpath(result, 'return[0]/offset')
   1028 
   1029         time.sleep(0.5)
   1030         result = self.vm.qmp('query-block-jobs')
   1031         self.assert_qmp(result, 'return[0]/offset', offset)
   1032 
   1033         result = self.vm.qmp('block-job-resume', device='job0')
   1034         self.assert_qmp(result, 'return', {})
   1035 
   1036         self.complete_and_wait(drive="job0")
   1037         self.vm.shutdown()
   1038         self.assertTrue(iotests.compare_images(quorum_img2, quorum_repair_img),
   1039                         'target image does not match source after mirroring')
   1040 
   1041     def test_medium_not_found(self):
   1042         if iotests.qemu_default_machine != 'pc':
   1043             return
   1044 
   1045         result = self.vm.qmp('drive-mirror', job_id='job0', device='drive0', # CD-ROM
   1046                              sync='full',
   1047                              node_name='repair0',
   1048                              replaces='img1',
   1049                              target=quorum_repair_img, format=iotests.imgfmt)
   1050         self.assert_qmp(result, 'error/class', 'GenericError')
   1051 
   1052     def test_image_not_found(self):
   1053         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1054                              sync='full', node_name='repair0', replaces='img1',
   1055                              mode='existing', target=quorum_repair_img,
   1056                              format=iotests.imgfmt)
   1057         self.assert_qmp(result, 'error/class', 'GenericError')
   1058 
   1059     def test_device_not_found(self):
   1060         result = self.vm.qmp('drive-mirror', job_id='job0',
   1061                              device='nonexistent', sync='full',
   1062                              node_name='repair0',
   1063                              replaces='img1',
   1064                              target=quorum_repair_img, format=iotests.imgfmt)
   1065         self.assert_qmp(result, 'error/class', 'GenericError')
   1066 
   1067     def test_wrong_sync_mode(self):
   1068         result = self.vm.qmp('drive-mirror', device='quorum0', job_id='job0',
   1069                              node_name='repair0',
   1070                              replaces='img1',
   1071                              target=quorum_repair_img, format=iotests.imgfmt)
   1072         self.assert_qmp(result, 'error/class', 'GenericError')
   1073 
   1074     def test_no_node_name(self):
   1075         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1076                              sync='full', replaces='img1',
   1077                              target=quorum_repair_img, format=iotests.imgfmt)
   1078         self.assert_qmp(result, 'error/class', 'GenericError')
   1079 
   1080     def test_nonexistent_replaces(self):
   1081         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1082                              sync='full', node_name='repair0', replaces='img77',
   1083                              target=quorum_repair_img, format=iotests.imgfmt)
   1084         self.assert_qmp(result, 'error/class', 'GenericError')
   1085 
   1086     def test_after_a_quorum_snapshot(self):
   1087         result = self.vm.qmp('blockdev-snapshot-sync', node_name='img1',
   1088                              snapshot_file=quorum_snapshot_file,
   1089                              snapshot_node_name="snap1");
   1090 
   1091         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1092                              sync='full', node_name='repair0', replaces="img1",
   1093                              target=quorum_repair_img, format=iotests.imgfmt)
   1094         self.assert_qmp(result, 'error/class', 'GenericError')
   1095 
   1096         result = self.vm.qmp('drive-mirror', job_id='job0', device='quorum0',
   1097                              sync='full', node_name='repair0', replaces="snap1",
   1098                              target=quorum_repair_img, format=iotests.imgfmt)
   1099         self.assert_qmp(result, 'return', {})
   1100 
   1101         self.complete_and_wait('job0')
   1102         self.assert_has_block_node("repair0", quorum_repair_img)
   1103         self.vm.assert_block_path('quorum0', '/children.1', 'repair0')
   1104 
   1105     def test_with_other_parent(self):
   1106         """
   1107         Check that we cannot replace a Quorum child when it has other
   1108         parents.
   1109         """
   1110         result = self.vm.qmp('nbd-server-start',
   1111                              addr={
   1112                                  'type': 'unix',
   1113                                  'data': {'path': nbd_sock_path}
   1114                              })
   1115         self.assert_qmp(result, 'return', {})
   1116 
   1117         result = self.vm.qmp('nbd-server-add', device='img1')
   1118         self.assert_qmp(result, 'return', {})
   1119 
   1120         result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
   1121                              sync='full', node_name='repair0', replaces='img1',
   1122                              target=quorum_repair_img, format=iotests.imgfmt)
   1123         self.assert_qmp(result, 'error/desc',
   1124                         "Cannot replace 'img1' by a node mirrored from "
   1125                         "'quorum0', because it cannot be guaranteed that doing "
   1126                         "so would not lead to an abrupt change of visible data")
   1127 
   1128     def test_with_other_parents_after_mirror_start(self):
   1129         """
   1130         The same as test_with_other_parent(), but add the NBD server
   1131         only when the mirror job is already running.
   1132         """
   1133         result = self.vm.qmp('nbd-server-start',
   1134                              addr={
   1135                                  'type': 'unix',
   1136                                  'data': {'path': nbd_sock_path}
   1137                              })
   1138         self.assert_qmp(result, 'return', {})
   1139 
   1140         result = self.vm.qmp('drive-mirror', job_id='mirror', device='quorum0',
   1141                              sync='full', node_name='repair0', replaces='img1',
   1142                              target=quorum_repair_img, format=iotests.imgfmt)
   1143         self.assert_qmp(result, 'return', {})
   1144 
   1145         result = self.vm.qmp('nbd-server-add', device='img1')
   1146         self.assert_qmp(result, 'return', {})
   1147 
   1148         # The full error message goes to stderr, we will check it later
   1149         self.complete_and_wait('mirror',
   1150                                completion_error='Operation not permitted')
   1151 
   1152         # Should not have been replaced
   1153         self.vm.assert_block_path('quorum0', '/children.1', 'img1')
   1154 
   1155         # Check the full error message now
   1156         self.vm.shutdown()
   1157         log = self.vm.get_log()
   1158         log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log)
   1159         log = re.sub(r'^Formatting.*\n', '', log)
   1160         log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log)
   1161         log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log)
   1162 
   1163         self.assertEqual(log,
   1164                          "Can no longer replace 'img1' by 'repair0', because " +
   1165                          "it can no longer be guaranteed that doing so would " +
   1166                          "not lead to an abrupt change of visible data")
   1167 
   1168 
   1169 # Test mirroring with a source that does not have any parents (not even a
   1170 # BlockBackend)
   1171 class TestOrphanedSource(iotests.QMPTestCase):
   1172     def setUp(self):
   1173         blk0 = { 'node-name': 'src',
   1174                  'driver': 'null-co' }
   1175 
   1176         blk1 = { 'node-name': 'dest',
   1177                  'driver': 'null-co' }
   1178 
   1179         blk2 = { 'node-name': 'dest-ro',
   1180                  'driver': 'null-co',
   1181                  'read-only': 'on' }
   1182 
   1183         self.vm = iotests.VM()
   1184         self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
   1185         self.vm.add_blockdev(self.vm.qmp_to_opts(blk1))
   1186         self.vm.add_blockdev(self.vm.qmp_to_opts(blk2))
   1187         self.vm.launch()
   1188 
   1189     def tearDown(self):
   1190         self.vm.shutdown()
   1191 
   1192     def test_no_job_id(self):
   1193         self.assert_no_active_block_jobs()
   1194 
   1195         result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
   1196                              target='dest')
   1197         self.assert_qmp(result, 'error/class', 'GenericError')
   1198 
   1199     def test_success(self):
   1200         self.assert_no_active_block_jobs()
   1201 
   1202         result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
   1203                              sync='full', target='dest')
   1204         self.assert_qmp(result, 'return', {})
   1205 
   1206         self.complete_and_wait('job')
   1207 
   1208     def test_failing_permissions(self):
   1209         self.assert_no_active_block_jobs()
   1210 
   1211         result = self.vm.qmp('blockdev-mirror', device='src', sync='full',
   1212                              target='dest-ro')
   1213         self.assert_qmp(result, 'error/class', 'GenericError')
   1214 
   1215     def test_failing_permission_in_complete(self):
   1216         self.assert_no_active_block_jobs()
   1217 
   1218         # Unshare consistent-read on the target
   1219         # (The mirror job does not care)
   1220         result = self.vm.qmp('blockdev-add',
   1221                              driver='blkdebug',
   1222                              node_name='dest-perm',
   1223                              image='dest',
   1224                              unshare_child_perms=['consistent-read'])
   1225         self.assert_qmp(result, 'return', {})
   1226 
   1227         result = self.vm.qmp('blockdev-mirror', job_id='job', device='src',
   1228                              sync='full', target='dest',
   1229                              filter_node_name='mirror-filter')
   1230         self.assert_qmp(result, 'return', {})
   1231 
   1232         # Require consistent-read on the source
   1233         # (We can only add this node once the job has started, or it
   1234         # will complain that it does not want to run on non-root nodes)
   1235         result = self.vm.qmp('blockdev-add',
   1236                              driver='blkdebug',
   1237                              node_name='src-perm',
   1238                              image='src',
   1239                              take_child_perms=['consistent-read'])
   1240         self.assert_qmp(result, 'return', {})
   1241 
   1242         # While completing, mirror will attempt to replace src by
   1243         # dest, which must fail because src-perm requires
   1244         # consistent-read but dest-perm does not share it; thus
   1245         # aborting the job when it is supposed to complete
   1246         self.complete_and_wait('job',
   1247                                completion_error='Operation not permitted')
   1248 
   1249         # Assert that all of our nodes are still there (except for the
   1250         # mirror filter, which should be gone despite the failure)
   1251         nodes = self.vm.qmp('query-named-block-nodes')['return']
   1252         nodes = [node['node-name'] for node in nodes]
   1253 
   1254         for expect in ('src', 'src-perm', 'dest', 'dest-perm'):
   1255             self.assertTrue(expect in nodes, '%s disappeared' % expect)
   1256         self.assertFalse('mirror-filter' in nodes,
   1257                          'Mirror filter node did not disappear')
   1258 
   1259 # Test cases for @replaces that do not necessarily involve Quorum
   1260 class TestReplaces(iotests.QMPTestCase):
   1261     # Each of these test cases needs their own block graph, so do not
   1262     # create any nodes here
   1263     def setUp(self):
   1264         self.vm = iotests.VM()
   1265         self.vm.launch()
   1266 
   1267     def tearDown(self):
   1268         self.vm.shutdown()
   1269         for img in (test_img, target_img):
   1270             try:
   1271                 os.remove(img)
   1272             except OSError:
   1273                 pass
   1274 
   1275     @iotests.skip_if_unsupported(['copy-on-read'])
   1276     def test_replace_filter(self):
   1277         """
   1278         Check that we can replace filter nodes.
   1279         """
   1280         result = self.vm.qmp('blockdev-add', **{
   1281                                  'driver': 'copy-on-read',
   1282                                  'node-name': 'filter0',
   1283                                  'file': {
   1284                                      'driver': 'copy-on-read',
   1285                                      'node-name': 'filter1',
   1286                                      'file': {
   1287                                          'driver': 'null-co'
   1288                                      }
   1289                                  }
   1290                              })
   1291         self.assert_qmp(result, 'return', {})
   1292 
   1293         result = self.vm.qmp('blockdev-add',
   1294                              node_name='target', driver='null-co')
   1295         self.assert_qmp(result, 'return', {})
   1296 
   1297         result = self.vm.qmp('blockdev-mirror', job_id='mirror', device='filter0',
   1298                              target='target', sync='full', replaces='filter1')
   1299         self.assert_qmp(result, 'return', {})
   1300 
   1301         self.complete_and_wait('mirror')
   1302 
   1303         self.vm.assert_block_path('filter0', '/file', 'target')
   1304 
   1305 # Tests for mirror with filters (and how the mirror filter behaves, as
   1306 # an example for an implicit filter)
   1307 class TestFilters(iotests.QMPTestCase):
   1308     def setUp(self):
   1309         qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
   1310         qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
   1311                  '-F', iotests.imgfmt, test_img)
   1312         qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
   1313                  '-F', iotests.imgfmt, target_img)
   1314 
   1315         qemu_io('-c', 'write -P 1 0 512k', backing_img)
   1316         qemu_io('-c', 'write -P 2 512k 512k', test_img)
   1317 
   1318         self.vm = iotests.VM().add_device('virtio-scsi,id=vio-scsi')
   1319         self.vm.launch()
   1320 
   1321         result = self.vm.qmp('blockdev-add', **{
   1322                                 'node-name': 'target',
   1323                                 'driver': iotests.imgfmt,
   1324                                 'file': {
   1325                                     'driver': 'file',
   1326                                     'filename': target_img
   1327                                 },
   1328                                 'backing': None
   1329                             })
   1330         self.assert_qmp(result, 'return', {})
   1331 
   1332         self.filterless_chain = {
   1333                 'node-name': 'source',
   1334                 'driver': iotests.imgfmt,
   1335                 'file': {
   1336                     'driver': 'file',
   1337                     'filename': test_img
   1338                 },
   1339                 'backing': {
   1340                     'node-name': 'backing',
   1341                     'driver': iotests.imgfmt,
   1342                     'file': {
   1343                         'driver': 'file',
   1344                         'filename': backing_img
   1345                     }
   1346                 }
   1347             }
   1348 
   1349     def tearDown(self):
   1350         self.vm.shutdown()
   1351 
   1352         os.remove(test_img)
   1353         os.remove(target_img)
   1354         os.remove(backing_img)
   1355 
   1356     def test_cor(self):
   1357         result = self.vm.qmp('blockdev-add', **{
   1358                                 'node-name': 'filter',
   1359                                 'driver': 'copy-on-read',
   1360                                 'file': self.filterless_chain
   1361                             })
   1362         self.assert_qmp(result, 'return', {})
   1363 
   1364         result = self.vm.qmp('blockdev-mirror',
   1365                              job_id='mirror',
   1366                              device='filter',
   1367                              target='target',
   1368                              sync='top')
   1369         self.assert_qmp(result, 'return', {})
   1370 
   1371         self.complete_and_wait('mirror')
   1372 
   1373         self.vm.qmp('blockdev-del', node_name='target')
   1374 
   1375         target_map = qemu_img_map(target_img)
   1376 
   1377         assert target_map[0]['start'] == 0
   1378         assert target_map[0]['length'] == 512 * 1024
   1379         assert target_map[0]['depth'] == 1
   1380 
   1381         assert target_map[1]['start'] == 512 * 1024
   1382         assert target_map[1]['length'] == 512 * 1024
   1383         assert target_map[1]['depth'] == 0
   1384 
   1385     def test_implicit_mirror_filter(self):
   1386         result = self.vm.qmp('blockdev-add', **self.filterless_chain)
   1387         self.assert_qmp(result, 'return', {})
   1388 
   1389         # We need this so we can query from above the mirror node
   1390         result = self.vm.qmp('device_add',
   1391                              driver='scsi-hd',
   1392                              id='virtio',
   1393                              bus='vio-scsi.0',
   1394                              drive='source')
   1395         self.assert_qmp(result, 'return', {})
   1396 
   1397         result = self.vm.qmp('blockdev-mirror',
   1398                              job_id='mirror',
   1399                              device='source',
   1400                              target='target',
   1401                              sync='top')
   1402         self.assert_qmp(result, 'return', {})
   1403 
   1404         # The mirror filter is now an implicit node, so it should be
   1405         # invisible when querying the backing chain
   1406         blockdevs = self.vm.qmp('query-block')['return']
   1407         device_info = next(dev for dev in blockdevs if dev['qdev'] == 'virtio')
   1408 
   1409         assert device_info['inserted']['node-name'] == 'source'
   1410 
   1411         image_info = device_info['inserted']['image']
   1412         assert image_info['filename'] == test_img
   1413         assert image_info['backing-image']['filename'] == backing_img
   1414 
   1415         self.complete_and_wait('mirror')
   1416 
   1417     def test_explicit_mirror_filter(self):
   1418         # Same test as above, but this time we give the mirror filter
   1419         # a node-name so it will not be invisible
   1420         result = self.vm.qmp('blockdev-add', **self.filterless_chain)
   1421         self.assert_qmp(result, 'return', {})
   1422 
   1423         # We need this so we can query from above the mirror node
   1424         result = self.vm.qmp('device_add',
   1425                              driver='scsi-hd',
   1426                              id='virtio',
   1427                              bus='vio-scsi.0',
   1428                              drive='source')
   1429         self.assert_qmp(result, 'return', {})
   1430 
   1431         result = self.vm.qmp('blockdev-mirror',
   1432                              job_id='mirror',
   1433                              device='source',
   1434                              target='target',
   1435                              sync='top',
   1436                              filter_node_name='mirror-filter')
   1437         self.assert_qmp(result, 'return', {})
   1438 
   1439         # With a node-name given to it, the mirror filter should now
   1440         # be visible
   1441         blockdevs = self.vm.qmp('query-block')['return']
   1442         device_info = next(dev for dev in blockdevs if dev['qdev'] == 'virtio')
   1443 
   1444         assert device_info['inserted']['node-name'] == 'mirror-filter'
   1445 
   1446         self.complete_and_wait('mirror')
   1447 
   1448 
   1449 if __name__ == '__main__':
   1450     iotests.main(supported_fmts=['qcow2', 'qed'],
   1451                  supported_protocols=['file'],
   1452                  supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd'])