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