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 33*9445673eSMarkus Armbruster 34*9445673eSMarkus Armbrusterdef flatten_sock_addr(crumpled_address): 35*9445673eSMarkus Armbruster result = { 'type': crumpled_address['type'] } 36*9445673eSMarkus Armbruster result.update(crumpled_address['data']) 37*9445673eSMarkus Armbruster return result 38*9445673eSMarkus Armbruster 39*9445673eSMarkus Armbruster 40b74fc7f7SMax Reitzclass NBDBlockdevAddBase(iotests.QMPTestCase): 41b74fc7f7SMax Reitz def blockdev_add_options(self, address, export=None): 42b74fc7f7SMax Reitz options = { 'node-name': 'nbd-blockdev', 43b74fc7f7SMax Reitz 'driver': 'raw', 44b74fc7f7SMax Reitz 'file': { 45b74fc7f7SMax Reitz 'driver': 'nbd', 46b74fc7f7SMax Reitz 'server': address 47b74fc7f7SMax Reitz } } 48b74fc7f7SMax Reitz if export is not None: 49b74fc7f7SMax Reitz options['file']['export'] = export 50b74fc7f7SMax Reitz return options 51b74fc7f7SMax Reitz 52b74fc7f7SMax Reitz def client_test(self, filename, address, export=None): 53b74fc7f7SMax Reitz bao = self.blockdev_add_options(address, export) 54b74fc7f7SMax Reitz result = self.vm.qmp('blockdev-add', **bao) 55b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 56b74fc7f7SMax Reitz 57b74fc7f7SMax Reitz result = self.vm.qmp('query-named-block-nodes') 58b74fc7f7SMax Reitz for node in result['return']: 59b74fc7f7SMax Reitz if node['node-name'] == 'nbd-blockdev': 60b74fc7f7SMax Reitz if isinstance(filename, str): 61b74fc7f7SMax Reitz self.assert_qmp(node, 'image/filename', filename) 62b74fc7f7SMax Reitz else: 63b74fc7f7SMax Reitz self.assert_json_filename_equal(node['image']['filename'], 64b74fc7f7SMax Reitz filename) 65b74fc7f7SMax Reitz break 66b74fc7f7SMax Reitz 6779b7a77eSMarkus Armbruster result = self.vm.qmp('blockdev-del', node_name='nbd-blockdev') 68b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 69b74fc7f7SMax Reitz 70b74fc7f7SMax Reitz 71b74fc7f7SMax Reitzclass QemuNBD(NBDBlockdevAddBase): 72b74fc7f7SMax Reitz def setUp(self): 73b74fc7f7SMax Reitz qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') 74b74fc7f7SMax Reitz self.vm = iotests.VM() 75b74fc7f7SMax Reitz self.vm.launch() 76b74fc7f7SMax Reitz 77b74fc7f7SMax Reitz def tearDown(self): 78b74fc7f7SMax Reitz self.vm.shutdown() 79b74fc7f7SMax Reitz os.remove(test_img) 80b74fc7f7SMax Reitz try: 81b74fc7f7SMax Reitz os.remove(unix_socket) 82b74fc7f7SMax Reitz except OSError: 83b74fc7f7SMax Reitz pass 84b74fc7f7SMax Reitz 85b74fc7f7SMax Reitz def _server_up(self, *args): 86b74fc7f7SMax Reitz self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) 87b74fc7f7SMax Reitz 88b74fc7f7SMax Reitz def test_inet(self): 89b74fc7f7SMax Reitz self._server_up('-p', str(NBD_PORT)) 90b74fc7f7SMax Reitz address = { 'type': 'inet', 91b74fc7f7SMax Reitz 'data': { 92b74fc7f7SMax Reitz 'host': 'localhost', 93b74fc7f7SMax Reitz 'port': str(NBD_PORT) 94b74fc7f7SMax Reitz } } 95*9445673eSMarkus Armbruster self.client_test('nbd://localhost:%i' % NBD_PORT, 96*9445673eSMarkus Armbruster flatten_sock_addr(address)) 97b74fc7f7SMax Reitz 98b74fc7f7SMax Reitz def test_unix(self): 99b74fc7f7SMax Reitz self._server_up('-k', unix_socket) 100b74fc7f7SMax Reitz address = { 'type': 'unix', 101b74fc7f7SMax Reitz 'data': { 'path': unix_socket } } 102*9445673eSMarkus Armbruster self.client_test('nbd+unix://?socket=' + unix_socket, 103*9445673eSMarkus Armbruster flatten_sock_addr(address)) 104b74fc7f7SMax Reitz 105b74fc7f7SMax Reitz 106b74fc7f7SMax Reitzclass BuiltinNBD(NBDBlockdevAddBase): 107b74fc7f7SMax Reitz def setUp(self): 108b74fc7f7SMax Reitz qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') 109b74fc7f7SMax Reitz self.vm = iotests.VM() 110b74fc7f7SMax Reitz self.vm.launch() 111b74fc7f7SMax Reitz self.server = iotests.VM('.server') 112b74fc7f7SMax Reitz self.server.add_drive_raw('if=none,id=nbd-export,' + 113b74fc7f7SMax Reitz 'file=%s,' % test_img + 114b74fc7f7SMax Reitz 'format=%s,' % imgfmt + 115b74fc7f7SMax Reitz 'cache=%s' % cachemode) 116b74fc7f7SMax Reitz self.server.launch() 117b74fc7f7SMax Reitz 118b74fc7f7SMax Reitz def tearDown(self): 119b74fc7f7SMax Reitz self.vm.shutdown() 120b74fc7f7SMax Reitz self.server.shutdown() 121b74fc7f7SMax Reitz os.remove(test_img) 122b74fc7f7SMax Reitz try: 123b74fc7f7SMax Reitz os.remove(unix_socket) 124b74fc7f7SMax Reitz except OSError: 125b74fc7f7SMax Reitz pass 126b74fc7f7SMax Reitz 127b74fc7f7SMax Reitz def _server_up(self, address): 128b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-start', addr=address) 129b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 130b74fc7f7SMax Reitz 131b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-add', device='nbd-export') 132b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 133b74fc7f7SMax Reitz 134b74fc7f7SMax Reitz def _server_down(self): 135b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-stop') 136b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 137b74fc7f7SMax Reitz 138b74fc7f7SMax Reitz def test_inet(self): 139b74fc7f7SMax Reitz address = { 'type': 'inet', 140b74fc7f7SMax Reitz 'data': { 141b74fc7f7SMax Reitz 'host': 'localhost', 142b74fc7f7SMax Reitz 'port': str(NBD_PORT) 143b74fc7f7SMax Reitz } } 144b74fc7f7SMax Reitz self._server_up(address) 145b74fc7f7SMax Reitz self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, 146*9445673eSMarkus Armbruster flatten_sock_addr(address), 'nbd-export') 147b74fc7f7SMax Reitz self._server_down() 148b74fc7f7SMax Reitz 149b74fc7f7SMax Reitz def test_inet6(self): 150b74fc7f7SMax Reitz address = { 'type': 'inet', 151b74fc7f7SMax Reitz 'data': { 152b74fc7f7SMax Reitz 'host': '::1', 153b74fc7f7SMax Reitz 'port': str(NBD_PORT), 154b74fc7f7SMax Reitz 'ipv4': False, 155b74fc7f7SMax Reitz 'ipv6': True 156b74fc7f7SMax Reitz } } 157b74fc7f7SMax Reitz filename = { 'driver': 'raw', 158b74fc7f7SMax Reitz 'file': { 159b74fc7f7SMax Reitz 'driver': 'nbd', 160b74fc7f7SMax Reitz 'export': 'nbd-export', 161*9445673eSMarkus Armbruster 'server': flatten_sock_addr(address) 162b74fc7f7SMax Reitz } } 163b74fc7f7SMax Reitz self._server_up(address) 164*9445673eSMarkus Armbruster self.client_test(filename, flatten_sock_addr(address), 'nbd-export') 165b74fc7f7SMax Reitz self._server_down() 166b74fc7f7SMax Reitz 167b74fc7f7SMax Reitz def test_unix(self): 168b74fc7f7SMax Reitz address = { 'type': 'unix', 169b74fc7f7SMax Reitz 'data': { 'path': unix_socket } } 170b74fc7f7SMax Reitz self._server_up(address) 171b74fc7f7SMax Reitz self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket, 172*9445673eSMarkus Armbruster flatten_sock_addr(address), 'nbd-export') 173b74fc7f7SMax Reitz self._server_down() 174b74fc7f7SMax Reitz 175b74fc7f7SMax Reitz def test_fd(self): 176b74fc7f7SMax Reitz self._server_up({ 'type': 'unix', 177b74fc7f7SMax Reitz 'data': { 'path': unix_socket } }) 178b74fc7f7SMax Reitz 179b74fc7f7SMax Reitz sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 180b74fc7f7SMax Reitz sockfd.connect(unix_socket) 181b74fc7f7SMax Reitz 182b74fc7f7SMax Reitz result = self.vm.send_fd_scm(str(sockfd.fileno())) 183b74fc7f7SMax Reitz self.assertEqual(result, 0, 'Failed to send socket FD') 184b74fc7f7SMax Reitz 185b74fc7f7SMax Reitz result = self.vm.qmp('getfd', fdname='nbd-fifo') 186b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 187b74fc7f7SMax Reitz 188b74fc7f7SMax Reitz address = { 'type': 'fd', 189b74fc7f7SMax Reitz 'data': { 'str': 'nbd-fifo' } } 190b74fc7f7SMax Reitz filename = { 'driver': 'raw', 191b74fc7f7SMax Reitz 'file': { 192b74fc7f7SMax Reitz 'driver': 'nbd', 193b74fc7f7SMax Reitz 'export': 'nbd-export', 194*9445673eSMarkus Armbruster 'server': flatten_sock_addr(address) 195b74fc7f7SMax Reitz } } 196*9445673eSMarkus Armbruster self.client_test(filename, flatten_sock_addr(address), 'nbd-export') 197b74fc7f7SMax Reitz 198b74fc7f7SMax Reitz self._server_down() 199b74fc7f7SMax Reitz 200b74fc7f7SMax Reitz 201b74fc7f7SMax Reitzif __name__ == '__main__': 202b74fc7f7SMax Reitz # Need to support image creation 203b74fc7f7SMax Reitz iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', 204b74fc7f7SMax Reitz 'vmdk', 'raw', 'vhdx', 'qed']) 205