diff options
Diffstat (limited to 'tests')
| -rwxr-xr-x | tests/qemu-iotests/147 | 68 | ||||
| -rw-r--r-- | tests/qemu-iotests/147.out | 4 | ||||
| -rw-r--r-- | tests/qemu-iotests/205 | 156 | ||||
| -rw-r--r-- | tests/qemu-iotests/205.out | 5 | ||||
| -rw-r--r-- | tests/qemu-iotests/group | 1 | ||||
| -rw-r--r-- | tests/qemu-iotests/iotests.py | 38 |
6 files changed, 257 insertions, 15 deletions
diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 90f40ed245..d2081df84b 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -38,8 +38,8 @@ def flatten_sock_addr(crumpled_address): class NBDBlockdevAddBase(iotests.QMPTestCase): - def blockdev_add_options(self, address, export=None): - options = { 'node-name': 'nbd-blockdev', + def blockdev_add_options(self, address, export, node_name): + options = { 'node-name': node_name, 'driver': 'raw', 'file': { 'driver': 'nbd', @@ -50,23 +50,28 @@ class NBDBlockdevAddBase(iotests.QMPTestCase): options['file']['export'] = export return options - def client_test(self, filename, address, export=None): - bao = self.blockdev_add_options(address, export) + def client_test(self, filename, address, export=None, + node_name='nbd-blockdev', delete=True): + bao = self.blockdev_add_options(address, export, node_name) result = self.vm.qmp('blockdev-add', **bao) self.assert_qmp(result, 'return', {}) + found = False result = self.vm.qmp('query-named-block-nodes') for node in result['return']: - if node['node-name'] == 'nbd-blockdev': + if node['node-name'] == node_name: + found = True if isinstance(filename, str): self.assert_qmp(node, 'image/filename', filename) else: self.assert_json_filename_equal(node['image']['filename'], filename) break + self.assertTrue(found) - result = self.vm.qmp('blockdev-del', node_name='nbd-blockdev') - self.assert_qmp(result, 'return', {}) + if delete: + result = self.vm.qmp('blockdev-del', node_name=node_name) + self.assert_qmp(result, 'return', {}) class QemuNBD(NBDBlockdevAddBase): @@ -125,26 +130,63 @@ class BuiltinNBD(NBDBlockdevAddBase): except OSError: pass - def _server_up(self, address): + def _server_up(self, address, export_name=None, export_name2=None): result = self.server.qmp('nbd-server-start', addr=address) self.assert_qmp(result, 'return', {}) - result = self.server.qmp('nbd-server-add', device='nbd-export') + if export_name is None: + result = self.server.qmp('nbd-server-add', device='nbd-export') + else: + result = self.server.qmp('nbd-server-add', device='nbd-export', + name=export_name) self.assert_qmp(result, 'return', {}) + if export_name2 is not None: + result = self.server.qmp('nbd-server-add', device='nbd-export', + name=export_name2) + self.assert_qmp(result, 'return', {}) + + def _server_down(self): result = self.server.qmp('nbd-server-stop') self.assert_qmp(result, 'return', {}) - def test_inet(self): + def do_test_inet(self, export_name=None): address = { 'type': 'inet', 'data': { 'host': 'localhost', 'port': str(NBD_PORT) } } - self._server_up(address) - self.client_test('nbd://localhost:%i/nbd-export' % NBD_PORT, - flatten_sock_addr(address), 'nbd-export') + self._server_up(address, export_name) + export_name = export_name or 'nbd-export' + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name), + flatten_sock_addr(address), export_name) + self._server_down() + + def test_inet_default_export_name(self): + self.do_test_inet() + + def test_inet_same_export_name(self): + self.do_test_inet('nbd-export') + + def test_inet_different_export_name(self): + self.do_test_inet('shadow') + + def test_inet_two_exports(self): + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(NBD_PORT) + } } + self._server_up(address, 'exp1', 'exp2') + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'), + flatten_sock_addr(address), 'exp1', 'node1', False) + self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'), + flatten_sock_addr(address), 'exp2', 'node2', False) + result = self.vm.qmp('blockdev-del', node_name='node1') + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-del', node_name='node2') + self.assert_qmp(result, 'return', {}) self._server_down() def test_inet6(self): diff --git a/tests/qemu-iotests/147.out b/tests/qemu-iotests/147.out index 3f8a935a08..dae404e278 100644 --- a/tests/qemu-iotests/147.out +++ b/tests/qemu-iotests/147.out @@ -1,5 +1,5 @@ -...... +......... ---------------------------------------------------------------------- -Ran 6 tests +Ran 9 tests OK diff --git a/tests/qemu-iotests/205 b/tests/qemu-iotests/205 new file mode 100644 index 0000000000..10388920dc --- /dev/null +++ b/tests/qemu-iotests/205 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# +# Tests for qmp command nbd-server-remove. +# +# Copyright (c) 2017 Virtuozzo International GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import sys +import iotests +import time +from iotests import qemu_img, qemu_io, filter_qemu_io, QemuIoInteractive + +nbd_sock = 'nbd_sock' +nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock +disk = os.path.join(iotests.test_dir, 'disk') + + +class TestNbdServerRemove(iotests.QMPTestCase): + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, disk, '1M') + + self.vm = iotests.VM().add_drive(disk) + self.vm.launch() + + address = { + 'type': 'unix', + 'data': { + 'path': nbd_sock + } + } + + result = self.vm.qmp('nbd-server-start', addr=address) + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('nbd-server-add', device='drive0', name='exp') + self.assert_qmp(result, 'return', {}) + + def tearDown(self): + self.vm.shutdown() + os.remove(nbd_sock) + os.remove(disk) + + def remove_export(self, name, mode=None): + if mode is None: + return self.vm.qmp('nbd-server-remove', name=name) + else: + return self.vm.qmp('nbd-server-remove', name=name, mode=mode) + + def assertExportNotFound(self, name): + result = self.vm.qmp('nbd-server-remove', name=name) + self.assert_qmp(result, 'error/desc', "Export 'exp' is not found") + + def assertExistingClients(self, result): + self.assert_qmp(result, 'error/desc', "export 'exp' still in use") + + def assertReadOk(self, qemu_io_output): + self.assertEqual( + filter_qemu_io(qemu_io_output).strip(), + 'read 512/512 bytes at offset 0\n' + + '512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)') + + def assertReadFailed(self, qemu_io_output): + self.assertEqual(filter_qemu_io(qemu_io_output).strip(), + 'read failed: Input/output error') + + def assertConnectFailed(self, qemu_io_output): + self.assertEqual(filter_qemu_io(qemu_io_output).strip(), + "can't open device " + nbd_uri + + ": Requested export not available\n" + "server reported: export 'exp' not present") + + def do_test_connect_after_remove(self, mode=None): + args = ('-r', '-f', 'raw', '-c', 'read 0 512', nbd_uri) + self.assertReadOk(qemu_io(*args)) + + result = self.remove_export('exp', mode) + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + self.assertConnectFailed(qemu_io(*args)) + + def test_connect_after_remove_default(self): + self.do_test_connect_after_remove() + + def test_connect_after_remove_safe(self): + self.do_test_connect_after_remove('safe') + + def test_connect_after_remove_force(self): + self.do_test_connect_after_remove('hard') + + def do_test_remove_during_connect_safe(self, mode=None): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', mode) + self.assertExistingClients(result) + + self.assertReadOk(qio.cmd('read 0 512')) + + qio.close() + + result = self.remove_export('exp', mode) + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + + def test_remove_during_connect_default(self): + self.do_test_remove_during_connect_safe() + + def test_remove_during_connect_safe(self): + self.do_test_remove_during_connect_safe('safe') + + def test_remove_during_connect_hard(self): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'hard') + self.assert_qmp(result, 'return', {}) + + self.assertReadFailed(qio.cmd('read 0 512')) + self.assertExportNotFound('exp') + + qio.close() + + def test_remove_during_connect_safe_hard(self): + qio = QemuIoInteractive('-r', '-f', 'raw', nbd_uri) + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'safe') + self.assertExistingClients(result) + + self.assertReadOk(qio.cmd('read 0 512')) + + result = self.remove_export('exp', 'hard') + self.assert_qmp(result, 'return', {}) + + self.assertExportNotFound('exp') + self.assertReadFailed(qio.cmd('read 0 512')) + qio.close() + + +if __name__ == '__main__': + iotests.main() diff --git a/tests/qemu-iotests/205.out b/tests/qemu-iotests/205.out new file mode 100644 index 0000000000..2f7d3902f2 --- /dev/null +++ b/tests/qemu-iotests/205.out @@ -0,0 +1,5 @@ +....... +---------------------------------------------------------------------- +Ran 7 tests + +OK diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 8fc4f62cca..a2dfe79d86 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -201,3 +201,4 @@ 202 rw auto quick 203 rw auto 204 rw auto quick +205 rw auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 44477e9295..5a10b2d534 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -93,6 +93,44 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] + +class QemuIoInteractive: + def __init__(self, *args): + self.args = qemu_io_args + list(args) + self._p = subprocess.Popen(self.args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + assert self._p.stdout.read(9) == 'qemu-io> ' + + def close(self): + self._p.communicate('q\n') + + def _read_output(self): + pattern = 'qemu-io> ' + n = len(pattern) + pos = 0 + s = [] + while pos != n: + c = self._p.stdout.read(1) + # check unexpected EOF + assert c != '' + s.append(c) + if c == pattern[pos]: + pos += 1 + else: + pos = 0 + + return ''.join(s[:-n]) + + def cmd(self, cmd): + # quit command is in close(), '\n' is added automatically + assert '\n' not in cmd + cmd = cmd.strip() + assert cmd != 'q' and cmd != 'quit' + self._p.stdin.write(cmd + '\n') + return self._read_output() + + def qemu_nbd(*args): '''Run qemu-nbd in daemon mode and return the parent's exit code''' return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) |