qemu

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

281 (12898B)


      1 #!/usr/bin/env python3
      2 # group: rw
      3 #
      4 # Test cases for blockdev + IOThread interactions
      5 #
      6 # Copyright (C) 2019 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 os
     23 import time
     24 import iotests
     25 from iotests import qemu_img, QemuStorageDaemon
     26 
     27 image_len = 64 * 1024 * 1024
     28 
     29 # Test for RHBZ#1782175
     30 class TestDirtyBitmapIOThread(iotests.QMPTestCase):
     31     drive0_img = os.path.join(iotests.test_dir, 'drive0.img')
     32     images = { 'drive0': drive0_img }
     33 
     34     def setUp(self):
     35         for name in self.images:
     36             qemu_img('create', '-f', iotests.imgfmt,
     37                      self.images[name], str(image_len))
     38 
     39         self.vm = iotests.VM()
     40         self.vm.add_object('iothread,id=iothread0')
     41 
     42         for name in self.images:
     43             self.vm.add_blockdev('driver=file,filename=%s,node-name=file_%s'
     44                                  % (self.images[name], name))
     45             self.vm.add_blockdev('driver=qcow2,file=file_%s,node-name=%s'
     46                                  % (name, name))
     47 
     48         self.vm.launch()
     49         self.vm.qmp('x-blockdev-set-iothread',
     50                     node_name='drive0', iothread='iothread0',
     51                     force=True)
     52 
     53     def tearDown(self):
     54         self.vm.shutdown()
     55         for name in self.images:
     56             os.remove(self.images[name])
     57 
     58     def test_add_dirty_bitmap(self):
     59         result = self.vm.qmp(
     60             'block-dirty-bitmap-add',
     61             node='drive0',
     62             name='bitmap1',
     63             persistent=True,
     64         )
     65 
     66         self.assert_qmp(result, 'return', {})
     67 
     68 
     69 # Test for RHBZ#1746217 & RHBZ#1773517
     70 class TestNBDMirrorIOThread(iotests.QMPTestCase):
     71     nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock')
     72     drive0_img = os.path.join(iotests.test_dir, 'drive0.img')
     73     mirror_img = os.path.join(iotests.test_dir, 'mirror.img')
     74     images = { 'drive0': drive0_img, 'mirror': mirror_img }
     75 
     76     def setUp(self):
     77         for name in self.images:
     78             qemu_img('create', '-f', iotests.imgfmt,
     79                      self.images[name], str(image_len))
     80 
     81         self.vm_src = iotests.VM(path_suffix='src')
     82         self.vm_src.add_object('iothread,id=iothread0')
     83         self.vm_src.add_blockdev('driver=file,filename=%s,node-name=file0'
     84                           % (self.drive0_img))
     85         self.vm_src.add_blockdev('driver=qcow2,file=file0,node-name=drive0')
     86         self.vm_src.launch()
     87         self.vm_src.qmp('x-blockdev-set-iothread',
     88                         node_name='drive0', iothread='iothread0',
     89                         force=True)
     90 
     91         self.vm_tgt = iotests.VM(path_suffix='tgt')
     92         self.vm_tgt.add_object('iothread,id=iothread0')
     93         self.vm_tgt.add_blockdev('driver=file,filename=%s,node-name=file0'
     94                           % (self.mirror_img))
     95         self.vm_tgt.add_blockdev('driver=qcow2,file=file0,node-name=drive0')
     96         self.vm_tgt.launch()
     97         self.vm_tgt.qmp('x-blockdev-set-iothread',
     98                         node_name='drive0', iothread='iothread0',
     99                         force=True)
    100 
    101     def tearDown(self):
    102         self.vm_src.shutdown()
    103         self.vm_tgt.shutdown()
    104         for name in self.images:
    105             os.remove(self.images[name])
    106 
    107     def test_nbd_mirror(self):
    108         result = self.vm_tgt.qmp(
    109             'nbd-server-start',
    110             addr={
    111                 'type': 'unix',
    112                 'data': { 'path': self.nbd_sock }
    113             }
    114         )
    115         self.assert_qmp(result, 'return', {})
    116 
    117         result = self.vm_tgt.qmp(
    118             'nbd-server-add',
    119             device='drive0',
    120             writable=True
    121         )
    122         self.assert_qmp(result, 'return', {})
    123 
    124         result = self.vm_src.qmp(
    125             'drive-mirror',
    126             device='drive0',
    127             target='nbd+unix:///drive0?socket=' + self.nbd_sock,
    128             sync='full',
    129             mode='existing',
    130             speed=64*1024*1024,
    131             job_id='j1'
    132         )
    133         self.assert_qmp(result, 'return', {})
    134 
    135         self.vm_src.event_wait(name="BLOCK_JOB_READY")
    136 
    137 
    138 # Test for RHBZ#1779036
    139 class TestExternalSnapshotAbort(iotests.QMPTestCase):
    140     drive0_img = os.path.join(iotests.test_dir, 'drive0.img')
    141     snapshot_img = os.path.join(iotests.test_dir, 'snapshot.img')
    142     images = { 'drive0': drive0_img, 'snapshot': snapshot_img }
    143 
    144     def setUp(self):
    145         for name in self.images:
    146             qemu_img('create', '-f', iotests.imgfmt,
    147                      self.images[name], str(image_len))
    148 
    149         self.vm = iotests.VM()
    150         self.vm.add_object('iothread,id=iothread0')
    151         self.vm.add_blockdev('driver=file,filename=%s,node-name=file0'
    152                           % (self.drive0_img))
    153         self.vm.add_blockdev('driver=qcow2,file=file0,node-name=drive0')
    154         self.vm.launch()
    155         self.vm.qmp('x-blockdev-set-iothread',
    156                     node_name='drive0', iothread='iothread0',
    157                     force=True)
    158 
    159     def tearDown(self):
    160         self.vm.shutdown()
    161         for name in self.images:
    162             os.remove(self.images[name])
    163 
    164     def test_external_snapshot_abort(self):
    165         # Use a two actions transaction with a bogus values on the second
    166         # one to trigger an abort of the transaction.
    167         result = self.vm.qmp('transaction', actions=[
    168             {
    169                 'type': 'blockdev-snapshot-sync',
    170                 'data': { 'node-name': 'drive0',
    171                           'snapshot-file': self.snapshot_img,
    172                           'snapshot-node-name': 'snap1',
    173                           'mode': 'absolute-paths',
    174                           'format': 'qcow2' }
    175             },
    176             {
    177                 'type': 'blockdev-snapshot-sync',
    178                 'data': { 'node-name': 'drive0',
    179                           'snapshot-file': '/fakesnapshot',
    180                           'snapshot-node-name': 'snap2',
    181                           'mode': 'absolute-paths',
    182                           'format': 'qcow2' }
    183             },
    184         ])
    185 
    186         # Crashes on failure, we expect this error.
    187         self.assert_qmp(result, 'error/class', 'GenericError')
    188 
    189 
    190 # Test for RHBZ#1782111
    191 class TestBlockdevBackupAbort(iotests.QMPTestCase):
    192     drive0_img = os.path.join(iotests.test_dir, 'drive0.img')
    193     drive1_img = os.path.join(iotests.test_dir, 'drive1.img')
    194     snap0_img = os.path.join(iotests.test_dir, 'snap0.img')
    195     snap1_img = os.path.join(iotests.test_dir, 'snap1.img')
    196     images = { 'drive0': drive0_img,
    197                'drive1': drive1_img,
    198                'snap0': snap0_img,
    199                'snap1': snap1_img }
    200 
    201     def setUp(self):
    202         for name in self.images:
    203             qemu_img('create', '-f', iotests.imgfmt,
    204                      self.images[name], str(image_len))
    205 
    206         self.vm = iotests.VM()
    207         self.vm.add_object('iothread,id=iothread0')
    208         self.vm.add_device('virtio-scsi,iothread=iothread0')
    209 
    210         for name in self.images:
    211             self.vm.add_blockdev('driver=file,filename=%s,node-name=file_%s'
    212                                  % (self.images[name], name))
    213             self.vm.add_blockdev('driver=qcow2,file=file_%s,node-name=%s'
    214                                  % (name, name))
    215 
    216         self.vm.add_device('scsi-hd,drive=drive0')
    217         self.vm.add_device('scsi-hd,drive=drive1')
    218         self.vm.launch()
    219 
    220     def tearDown(self):
    221         self.vm.shutdown()
    222         for name in self.images:
    223             os.remove(self.images[name])
    224 
    225     def test_blockdev_backup_abort(self):
    226         # Use a two actions transaction with a bogus values on the second
    227         # one to trigger an abort of the transaction.
    228         result = self.vm.qmp('transaction', actions=[
    229             {
    230                 'type': 'blockdev-backup',
    231                 'data': { 'device': 'drive0',
    232                           'target': 'snap0',
    233                           'sync': 'full',
    234                           'job-id': 'j1' }
    235             },
    236             {
    237                 'type': 'blockdev-backup',
    238                 'data': { 'device': 'drive1',
    239                           'target': 'snap1',
    240                           'sync': 'full' }
    241             },
    242         ])
    243 
    244         # Hangs on failure, we expect this error.
    245         self.assert_qmp(result, 'error/class', 'GenericError')
    246 
    247 # Test for RHBZ#2033626
    248 class TestYieldingAndTimers(iotests.QMPTestCase):
    249     sock = os.path.join(iotests.sock_dir, 'nbd.sock')
    250     qsd = None
    251 
    252     def setUp(self):
    253         self.create_nbd_export()
    254 
    255         # Simple VM with an NBD block device connected to the NBD export
    256         # provided by the QSD, and an (initially unused) iothread
    257         self.vm = iotests.VM()
    258         self.vm.add_object('iothread,id=iothr')
    259         self.vm.add_blockdev('nbd,node-name=nbd,server.type=unix,' +
    260                              f'server.path={self.sock},export=exp,' +
    261                              'reconnect-delay=1,open-timeout=1')
    262 
    263         self.vm.launch()
    264 
    265     def tearDown(self):
    266         self.stop_nbd_export()
    267         self.vm.shutdown()
    268 
    269     def test_timers_with_blockdev_del(self):
    270         # The NBD BDS will have had an active open timer, because setUp() gave
    271         # a positive value for @open-timeout.  It should be gone once the BDS
    272         # has been opened.
    273         # (But there used to be a bug where it remained active, which will
    274         # become important below.)
    275 
    276         # Stop and restart the NBD server, and do some I/O on the client to
    277         # trigger a reconnect and start the reconnect delay timer
    278         self.stop_nbd_export()
    279         self.create_nbd_export()
    280 
    281         result = self.vm.qmp('human-monitor-command',
    282                              command_line='qemu-io nbd "write 0 512"')
    283         self.assert_qmp(result, 'return', '')
    284 
    285         # Reconnect is done, so the reconnect delay timer should be gone.
    286         # (This is similar to how the open timer should be gone after open,
    287         # and similarly there used to be a bug where it was not gone.)
    288 
    289         # Delete the BDS to see whether both timers are gone.  If they are not,
    290         # they will remain active, fire later, and then access freed data.
    291         # (Or, with "block/nbd: Assert there are no timers when closed"
    292         # applied, the assertions added in that patch will fail.)
    293         result = self.vm.qmp('blockdev-del', node_name='nbd')
    294         self.assert_qmp(result, 'return', {})
    295 
    296         # Give the timers some time to fire (both have a timeout of 1 s).
    297         # (Sleeping in an iotest may ring some alarm bells, but note that if
    298         # the timing is off here, the test will just always pass.  If we kill
    299         # the VM too early, then we just kill the timers before they can fire,
    300         # thus not see the error, and so the test will pass.)
    301         time.sleep(2)
    302 
    303     def test_yield_in_iothread(self):
    304         # Move the NBD node to the I/O thread; the NBD block driver should
    305         # attach the connection's QIOChannel to that thread's AioContext, too
    306         result = self.vm.qmp('x-blockdev-set-iothread',
    307                              node_name='nbd', iothread='iothr')
    308         self.assert_qmp(result, 'return', {})
    309 
    310         # Do some I/O that will be throttled by the QSD, so that the network
    311         # connection hopefully will yield here.  When it is resumed, it must
    312         # then be resumed in the I/O thread's AioContext.
    313         result = self.vm.qmp('human-monitor-command',
    314                              command_line='qemu-io nbd "read 0 128K"')
    315         self.assert_qmp(result, 'return', '')
    316 
    317     def create_nbd_export(self):
    318         assert self.qsd is None
    319 
    320         # Export a throttled null-co BDS: Reads are throttled (max 64 kB/s),
    321         # writes are not.
    322         self.qsd = QemuStorageDaemon(
    323             '--object',
    324             'throttle-group,id=thrgr,x-bps-read=65536,x-bps-read-max=65536',
    325 
    326             '--blockdev',
    327             'null-co,node-name=null,read-zeroes=true',
    328 
    329             '--blockdev',
    330             'throttle,node-name=thr,file=null,throttle-group=thrgr',
    331 
    332             '--nbd-server',
    333             f'addr.type=unix,addr.path={self.sock}',
    334 
    335             '--export',
    336             'nbd,id=exp,node-name=thr,name=exp,writable=true'
    337         )
    338 
    339     def stop_nbd_export(self):
    340         self.qsd.stop()
    341         self.qsd = None
    342 
    343 if __name__ == '__main__':
    344     iotests.main(supported_fmts=['qcow2'],
    345                  supported_protocols=['file'],
    346                  unsupported_imgopts=['compat'])