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