qemu

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

147 (10531B)


      1 #!/usr/bin/env python3
      2 # group: img
      3 #
      4 # Test case for NBD's blockdev-add interface
      5 #
      6 # Copyright (C) 2016 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 random
     24 import socket
     25 import stat
     26 import time
     27 import iotests
     28 from iotests import cachemode, aiomode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe
     29 
     30 NBD_PORT_START      = 32768
     31 NBD_PORT_END        = NBD_PORT_START + 1024
     32 NBD_IPV6_PORT_START = NBD_PORT_END
     33 NBD_IPV6_PORT_END   = NBD_IPV6_PORT_START + 1024
     34 
     35 test_img = os.path.join(iotests.test_dir, 'test.img')
     36 unix_socket = os.path.join(iotests.sock_dir, 'nbd.socket')
     37 
     38 
     39 def flatten_sock_addr(crumpled_address):
     40     result = { 'type': crumpled_address['type'] }
     41     result.update(crumpled_address['data'])
     42     return result
     43 
     44 
     45 class NBDBlockdevAddBase(iotests.QMPTestCase):
     46     def blockdev_add_options(self, address, export, node_name):
     47         options = { 'node-name': node_name,
     48                     'driver': 'raw',
     49                     'file': {
     50                         'driver': 'nbd',
     51                         'read-only': True,
     52                         'server': address
     53                     } }
     54         if export is not None:
     55             options['file']['export'] = export
     56         return options
     57 
     58     def client_test(self, filename, address, export=None,
     59                     node_name='nbd-blockdev', delete=True):
     60         bao = self.blockdev_add_options(address, export, node_name)
     61         result = self.vm.qmp('blockdev-add', **bao)
     62         self.assert_qmp(result, 'return', {})
     63 
     64         found = False
     65         result = self.vm.qmp('query-named-block-nodes')
     66         for node in result['return']:
     67             if node['node-name'] == node_name:
     68                 found = True
     69                 if isinstance(filename, str):
     70                     self.assert_qmp(node, 'image/filename', filename)
     71                 else:
     72                     self.assert_json_filename_equal(node['image']['filename'],
     73                                                     filename)
     74                 break
     75         self.assertTrue(found)
     76 
     77         if delete:
     78             result = self.vm.qmp('blockdev-del', node_name=node_name)
     79             self.assert_qmp(result, 'return', {})
     80 
     81 
     82 class QemuNBD(NBDBlockdevAddBase):
     83     def setUp(self):
     84         qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
     85         self.vm = iotests.VM()
     86         self.vm.launch()
     87 
     88     def tearDown(self):
     89         self.vm.shutdown()
     90         os.remove(test_img)
     91         try:
     92             os.remove(unix_socket)
     93         except OSError:
     94             pass
     95 
     96     def _try_server_up(self, *args):
     97         status, msg = qemu_nbd_early_pipe('-f', imgfmt, test_img, *args)
     98         if status == 0:
     99             return True
    100         if 'Address already in use' in msg:
    101             return False
    102         self.fail(msg)
    103 
    104     def _server_up(self, *args):
    105         self.assertTrue(self._try_server_up(*args))
    106 
    107     def test_inet(self):
    108         while True:
    109             nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
    110             if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)):
    111                 break
    112 
    113         address = { 'type': 'inet',
    114                     'data': {
    115                         'host': 'localhost',
    116                         'port': str(nbd_port)
    117                     } }
    118         self.client_test('nbd://localhost:%i' % nbd_port,
    119                          flatten_sock_addr(address))
    120 
    121     def test_unix(self):
    122         self._server_up('-k', unix_socket)
    123         address = { 'type': 'unix',
    124                     'data': { 'path': unix_socket } }
    125         self.client_test('nbd+unix://?socket=' + unix_socket,
    126                          flatten_sock_addr(address))
    127 
    128 
    129 class BuiltinNBD(NBDBlockdevAddBase):
    130     def setUp(self):
    131         qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
    132         self.vm = iotests.VM()
    133         self.vm.launch()
    134         self.server = iotests.VM('.server')
    135         self.server.add_drive_raw('if=none,id=nbd-export,' +
    136                                   'file=%s,' % test_img +
    137                                   'format=%s,' % imgfmt +
    138                                   'cache=%s,' % cachemode +
    139                                   'aio=%s' % aiomode)
    140         self.server.launch()
    141 
    142     def tearDown(self):
    143         self.vm.shutdown()
    144         self.server.shutdown()
    145         os.remove(test_img)
    146         try:
    147             os.remove(unix_socket)
    148         except OSError:
    149             pass
    150 
    151     # Returns False on EADDRINUSE; fails an assertion on other errors.
    152     # Returns True on success.
    153     def _try_server_up(self, address, export_name=None, export_name2=None):
    154         result = self.server.qmp('nbd-server-start', addr=address)
    155         if 'error' in result and \
    156            'Address already in use' in result['error']['desc']:
    157             return False
    158         self.assert_qmp(result, 'return', {})
    159 
    160         if export_name is None:
    161             result = self.server.qmp('nbd-server-add', device='nbd-export')
    162         else:
    163             result = self.server.qmp('nbd-server-add', device='nbd-export',
    164                                      name=export_name)
    165         self.assert_qmp(result, 'return', {})
    166 
    167         if export_name2 is not None:
    168             result = self.server.qmp('nbd-server-add', device='nbd-export',
    169                                      name=export_name2)
    170             self.assert_qmp(result, 'return', {})
    171 
    172         return True
    173 
    174     def _server_up(self, address, export_name=None, export_name2=None):
    175         self.assertTrue(self._try_server_up(address, export_name, export_name2))
    176 
    177     def _server_down(self):
    178         result = self.server.qmp('nbd-server-stop')
    179         self.assert_qmp(result, 'return', {})
    180 
    181     def do_test_inet(self, export_name=None):
    182         while True:
    183             nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
    184             address = { 'type': 'inet',
    185                         'data': {
    186                             'host': 'localhost',
    187                             'port': str(nbd_port)
    188                         } }
    189             if self._try_server_up(address, export_name):
    190                 break
    191 
    192         export_name = export_name or 'nbd-export'
    193         self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name),
    194                          flatten_sock_addr(address), export_name)
    195         self._server_down()
    196 
    197     def test_inet_default_export_name(self):
    198         self.do_test_inet()
    199 
    200     def test_inet_same_export_name(self):
    201         self.do_test_inet('nbd-export')
    202 
    203     def test_inet_different_export_name(self):
    204         self.do_test_inet('shadow')
    205 
    206     def test_inet_two_exports(self):
    207         while True:
    208             nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
    209             address = { 'type': 'inet',
    210                         'data': {
    211                             'host': 'localhost',
    212                             'port': str(nbd_port)
    213                         } }
    214             if self._try_server_up(address, 'exp1', 'exp2'):
    215                 break
    216 
    217         self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'),
    218                          flatten_sock_addr(address), 'exp1', 'node1', False)
    219         self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'),
    220                          flatten_sock_addr(address), 'exp2', 'node2', False)
    221         result = self.vm.qmp('blockdev-del', node_name='node1')
    222         self.assert_qmp(result, 'return', {})
    223         result = self.vm.qmp('blockdev-del', node_name='node2')
    224         self.assert_qmp(result, 'return', {})
    225         self._server_down()
    226 
    227     def test_inet6(self):
    228         try:
    229             socket.getaddrinfo("::0", "0", socket.AF_INET6,
    230                                socket.SOCK_STREAM, socket.IPPROTO_TCP,
    231                                socket.AI_ADDRCONFIG | socket.AI_CANONNAME)
    232         except socket.gaierror:
    233             # IPv6 not available, skip
    234             return
    235 
    236         while True:
    237             nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END)
    238             address = { 'type': 'inet',
    239                         'data': {
    240                             'host': '::1',
    241                             'port': str(nbd_port),
    242                             'ipv4': False,
    243                             'ipv6': True
    244                         } }
    245             if self._try_server_up(address):
    246                 break
    247 
    248         filename = { 'driver': 'raw',
    249                      'file': {
    250                          'driver': 'nbd',
    251                          'export': 'nbd-export',
    252                          'server': flatten_sock_addr(address)
    253                      } }
    254         self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
    255         self._server_down()
    256 
    257     def test_unix(self):
    258         address = { 'type': 'unix',
    259                     'data': { 'path': unix_socket } }
    260         self._server_up(address)
    261         self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket,
    262                          flatten_sock_addr(address), 'nbd-export')
    263         self._server_down()
    264 
    265     def test_fd(self):
    266         self._server_up({ 'type': 'unix',
    267                           'data': { 'path': unix_socket } })
    268 
    269         sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    270         sockfd.connect(unix_socket)
    271 
    272         result = self.vm.send_fd_scm(fd=sockfd.fileno())
    273         self.assertEqual(result, 0, 'Failed to send socket FD')
    274 
    275         result = self.vm.qmp('getfd', fdname='nbd-fifo')
    276         self.assert_qmp(result, 'return', {})
    277 
    278         address = { 'type': 'fd',
    279                     'data': { 'str': 'nbd-fifo' } }
    280         filename = { 'driver': 'raw',
    281                      'file': {
    282                          'driver': 'nbd',
    283                          'export': 'nbd-export',
    284                          'server': flatten_sock_addr(address)
    285                      } }
    286         self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
    287 
    288         self._server_down()
    289 
    290 
    291 if __name__ == '__main__':
    292     iotests.main(supported_fmts=['raw'],
    293                  supported_protocols=['nbd'])