xref: /openbmc/qemu/tests/qemu-iotests/147 (revision bf43b29df4cdc716f4a2af98b37e206a11541d99)
1b74fc7f7SMax Reitz#!/usr/bin/env python
2b74fc7f7SMax Reitz#
3b74fc7f7SMax Reitz# Test case for NBD's blockdev-add interface
4b74fc7f7SMax Reitz#
5b74fc7f7SMax Reitz# Copyright (C) 2016 Red Hat, Inc.
6b74fc7f7SMax Reitz#
7b74fc7f7SMax Reitz# This program is free software; you can redistribute it and/or modify
8b74fc7f7SMax Reitz# it under the terms of the GNU General Public License as published by
9b74fc7f7SMax Reitz# the Free Software Foundation; either version 2 of the License, or
10b74fc7f7SMax Reitz# (at your option) any later version.
11b74fc7f7SMax Reitz#
12b74fc7f7SMax Reitz# This program is distributed in the hope that it will be useful,
13b74fc7f7SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of
14b74fc7f7SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15b74fc7f7SMax Reitz# GNU General Public License for more details.
16b74fc7f7SMax Reitz#
17b74fc7f7SMax Reitz# You should have received a copy of the GNU General Public License
18b74fc7f7SMax Reitz# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19b74fc7f7SMax Reitz#
20b74fc7f7SMax Reitz
21b74fc7f7SMax Reitzimport os
22b74fc7f7SMax Reitzimport socket
23b74fc7f7SMax Reitzimport stat
24b74fc7f7SMax Reitzimport time
25b74fc7f7SMax Reitzimport iotests
26b74fc7f7SMax Reitzfrom iotests import cachemode, imgfmt, qemu_img, qemu_nbd
27b74fc7f7SMax Reitz
28b74fc7f7SMax ReitzNBD_PORT = 10811
29b74fc7f7SMax Reitz
30b74fc7f7SMax Reitztest_img = os.path.join(iotests.test_dir, 'test.img')
31b74fc7f7SMax Reitzunix_socket = os.path.join(iotests.test_dir, 'nbd.socket')
32b74fc7f7SMax Reitz
339445673eSMarkus Armbruster
349445673eSMarkus Armbrusterdef flatten_sock_addr(crumpled_address):
359445673eSMarkus Armbruster    result = { 'type': crumpled_address['type'] }
369445673eSMarkus Armbruster    result.update(crumpled_address['data'])
379445673eSMarkus Armbruster    return result
389445673eSMarkus Armbruster
399445673eSMarkus Armbruster
40b74fc7f7SMax Reitzclass NBDBlockdevAddBase(iotests.QMPTestCase):
41549084eaSVladimir Sementsov-Ogievskiy    def blockdev_add_options(self, address, export, node_name):
42549084eaSVladimir Sementsov-Ogievskiy        options = { 'node-name': node_name,
43b74fc7f7SMax Reitz                    'driver': 'raw',
44b74fc7f7SMax Reitz                    'file': {
45b74fc7f7SMax Reitz                        'driver': 'nbd',
461104d83cSEric Blake                        'read-only': True,
47b74fc7f7SMax Reitz                        'server': address
48b74fc7f7SMax Reitz                    } }
49b74fc7f7SMax Reitz        if export is not None:
50b74fc7f7SMax Reitz            options['file']['export'] = export
51b74fc7f7SMax Reitz        return options
52b74fc7f7SMax Reitz
53549084eaSVladimir Sementsov-Ogievskiy    def client_test(self, filename, address, export=None,
54549084eaSVladimir Sementsov-Ogievskiy                    node_name='nbd-blockdev', delete=True):
55549084eaSVladimir Sementsov-Ogievskiy        bao = self.blockdev_add_options(address, export, node_name)
56b74fc7f7SMax Reitz        result = self.vm.qmp('blockdev-add', **bao)
57b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
58b74fc7f7SMax Reitz
59549084eaSVladimir Sementsov-Ogievskiy        found = False
60b74fc7f7SMax Reitz        result = self.vm.qmp('query-named-block-nodes')
61b74fc7f7SMax Reitz        for node in result['return']:
62549084eaSVladimir Sementsov-Ogievskiy            if node['node-name'] == node_name:
63549084eaSVladimir Sementsov-Ogievskiy                found = True
64b74fc7f7SMax Reitz                if isinstance(filename, str):
65b74fc7f7SMax Reitz                    self.assert_qmp(node, 'image/filename', filename)
66b74fc7f7SMax Reitz                else:
67b74fc7f7SMax Reitz                    self.assert_json_filename_equal(node['image']['filename'],
68b74fc7f7SMax Reitz                                                    filename)
69b74fc7f7SMax Reitz                break
70549084eaSVladimir Sementsov-Ogievskiy        self.assertTrue(found)
71b74fc7f7SMax Reitz
72549084eaSVladimir Sementsov-Ogievskiy        if delete:
73549084eaSVladimir Sementsov-Ogievskiy            result = self.vm.qmp('blockdev-del', node_name=node_name)
74b74fc7f7SMax Reitz            self.assert_qmp(result, 'return', {})
75b74fc7f7SMax Reitz
76b74fc7f7SMax Reitz
77b74fc7f7SMax Reitzclass QemuNBD(NBDBlockdevAddBase):
78b74fc7f7SMax Reitz    def setUp(self):
79b74fc7f7SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
80b74fc7f7SMax Reitz        self.vm = iotests.VM()
81b74fc7f7SMax Reitz        self.vm.launch()
82b74fc7f7SMax Reitz
83b74fc7f7SMax Reitz    def tearDown(self):
84b74fc7f7SMax Reitz        self.vm.shutdown()
85b74fc7f7SMax Reitz        os.remove(test_img)
86b74fc7f7SMax Reitz        try:
87b74fc7f7SMax Reitz            os.remove(unix_socket)
88b74fc7f7SMax Reitz        except OSError:
89b74fc7f7SMax Reitz            pass
90b74fc7f7SMax Reitz
91b74fc7f7SMax Reitz    def _server_up(self, *args):
92b74fc7f7SMax Reitz        self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0)
93b74fc7f7SMax Reitz
94b74fc7f7SMax Reitz    def test_inet(self):
95b74fc7f7SMax Reitz        self._server_up('-p', str(NBD_PORT))
96b74fc7f7SMax Reitz        address = { 'type': 'inet',
97b74fc7f7SMax Reitz                    'data': {
98b74fc7f7SMax Reitz                        'host': 'localhost',
99b74fc7f7SMax Reitz                        'port': str(NBD_PORT)
100b74fc7f7SMax Reitz                    } }
1019445673eSMarkus Armbruster        self.client_test('nbd://localhost:%i' % NBD_PORT,
1029445673eSMarkus Armbruster                         flatten_sock_addr(address))
103b74fc7f7SMax Reitz
104b74fc7f7SMax Reitz    def test_unix(self):
105b74fc7f7SMax Reitz        self._server_up('-k', unix_socket)
106b74fc7f7SMax Reitz        address = { 'type': 'unix',
107b74fc7f7SMax Reitz                    'data': { 'path': unix_socket } }
1089445673eSMarkus Armbruster        self.client_test('nbd+unix://?socket=' + unix_socket,
1099445673eSMarkus Armbruster                         flatten_sock_addr(address))
110b74fc7f7SMax Reitz
111b74fc7f7SMax Reitz
112b74fc7f7SMax Reitzclass BuiltinNBD(NBDBlockdevAddBase):
113b74fc7f7SMax Reitz    def setUp(self):
114b74fc7f7SMax Reitz        qemu_img('create', '-f', iotests.imgfmt, test_img, '64k')
115b74fc7f7SMax Reitz        self.vm = iotests.VM()
116b74fc7f7SMax Reitz        self.vm.launch()
117b74fc7f7SMax Reitz        self.server = iotests.VM('.server')
118b74fc7f7SMax Reitz        self.server.add_drive_raw('if=none,id=nbd-export,' +
119b74fc7f7SMax Reitz                                  'file=%s,' % test_img +
120b74fc7f7SMax Reitz                                  'format=%s,' % imgfmt +
121b74fc7f7SMax Reitz                                  'cache=%s' % cachemode)
122b74fc7f7SMax Reitz        self.server.launch()
123b74fc7f7SMax Reitz
124b74fc7f7SMax Reitz    def tearDown(self):
125b74fc7f7SMax Reitz        self.vm.shutdown()
126b74fc7f7SMax Reitz        self.server.shutdown()
127b74fc7f7SMax Reitz        os.remove(test_img)
128b74fc7f7SMax Reitz        try:
129b74fc7f7SMax Reitz            os.remove(unix_socket)
130b74fc7f7SMax Reitz        except OSError:
131b74fc7f7SMax Reitz            pass
132b74fc7f7SMax Reitz
133549084eaSVladimir Sementsov-Ogievskiy    def _server_up(self, address, export_name=None, export_name2=None):
134b74fc7f7SMax Reitz        result = self.server.qmp('nbd-server-start', addr=address)
135b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
136b74fc7f7SMax Reitz
137549084eaSVladimir Sementsov-Ogievskiy        if export_name is None:
138b74fc7f7SMax Reitz            result = self.server.qmp('nbd-server-add', device='nbd-export')
139549084eaSVladimir Sementsov-Ogievskiy        else:
140549084eaSVladimir Sementsov-Ogievskiy            result = self.server.qmp('nbd-server-add', device='nbd-export',
141549084eaSVladimir Sementsov-Ogievskiy                                     name=export_name)
142b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
143b74fc7f7SMax Reitz
144549084eaSVladimir Sementsov-Ogievskiy        if export_name2 is not None:
145549084eaSVladimir Sementsov-Ogievskiy            result = self.server.qmp('nbd-server-add', device='nbd-export',
146549084eaSVladimir Sementsov-Ogievskiy                                     name=export_name2)
147549084eaSVladimir Sementsov-Ogievskiy            self.assert_qmp(result, 'return', {})
148549084eaSVladimir Sementsov-Ogievskiy
149549084eaSVladimir Sementsov-Ogievskiy
150b74fc7f7SMax Reitz    def _server_down(self):
151b74fc7f7SMax Reitz        result = self.server.qmp('nbd-server-stop')
152b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
153b74fc7f7SMax Reitz
154549084eaSVladimir Sementsov-Ogievskiy    def do_test_inet(self, export_name=None):
155b74fc7f7SMax Reitz        address = { 'type': 'inet',
156b74fc7f7SMax Reitz                    'data': {
157b74fc7f7SMax Reitz                        'host': 'localhost',
158b74fc7f7SMax Reitz                        'port': str(NBD_PORT)
159b74fc7f7SMax Reitz                    } }
160549084eaSVladimir Sementsov-Ogievskiy        self._server_up(address, export_name)
161549084eaSVladimir Sementsov-Ogievskiy        export_name = export_name or 'nbd-export'
162549084eaSVladimir Sementsov-Ogievskiy        self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name),
163549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), export_name)
164549084eaSVladimir Sementsov-Ogievskiy        self._server_down()
165549084eaSVladimir Sementsov-Ogievskiy
166549084eaSVladimir Sementsov-Ogievskiy    def test_inet_default_export_name(self):
167549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet()
168549084eaSVladimir Sementsov-Ogievskiy
169549084eaSVladimir Sementsov-Ogievskiy    def test_inet_same_export_name(self):
170549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet('nbd-export')
171549084eaSVladimir Sementsov-Ogievskiy
172549084eaSVladimir Sementsov-Ogievskiy    def test_inet_different_export_name(self):
173549084eaSVladimir Sementsov-Ogievskiy        self.do_test_inet('shadow')
174549084eaSVladimir Sementsov-Ogievskiy
175549084eaSVladimir Sementsov-Ogievskiy    def test_inet_two_exports(self):
176549084eaSVladimir Sementsov-Ogievskiy        address = { 'type': 'inet',
177549084eaSVladimir Sementsov-Ogievskiy                    'data': {
178549084eaSVladimir Sementsov-Ogievskiy                        'host': 'localhost',
179549084eaSVladimir Sementsov-Ogievskiy                        'port': str(NBD_PORT)
180549084eaSVladimir Sementsov-Ogievskiy                    } }
181549084eaSVladimir Sementsov-Ogievskiy        self._server_up(address, 'exp1', 'exp2')
182549084eaSVladimir Sementsov-Ogievskiy        self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'),
183549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), 'exp1', 'node1', False)
184549084eaSVladimir Sementsov-Ogievskiy        self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'),
185549084eaSVladimir Sementsov-Ogievskiy                         flatten_sock_addr(address), 'exp2', 'node2', False)
186549084eaSVladimir Sementsov-Ogievskiy        result = self.vm.qmp('blockdev-del', node_name='node1')
187549084eaSVladimir Sementsov-Ogievskiy        self.assert_qmp(result, 'return', {})
188549084eaSVladimir Sementsov-Ogievskiy        result = self.vm.qmp('blockdev-del', node_name='node2')
189549084eaSVladimir Sementsov-Ogievskiy        self.assert_qmp(result, 'return', {})
190b74fc7f7SMax Reitz        self._server_down()
191b74fc7f7SMax Reitz
192b74fc7f7SMax Reitz    def test_inet6(self):
193cf1cd117SFam Zheng        try:
194cf1cd117SFam Zheng            socket.getaddrinfo("::0", "0", socket.AF_INET6,
195cf1cd117SFam Zheng                               socket.SOCK_STREAM, socket.IPPROTO_TCP,
196cf1cd117SFam Zheng                               socket.AI_ADDRCONFIG | socket.AI_CANONNAME)
197cf1cd117SFam Zheng        except socket.gaierror:
198cf1cd117SFam Zheng            # IPv6 not available, skip
199cf1cd117SFam Zheng            return
200b74fc7f7SMax Reitz        address = { 'type': 'inet',
201b74fc7f7SMax Reitz                    'data': {
202b74fc7f7SMax Reitz                        'host': '::1',
203b74fc7f7SMax Reitz                        'port': str(NBD_PORT),
204b74fc7f7SMax Reitz                        'ipv4': False,
205b74fc7f7SMax Reitz                        'ipv6': True
206b74fc7f7SMax Reitz                    } }
207b74fc7f7SMax Reitz        filename = { 'driver': 'raw',
208b74fc7f7SMax Reitz                     'file': {
209b74fc7f7SMax Reitz                         'driver': 'nbd',
210b74fc7f7SMax Reitz                         'export': 'nbd-export',
2119445673eSMarkus Armbruster                         'server': flatten_sock_addr(address)
212b74fc7f7SMax Reitz                     } }
213b74fc7f7SMax Reitz        self._server_up(address)
2149445673eSMarkus Armbruster        self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
215b74fc7f7SMax Reitz        self._server_down()
216b74fc7f7SMax Reitz
217b74fc7f7SMax Reitz    def test_unix(self):
218b74fc7f7SMax Reitz        address = { 'type': 'unix',
219b74fc7f7SMax Reitz                    'data': { 'path': unix_socket } }
220b74fc7f7SMax Reitz        self._server_up(address)
221b74fc7f7SMax Reitz        self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket,
2229445673eSMarkus Armbruster                         flatten_sock_addr(address), 'nbd-export')
223b74fc7f7SMax Reitz        self._server_down()
224b74fc7f7SMax Reitz
225b74fc7f7SMax Reitz    def test_fd(self):
226b74fc7f7SMax Reitz        self._server_up({ 'type': 'unix',
227b74fc7f7SMax Reitz                          'data': { 'path': unix_socket } })
228b74fc7f7SMax Reitz
229b74fc7f7SMax Reitz        sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
230b74fc7f7SMax Reitz        sockfd.connect(unix_socket)
231b74fc7f7SMax Reitz
232*bf43b29dSMax Reitz        result = self.vm.send_fd_scm(fd=sockfd.fileno())
233b74fc7f7SMax Reitz        self.assertEqual(result, 0, 'Failed to send socket FD')
234b74fc7f7SMax Reitz
235b74fc7f7SMax Reitz        result = self.vm.qmp('getfd', fdname='nbd-fifo')
236b74fc7f7SMax Reitz        self.assert_qmp(result, 'return', {})
237b74fc7f7SMax Reitz
238b74fc7f7SMax Reitz        address = { 'type': 'fd',
239b74fc7f7SMax Reitz                    'data': { 'str': 'nbd-fifo' } }
240b74fc7f7SMax Reitz        filename = { 'driver': 'raw',
241b74fc7f7SMax Reitz                     'file': {
242b74fc7f7SMax Reitz                         'driver': 'nbd',
243b74fc7f7SMax Reitz                         'export': 'nbd-export',
2449445673eSMarkus Armbruster                         'server': flatten_sock_addr(address)
245b74fc7f7SMax Reitz                     } }
2469445673eSMarkus Armbruster        self.client_test(filename, flatten_sock_addr(address), 'nbd-export')
247b74fc7f7SMax Reitz
248b74fc7f7SMax Reitz        self._server_down()
249b74fc7f7SMax Reitz
250b74fc7f7SMax Reitz
251b74fc7f7SMax Reitzif __name__ == '__main__':
252b74fc7f7SMax Reitz    # Need to support image creation
253b74fc7f7SMax Reitz    iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2',
254b74fc7f7SMax Reitz                                 'vmdk', 'raw', 'vhdx', 'qed'])
255