1*b74fc7f7SMax Reitz#!/usr/bin/env python 2*b74fc7f7SMax Reitz# 3*b74fc7f7SMax Reitz# Test case for NBD's blockdev-add interface 4*b74fc7f7SMax Reitz# 5*b74fc7f7SMax Reitz# Copyright (C) 2016 Red Hat, Inc. 6*b74fc7f7SMax Reitz# 7*b74fc7f7SMax Reitz# This program is free software; you can redistribute it and/or modify 8*b74fc7f7SMax Reitz# it under the terms of the GNU General Public License as published by 9*b74fc7f7SMax Reitz# the Free Software Foundation; either version 2 of the License, or 10*b74fc7f7SMax Reitz# (at your option) any later version. 11*b74fc7f7SMax Reitz# 12*b74fc7f7SMax Reitz# This program is distributed in the hope that it will be useful, 13*b74fc7f7SMax Reitz# but WITHOUT ANY WARRANTY; without even the implied warranty of 14*b74fc7f7SMax Reitz# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*b74fc7f7SMax Reitz# GNU General Public License for more details. 16*b74fc7f7SMax Reitz# 17*b74fc7f7SMax Reitz# You should have received a copy of the GNU General Public License 18*b74fc7f7SMax Reitz# along with this program. If not, see <http://www.gnu.org/licenses/>. 19*b74fc7f7SMax Reitz# 20*b74fc7f7SMax Reitz 21*b74fc7f7SMax Reitzimport os 22*b74fc7f7SMax Reitzimport socket 23*b74fc7f7SMax Reitzimport stat 24*b74fc7f7SMax Reitzimport time 25*b74fc7f7SMax Reitzimport iotests 26*b74fc7f7SMax Reitzfrom iotests import cachemode, imgfmt, qemu_img, qemu_nbd 27*b74fc7f7SMax Reitz 28*b74fc7f7SMax ReitzNBD_PORT = 10811 29*b74fc7f7SMax Reitz 30*b74fc7f7SMax Reitztest_img = os.path.join(iotests.test_dir, 'test.img') 31*b74fc7f7SMax Reitzunix_socket = os.path.join(iotests.test_dir, 'nbd.socket') 32*b74fc7f7SMax Reitz 33*b74fc7f7SMax Reitzclass NBDBlockdevAddBase(iotests.QMPTestCase): 34*b74fc7f7SMax Reitz def blockdev_add_options(self, address, export=None): 35*b74fc7f7SMax Reitz options = { 'node-name': 'nbd-blockdev', 36*b74fc7f7SMax Reitz 'driver': 'raw', 37*b74fc7f7SMax Reitz 'file': { 38*b74fc7f7SMax Reitz 'driver': 'nbd', 39*b74fc7f7SMax Reitz 'server': address 40*b74fc7f7SMax Reitz } } 41*b74fc7f7SMax Reitz if export is not None: 42*b74fc7f7SMax Reitz options['file']['export'] = export 43*b74fc7f7SMax Reitz return options 44*b74fc7f7SMax Reitz 45*b74fc7f7SMax Reitz def client_test(self, filename, address, export=None): 46*b74fc7f7SMax Reitz bao = self.blockdev_add_options(address, export) 47*b74fc7f7SMax Reitz result = self.vm.qmp('blockdev-add', **bao) 48*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 49*b74fc7f7SMax Reitz 50*b74fc7f7SMax Reitz result = self.vm.qmp('query-named-block-nodes') 51*b74fc7f7SMax Reitz for node in result['return']: 52*b74fc7f7SMax Reitz if node['node-name'] == 'nbd-blockdev': 53*b74fc7f7SMax Reitz if isinstance(filename, str): 54*b74fc7f7SMax Reitz self.assert_qmp(node, 'image/filename', filename) 55*b74fc7f7SMax Reitz else: 56*b74fc7f7SMax Reitz self.assert_json_filename_equal(node['image']['filename'], 57*b74fc7f7SMax Reitz filename) 58*b74fc7f7SMax Reitz break 59*b74fc7f7SMax Reitz 60*b74fc7f7SMax Reitz result = self.vm.qmp('x-blockdev-del', node_name='nbd-blockdev') 61*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 62*b74fc7f7SMax Reitz 63*b74fc7f7SMax Reitz 64*b74fc7f7SMax Reitzclass QemuNBD(NBDBlockdevAddBase): 65*b74fc7f7SMax Reitz def setUp(self): 66*b74fc7f7SMax Reitz qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') 67*b74fc7f7SMax Reitz self.vm = iotests.VM() 68*b74fc7f7SMax Reitz self.vm.launch() 69*b74fc7f7SMax Reitz 70*b74fc7f7SMax Reitz def tearDown(self): 71*b74fc7f7SMax Reitz self.vm.shutdown() 72*b74fc7f7SMax Reitz os.remove(test_img) 73*b74fc7f7SMax Reitz try: 74*b74fc7f7SMax Reitz os.remove(unix_socket) 75*b74fc7f7SMax Reitz except OSError: 76*b74fc7f7SMax Reitz pass 77*b74fc7f7SMax Reitz 78*b74fc7f7SMax Reitz def _server_up(self, *args): 79*b74fc7f7SMax Reitz self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) 80*b74fc7f7SMax Reitz 81*b74fc7f7SMax Reitz def test_inet(self): 82*b74fc7f7SMax Reitz self._server_up('-p', str(NBD_PORT)) 83*b74fc7f7SMax Reitz address = { 'type': 'inet', 84*b74fc7f7SMax Reitz 'data': { 85*b74fc7f7SMax Reitz 'host': 'localhost', 86*b74fc7f7SMax Reitz 'port': str(NBD_PORT) 87*b74fc7f7SMax Reitz } } 88*b74fc7f7SMax Reitz self.client_test('nbd://localhost:%i' % NBD_PORT, address) 89*b74fc7f7SMax Reitz 90*b74fc7f7SMax Reitz def test_unix(self): 91*b74fc7f7SMax Reitz self._server_up('-k', unix_socket) 92*b74fc7f7SMax Reitz address = { 'type': 'unix', 93*b74fc7f7SMax Reitz 'data': { 'path': unix_socket } } 94*b74fc7f7SMax Reitz self.client_test('nbd+unix://?socket=' + unix_socket, address) 95*b74fc7f7SMax Reitz 96*b74fc7f7SMax Reitz 97*b74fc7f7SMax Reitzclass BuiltinNBD(NBDBlockdevAddBase): 98*b74fc7f7SMax Reitz def setUp(self): 99*b74fc7f7SMax Reitz qemu_img('create', '-f', iotests.imgfmt, test_img, '64k') 100*b74fc7f7SMax Reitz self.vm = iotests.VM() 101*b74fc7f7SMax Reitz self.vm.launch() 102*b74fc7f7SMax Reitz self.server = iotests.VM('.server') 103*b74fc7f7SMax Reitz self.server.add_drive_raw('if=none,id=nbd-export,' + 104*b74fc7f7SMax Reitz 'file=%s,' % test_img + 105*b74fc7f7SMax Reitz 'format=%s,' % imgfmt + 106*b74fc7f7SMax Reitz 'cache=%s' % cachemode) 107*b74fc7f7SMax Reitz self.server.launch() 108*b74fc7f7SMax Reitz 109*b74fc7f7SMax Reitz def tearDown(self): 110*b74fc7f7SMax Reitz self.vm.shutdown() 111*b74fc7f7SMax Reitz self.server.shutdown() 112*b74fc7f7SMax Reitz os.remove(test_img) 113*b74fc7f7SMax Reitz try: 114*b74fc7f7SMax Reitz os.remove(unix_socket) 115*b74fc7f7SMax Reitz except OSError: 116*b74fc7f7SMax Reitz pass 117*b74fc7f7SMax Reitz 118*b74fc7f7SMax Reitz def _server_up(self, address): 119*b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-start', addr=address) 120*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 121*b74fc7f7SMax Reitz 122*b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-add', device='nbd-export') 123*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 124*b74fc7f7SMax Reitz 125*b74fc7f7SMax Reitz def _server_down(self): 126*b74fc7f7SMax Reitz result = self.server.qmp('nbd-server-stop') 127*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 128*b74fc7f7SMax Reitz 129*b74fc7f7SMax Reitz def test_inet(self): 130*b74fc7f7SMax Reitz address = { 'type': 'inet', 131*b74fc7f7SMax Reitz 'data': { 132*b74fc7f7SMax Reitz 'host': 'localhost', 133*b74fc7f7SMax Reitz 'port': str(NBD_PORT) 134*b74fc7f7SMax Reitz } } 135*b74fc7f7SMax Reitz self._server_up(address) 136*b74fc7f7SMax Reitz self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, 137*b74fc7f7SMax Reitz address, 'nbd-export') 138*b74fc7f7SMax Reitz self._server_down() 139*b74fc7f7SMax Reitz 140*b74fc7f7SMax Reitz def test_inet6(self): 141*b74fc7f7SMax Reitz address = { 'type': 'inet', 142*b74fc7f7SMax Reitz 'data': { 143*b74fc7f7SMax Reitz 'host': '::1', 144*b74fc7f7SMax Reitz 'port': str(NBD_PORT), 145*b74fc7f7SMax Reitz 'ipv4': False, 146*b74fc7f7SMax Reitz 'ipv6': True 147*b74fc7f7SMax Reitz } } 148*b74fc7f7SMax Reitz filename = { 'driver': 'raw', 149*b74fc7f7SMax Reitz 'file': { 150*b74fc7f7SMax Reitz 'driver': 'nbd', 151*b74fc7f7SMax Reitz 'export': 'nbd-export', 152*b74fc7f7SMax Reitz 'server': address 153*b74fc7f7SMax Reitz } } 154*b74fc7f7SMax Reitz self._server_up(address) 155*b74fc7f7SMax Reitz self.client_test(filename, address, 'nbd-export') 156*b74fc7f7SMax Reitz self._server_down() 157*b74fc7f7SMax Reitz 158*b74fc7f7SMax Reitz def test_unix(self): 159*b74fc7f7SMax Reitz address = { 'type': 'unix', 160*b74fc7f7SMax Reitz 'data': { 'path': unix_socket } } 161*b74fc7f7SMax Reitz self._server_up(address) 162*b74fc7f7SMax Reitz self.client_test('nbd+unix:///nbd-export?socket=' + unix_socket, 163*b74fc7f7SMax Reitz address, 'nbd-export') 164*b74fc7f7SMax Reitz self._server_down() 165*b74fc7f7SMax Reitz 166*b74fc7f7SMax Reitz def test_fd(self): 167*b74fc7f7SMax Reitz self._server_up({ 'type': 'unix', 168*b74fc7f7SMax Reitz 'data': { 'path': unix_socket } }) 169*b74fc7f7SMax Reitz 170*b74fc7f7SMax Reitz sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 171*b74fc7f7SMax Reitz sockfd.connect(unix_socket) 172*b74fc7f7SMax Reitz 173*b74fc7f7SMax Reitz result = self.vm.send_fd_scm(str(sockfd.fileno())) 174*b74fc7f7SMax Reitz self.assertEqual(result, 0, 'Failed to send socket FD') 175*b74fc7f7SMax Reitz 176*b74fc7f7SMax Reitz result = self.vm.qmp('getfd', fdname='nbd-fifo') 177*b74fc7f7SMax Reitz self.assert_qmp(result, 'return', {}) 178*b74fc7f7SMax Reitz 179*b74fc7f7SMax Reitz address = { 'type': 'fd', 180*b74fc7f7SMax Reitz 'data': { 'str': 'nbd-fifo' } } 181*b74fc7f7SMax Reitz filename = { 'driver': 'raw', 182*b74fc7f7SMax Reitz 'file': { 183*b74fc7f7SMax Reitz 'driver': 'nbd', 184*b74fc7f7SMax Reitz 'export': 'nbd-export', 185*b74fc7f7SMax Reitz 'server': address 186*b74fc7f7SMax Reitz } } 187*b74fc7f7SMax Reitz self.client_test(filename, address, 'nbd-export') 188*b74fc7f7SMax Reitz 189*b74fc7f7SMax Reitz self._server_down() 190*b74fc7f7SMax Reitz 191*b74fc7f7SMax Reitz 192*b74fc7f7SMax Reitzif __name__ == '__main__': 193*b74fc7f7SMax Reitz # Need to support image creation 194*b74fc7f7SMax Reitz iotests.main(supported_fmts=['vpc', 'parallels', 'qcow', 'vdi', 'qcow2', 195*b74fc7f7SMax Reitz 'vmdk', 'raw', 'vhdx', 'qed']) 196