xref: /openbmc/qemu/tests/qemu-iotests/147 (revision b6aed193)
1903cb1bfSPhilippe Mathieu-Daudé#!/usr/bin/env python3
29dd003a9SVladimir Sementsov-Ogievskiy# group: img
3b74fc7f7SMax Reitz#
4b74fc7f7SMax Reitz# Test case for NBD's blockdev-add interface
5b74fc7f7SMax Reitz#
6b74fc7f7SMax Reitz# Copyright (C) 2016 Red Hat, Inc.
7b74fc7f7SMax Reitz#
8b74fc7f7SMax Reitz# This program is free software; you can redistribute it and/or modify
9b74fc7f7SMax Reitz# it under the terms of the GNU General Public License as published by
10b74fc7f7SMax Reitz# the Free Software Foundation; either version 2 of the License, or
11b74fc7f7SMax Reitz# (at your option) any later version.
12b74fc7f7SMax Reitz#
13b74fc7f7SMax Reitz# This program is distributed in the hope that it will be useful,
14b74fc7f7SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of
15b74fc7f7SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16b74fc7f7SMax Reitz# GNU General Public License for more details.
17b74fc7f7SMax Reitz#
18b74fc7f7SMax Reitz# You should have received a copy of the GNU General Public License
19b74fc7f7SMax Reitz# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20b74fc7f7SMax Reitz#
21b74fc7f7SMax Reitz
22b74fc7f7SMax Reitzimport os
23908b3016SMax Reitzimport random
24b74fc7f7SMax Reitzimport socket
25b74fc7f7SMax Reitzimport stat
26b74fc7f7SMax Reitzimport time
27b74fc7f7SMax Reitzimport iotests
288dff69b9SAarushi Mehtafrom iotests import cachemode, aiomode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_early_pipe
29b74fc7f7SMax Reitz
30908b3016SMax ReitzNBD_PORT_START      = 32768
31908b3016SMax ReitzNBD_PORT_END        = NBD_PORT_START + 1024
32908b3016SMax ReitzNBD_IPV6_PORT_START = NBD_PORT_END
33908b3016SMax ReitzNBD_IPV6_PORT_END   = NBD_IPV6_PORT_START + 1024
34b74fc7f7SMax Reitz
35b74fc7f7SMax Reitztest_img = os.path.join(iotests.test_dir, 'test.img')
36610dffaaSMax Reitzunix_socket = os.path.join(iotests.sock_dir, 'nbd.socket')
37b74fc7f7SMax Reitz
389445673eSMarkus Armbruster
399445673eSMarkus Armbrusterdef flatten_sock_addr(crumpled_address):
409445673eSMarkus Armbruster    result = { 'type': crumpled_address['type'] }
419445673eSMarkus Armbruster    result.update(crumpled_address['data'])
429445673eSMarkus Armbruster    return result
439445673eSMarkus Armbruster
449445673eSMarkus Armbruster
45b74fc7f7SMax Reitzclass NBDBlockdevAddBase(iotests.QMPTestCase):
46549084eaSVladimir Sementsov-Ogievskiy    def blockdev_add_options(self, address, export, node_name):
47549084eaSVladimir Sementsov-Ogievskiy        options = { 'node-name': node_name,
48b74fc7f7SMax Reitz                    'driver': 'raw',
49b74fc7f7SMax Reitz                    'file': {
50b74fc7f7SMax Reitz                        'driver': 'nbd',
511104d83cSEric Blake                        'read-only': True,
52b74fc7f7SMax Reitz                        'server': address
53b74fc7f7SMax Reitz                    } }
54b74fc7f7SMax Reitz        if export is not None:
55b74fc7f7SMax Reitz            options['file']['export'] = export
56b74fc7f7SMax Reitz        return options
57b74fc7f7SMax Reitz
58549084eaSVladimir Sementsov-Ogievskiy    def client_test(self, filename, address, export=None,
59549084eaSVladimir Sementsov-Ogievskiy                    node_name='nbd-blockdev', delete=True):
60549084eaSVladimir Sementsov-Ogievskiy        bao = self.blockdev_add_options(address, export, node_name)
61*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-add', bao)
62b74fc7f7SMax Reitz
63549084eaSVladimir Sementsov-Ogievskiy        found = False
64b74fc7f7SMax Reitz        result = self.vm.qmp('query-named-block-nodes')
65b74fc7f7SMax Reitz        for node in result['return']:
66549084eaSVladimir Sementsov-Ogievskiy            if node['node-name'] == node_name:
67549084eaSVladimir Sementsov-Ogievskiy                found = True
68b74fc7f7SMax Reitz                if isinstance(filename, str):
69b74fc7f7SMax Reitz                    self.assert_qmp(node, 'image/filename', filename)
70b74fc7f7SMax Reitz                else:
71b74fc7f7SMax Reitz                    self.assert_json_filename_equal(node['image']['filename'],
72b74fc7f7SMax Reitz                                                    filename)
73b74fc7f7SMax Reitz                break
74549084eaSVladimir Sementsov-Ogievskiy        self.assertTrue(found)
75b74fc7f7SMax Reitz
76549084eaSVladimir Sementsov-Ogievskiy        if delete:
77*b6aed193SVladimir Sementsov-Ogievskiy            self.vm.cmd('blockdev-del', node_name=node_name)
78b74fc7f7SMax Reitz
79b74fc7f7SMax Reitz
80b74fc7f7SMax Reitzclass QemuNBD(NBDBlockdevAddBase):
81b74fc7f7SMax Reitz    def setUp(self):
82b74fc7f7SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
83b74fc7f7SMax Reitz        self.vm = iotests.VM()
84b74fc7f7SMax Reitz        self.vm.launch()
85b74fc7f7SMax Reitz
86b74fc7f7SMax Reitz    def tearDown(self):
87b74fc7f7SMax Reitz        self.vm.shutdown()
88b74fc7f7SMax Reitz        os.remove(test_img)
89b74fc7f7SMax Reitz        try:
90b74fc7f7SMax Reitz            os.remove(unix_socket)
91b74fc7f7SMax Reitz        except OSError:
92b74fc7f7SMax Reitz            pass
93b74fc7f7SMax Reitz
94908b3016SMax Reitz    def _try_server_up(self, *args):
956177b584SMax Reitz        status, msg = qemu_nbd_early_pipe('-f', imgfmt, test_img, *args)
96908b3016SMax Reitz        if status == 0:
97908b3016SMax Reitz            return True
98908b3016SMax Reitz        if 'Address already in use' in msg:
99908b3016SMax Reitz            return False
100908b3016SMax Reitz        self.fail(msg)
101908b3016SMax Reitz
102b74fc7f7SMax Reitz    def _server_up(self, *args):
103908b3016SMax Reitz        self.assertTrue(self._try_server_up(*args))
104b74fc7f7SMax Reitz
105b74fc7f7SMax Reitz    def test_inet(self):
106908b3016SMax Reitz        while True:
107908b3016SMax Reitz            nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
108908b3016SMax Reitz            if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)):
109908b3016SMax Reitz                break
110908b3016SMax Reitz
111b74fc7f7SMax Reitz        address = { 'type': 'inet',
112b74fc7f7SMax Reitz                    'data': {
113b74fc7f7SMax Reitz                        'host': 'localhost',
114908b3016SMax Reitz                        'port': str(nbd_port)
115b74fc7f7SMax Reitz                    } }
116908b3016SMax Reitz        self.client_test('nbd://localhost:%i' % nbd_port,
1179445673eSMarkus Armbruster                         flatten_sock_addr(address))
118b74fc7f7SMax Reitz
119b74fc7f7SMax Reitz    def test_unix(self):
120b74fc7f7SMax Reitz        self._server_up('-k', unix_socket)
121b74fc7f7SMax Reitz        address = { 'type': 'unix',
122b74fc7f7SMax Reitz                    'data': { 'path': unix_socket } }
1239445673eSMarkus Armbruster        self.client_test('nbd+unix://?socket=' + unix_socket,
1249445673eSMarkus Armbruster                         flatten_sock_addr(address))
125b74fc7f7SMax Reitz
126b74fc7f7SMax Reitz
127b74fc7f7SMax Reitzclass BuiltinNBD(NBDBlockdevAddBase):
128b74fc7f7SMax Reitz    def setUp(self):
129b74fc7f7SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
130b74fc7f7SMax Reitz        self.vm = iotests.VM()
131b74fc7f7SMax Reitz        self.vm.launch()
132b74fc7f7SMax Reitz        self.server = iotests.VM('.server')
133b74fc7f7SMax Reitz        self.server.add_drive_raw('if=none,id=nbd-export,' +
134b74fc7f7SMax Reitz                                  'file=%s,' % test_img +
135b74fc7f7SMax Reitz                                  'format=%s,' % imgfmt +
136b0c4cf21SMax Reitz                                  'cache=%s,' % cachemode +
1378dff69b9SAarushi Mehta                                  'aio=%s' % aiomode)
138b74fc7f7SMax Reitz        self.server.launch()
139b74fc7f7SMax Reitz
140b74fc7f7SMax Reitz    def tearDown(self):
141b74fc7f7SMax Reitz        self.vm.shutdown()
142b74fc7f7SMax Reitz        self.server.shutdown()
143b74fc7f7SMax Reitz        os.remove(test_img)
144b74fc7f7SMax Reitz        try:
145b74fc7f7SMax Reitz            os.remove(unix_socket)
146b74fc7f7SMax Reitz        except OSError:
147b74fc7f7SMax Reitz            pass
148b74fc7f7SMax Reitz
149908b3016SMax Reitz    # Returns False on EADDRINUSE; fails an assertion on other errors.
150908b3016SMax Reitz    # Returns True on success.
151908b3016SMax Reitz    def _try_server_up(self, address, export_name=None, export_name2=None):
152b74fc7f7SMax Reitz        result = self.server.qmp('nbd-server-start', addr=address)
153908b3016SMax Reitz        if 'error' in result and \
154908b3016SMax Reitz           'Address already in use' in result['error']['desc']:
155908b3016SMax Reitz            return False
156b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
157b74fc7f7SMax Reitz
158549084eaSVladimir Sementsov-Ogievskiy        if export_name is None:
159*b6aed193SVladimir Sementsov-Ogievskiy            self.server.cmd('nbd-server-add', device='nbd-export')
160549084eaSVladimir Sementsov-Ogievskiy        else:
161*b6aed193SVladimir Sementsov-Ogievskiy            self.server.cmd('nbd-server-add', device='nbd-export',
162549084eaSVladimir Sementsov-Ogievskiy                            name=export_name)
163b74fc7f7SMax Reitz
164549084eaSVladimir Sementsov-Ogievskiy        if export_name2 is not None:
165*b6aed193SVladimir Sementsov-Ogievskiy            self.server.cmd('nbd-server-add', device='nbd-export',
166549084eaSVladimir Sementsov-Ogievskiy                            name=export_name2)
167549084eaSVladimir Sementsov-Ogievskiy
168908b3016SMax Reitz        return True
169908b3016SMax Reitz
170908b3016SMax Reitz    def _server_up(self, address, export_name=None, export_name2=None):
171908b3016SMax Reitz        self.assertTrue(self._try_server_up(address, export_name, export_name2))
172549084eaSVladimir Sementsov-Ogievskiy
173b74fc7f7SMax Reitz    def _server_down(self):
174*b6aed193SVladimir Sementsov-Ogievskiy        self.server.cmd('nbd-server-stop')
175b74fc7f7SMax Reitz
176549084eaSVladimir Sementsov-Ogievskiy    def do_test_inet(self, export_name=None):
177908b3016SMax Reitz        while True:
178908b3016SMax Reitz            nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
179b74fc7f7SMax Reitz            address = { 'type': 'inet',
180b74fc7f7SMax Reitz                        'data': {
181b74fc7f7SMax Reitz                            'host': 'localhost',
182908b3016SMax Reitz                            'port': str(nbd_port)
183b74fc7f7SMax Reitz                        } }
184908b3016SMax Reitz            if self._try_server_up(address, export_name):
185908b3016SMax Reitz                break
186908b3016SMax Reitz
187549084eaSVladimir Sementsov-Ogievskiy        export_name = export_name or 'nbd-export'
188908b3016SMax Reitz        self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name),
189549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), export_name)
190549084eaSVladimir Sementsov-Ogievskiy        self._server_down()
191549084eaSVladimir Sementsov-Ogievskiy
192549084eaSVladimir Sementsov-Ogievskiy    def test_inet_default_export_name(self):
193549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet()
194549084eaSVladimir Sementsov-Ogievskiy
195549084eaSVladimir Sementsov-Ogievskiy    def test_inet_same_export_name(self):
196549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet('nbd-export')
197549084eaSVladimir Sementsov-Ogievskiy
198549084eaSVladimir Sementsov-Ogievskiy    def test_inet_different_export_name(self):
199549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet('shadow')
200549084eaSVladimir Sementsov-Ogievskiy
201549084eaSVladimir Sementsov-Ogievskiy    def test_inet_two_exports(self):
202908b3016SMax Reitz        while True:
203908b3016SMax Reitz            nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END)
204549084eaSVladimir Sementsov-Ogievskiy            address = { 'type': 'inet',
205549084eaSVladimir Sementsov-Ogievskiy                        'data': {
206549084eaSVladimir Sementsov-Ogievskiy                            'host': 'localhost',
207908b3016SMax Reitz                            'port': str(nbd_port)
208549084eaSVladimir Sementsov-Ogievskiy                        } }
209908b3016SMax Reitz            if self._try_server_up(address, 'exp1', 'exp2'):
210908b3016SMax Reitz                break
211908b3016SMax Reitz
212908b3016SMax Reitz        self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'),
213549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), 'exp1', 'node1', False)
214908b3016SMax Reitz        self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'),
215549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), 'exp2', 'node2', False)
216*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-del', node_name='node1')
217*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('blockdev-del', node_name='node2')
218b74fc7f7SMax Reitz        self._server_down()
219b74fc7f7SMax Reitz
220b74fc7f7SMax Reitz    def test_inet6(self):
221cf1cd117SFam Zheng        try:
222cf1cd117SFam Zheng            socket.getaddrinfo("::0", "0", socket.AF_INET6,
223cf1cd117SFam Zheng                               socket.SOCK_STREAM, socket.IPPROTO_TCP,
224cf1cd117SFam Zheng                               socket.AI_ADDRCONFIG | socket.AI_CANONNAME)
225cf1cd117SFam Zheng        except socket.gaierror:
226cf1cd117SFam Zheng            # IPv6 not available, skip
227cf1cd117SFam Zheng            return
228908b3016SMax Reitz
229908b3016SMax Reitz        while True:
230908b3016SMax Reitz            nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END)
231b74fc7f7SMax Reitz            address = { 'type': 'inet',
232b74fc7f7SMax Reitz                        'data': {
233b74fc7f7SMax Reitz                            'host': '::1',
234908b3016SMax Reitz                            'port': str(nbd_port),
235b74fc7f7SMax Reitz                            'ipv4': False,
236b74fc7f7SMax Reitz                            'ipv6': True
237b74fc7f7SMax Reitz                        } }
238908b3016SMax Reitz            if self._try_server_up(address):
239908b3016SMax Reitz                break
240908b3016SMax Reitz
241b74fc7f7SMax Reitz        filename = { 'driver': 'raw',
242b74fc7f7SMax Reitz                     'file': {
243b74fc7f7SMax Reitz                         'driver': 'nbd',
244b74fc7f7SMax Reitz                         'export': 'nbd-export',
2459445673eSMarkus Armbruster                         'server': flatten_sock_addr(address)
246b74fc7f7SMax Reitz                     } }
2479445673eSMarkus Armbruster        self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
248b74fc7f7SMax Reitz        self._server_down()
249b74fc7f7SMax Reitz
250b74fc7f7SMax Reitz    def test_unix(self):
251b74fc7f7SMax Reitz        address = { 'type': 'unix',
252b74fc7f7SMax Reitz                    'data': { 'path': unix_socket } }
253b74fc7f7SMax Reitz        self._server_up(address)
254b74fc7f7SMax Reitz        self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket,
2559445673eSMarkus Armbruster                         flatten_sock_addr(address), 'nbd-export')
256b74fc7f7SMax Reitz        self._server_down()
257b74fc7f7SMax Reitz
258b74fc7f7SMax Reitz    def test_fd(self):
259b74fc7f7SMax Reitz        self._server_up({ 'type': 'unix',
260b74fc7f7SMax Reitz                          'data': { 'path': unix_socket } })
261b74fc7f7SMax Reitz
262b74fc7f7SMax Reitz        sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
263b74fc7f7SMax Reitz        sockfd.connect(unix_socket)
264b74fc7f7SMax Reitz
265bf43b29dSMax Reitz        result = self.vm.send_fd_scm(fd=sockfd.fileno())
266b74fc7f7SMax Reitz        self.assertEqual(result, 0, 'Failed to send socket FD')
267b74fc7f7SMax Reitz
268*b6aed193SVladimir Sementsov-Ogievskiy        self.vm.cmd('getfd', fdname='nbd-fifo')
269b74fc7f7SMax Reitz
270b74fc7f7SMax Reitz        address = { 'type': 'fd',
271b74fc7f7SMax Reitz                    'data': { 'str': 'nbd-fifo' } }
272b74fc7f7SMax Reitz        filename = { 'driver': 'raw',
273b74fc7f7SMax Reitz                     'file': {
274b74fc7f7SMax Reitz                         'driver': 'nbd',
275b74fc7f7SMax Reitz                         'export': 'nbd-export',
2769445673eSMarkus Armbruster                         'server': flatten_sock_addr(address)
277b74fc7f7SMax Reitz                     } }
2789445673eSMarkus Armbruster        self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
279b74fc7f7SMax Reitz
280b74fc7f7SMax Reitz        self._server_down()
281b74fc7f7SMax Reitz
282b74fc7f7SMax Reitz
283b74fc7f7SMax Reitzif __name__ == '__main__':
2847c932a1dSMax Reitz    iotests.main(supported_fmts=['raw'],
2857c932a1dSMax Reitz                 supported_protocols=['nbd'])
286