From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, den@openvz.org
Subject: [PATCH hci-8.0 1/5] iotests: add coverage for NBD transmission commands #VSTOR-119829
Date: Mon, 31 Aug 2026 17:25:24 +0200 [thread overview]
Message-ID: <20260831152528.1350583-2-den@openvz.org> (raw)
In-Reply-To: <20260831152528.1350583-1-den@openvz.org>
From: Denis V. Lunev <den@openvz.org>
NBD_CMD_CACHE has no coverage anywhere in the tree. Nothing ever
sends it: our own NBD client does not implement the command, and
neither qemu-io nor 'qemu-nbd --list' can issue one, so the only
clients reaching this server path are external ones.
Add a test driven by libnbd, gated the way nbd-multiconn already is,
and start it with the case the command exists for. The export is a
qcow2 image over a fully written backing file, so a prefetch has
visible work to do.
Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Eric Blake <eblake@redhat.com>
Message-ID: <20260827161002.310688-2-den@openvz.org>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
tests/qemu-iotests/tests/nbd-commands | 140 ++++++++++++++++++++++
tests/qemu-iotests/tests/nbd-commands.out | 5 +
2 files changed, 145 insertions(+)
create mode 100755 tests/qemu-iotests/tests/nbd-commands
create mode 100644 tests/qemu-iotests/tests/nbd-commands.out
diff --git a/tests/qemu-iotests/tests/nbd-commands b/tests/qemu-iotests/tests/nbd-commands
new file mode 100755
index 00000000000..4c1cd33db74
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-commands
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+# group: rw auto quick
+#
+# Test NBD transmission commands against a qemu NBD export
+#
+# Copyright (C) 2026 Virtuozzo International GmbH
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+from types import ModuleType
+
+import iotests
+from iotests import qemu_img_create, qemu_img_map, qemu_io
+
+
+base = os.path.join(iotests.test_dir, 'base')
+top = os.path.join(iotests.test_dir, 'top')
+# Larger than the maximum payload size an export can advertise
+size = 64 * 1024 * 1024
+pattern = 0xa5
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd_sock')
+nbd_uri = 'nbd+unix:///exp?socket=' + nbd_sock
+nbd: ModuleType
+
+DEPTH_LOCAL = 1
+DEPTH_BACKING = 2
+
+
+class TestNbdCommands(iotests.QMPTestCase):
+ def setUp(self):
+ qemu_img_create('-f', iotests.imgfmt, base, str(size))
+ qemu_io('-c', f'write -P {pattern} 0 {size}', base)
+ qemu_img_create('-f', iotests.imgfmt, '-b', base,
+ '-F', iotests.imgfmt, top, str(size))
+
+ self.vm = iotests.VM()
+ self.vm.launch()
+ self.vm.cmd('blockdev-add', {
+ 'driver': iotests.imgfmt,
+ 'node-name': 'n',
+ 'file': {'driver': 'file', 'filename': top},
+ 'backing': {
+ 'driver': iotests.imgfmt,
+ 'node-name': 'base',
+ 'file': {'driver': 'file', 'filename': base},
+ },
+ })
+ self.vm.cmd('nbd-server-start', {
+ 'addr': {'type': 'unix', 'data': {'path': nbd_sock}}
+ })
+ self.vm.cmd('block-export-add', {
+ 'type': 'nbd',
+ 'id': 'exp',
+ 'node-name': 'n',
+ 'name': 'exp',
+ 'writable': True,
+ 'allocation-depth': True,
+ })
+
+ self.h = None
+ self.connect()
+
+ def tearDown(self):
+ self.disconnect()
+ self.vm.shutdown()
+ for f in (top, base, nbd_sock):
+ try:
+ os.remove(f)
+ except OSError:
+ pass
+
+ def connect(self, structured=True, extended=True):
+ self.disconnect()
+ h = nbd.NBD()
+ h.set_request_structured_replies(structured)
+ h.set_request_extended_headers(extended)
+ h.add_meta_context('base:allocation')
+ h.add_meta_context('qemu:allocation-depth')
+ # Let the server, not libnbd, reject the out of range requests below
+ h.set_strict_mode(h.get_strict_mode() &
+ ~(nbd.STRICT_BOUNDS | nbd.STRICT_PAYLOAD))
+ h.connect_uri(nbd_uri)
+ self.assertEqual(h.get_structured_replies_negotiated(), structured)
+ self.assertEqual(h.get_extended_headers_negotiated(), extended)
+ self.h = h
+
+ def disconnect(self):
+ if self.h is not None:
+ self.h.shutdown()
+ self.h = None
+
+ def block_status(self, count=size):
+ """Map each meta context in the reply to its list of extents."""
+ reply = {}
+
+ def cb(meta, _offset, entries, _err):
+ reply.setdefault(meta, []).extend(zip(entries[0::2],
+ entries[1::2]))
+
+ self.h.block_status(count, 0, cb)
+ return reply
+
+ def top_extents(self):
+ """Which parts of the top image are local, once qemu has let go."""
+ self.disconnect()
+ self.vm.shutdown()
+ return [(e['start'], e['length'], e['depth'])
+ for e in qemu_img_map(top)]
+
+ def test_cache_copies_on_read(self):
+ maximum = self.h.get_block_size(nbd.SIZE_MAXIMUM)
+ self.assertLess(maximum, size)
+ self.assertEqual(self.block_status()['qemu:allocation-depth'],
+ [(size, DEPTH_BACKING)])
+
+ self.h.cache(maximum, 0)
+
+ self.assertEqual(self.top_extents(),
+ [(0, maximum, 0), (maximum, size - maximum, 1)])
+ qemu_io('-c', f'read -P {pattern} 0 {size}', top)
+
+ def test_cache_past_end_of_export(self):
+ self.assertRaises(nbd.Error, self.h.cache, size + 1, 0)
+
+ def test_read_bound_by_max_payload(self):
+ maximum = self.h.get_block_size(nbd.SIZE_MAXIMUM)
+ self.assertRaises(nbd.Error, self.h.pread, maximum + 65536, 0)
+
+
+if __name__ == '__main__':
+ try:
+ # Easier to use libnbd than to try and set up parallel
+ # 'qemu-nbd --list' or 'qemu-io' processes, but not all systems
+ # have libnbd installed.
+ import nbd # type: ignore
+
+ iotests.main(supported_fmts=['qcow2'])
+ except ImportError:
+ iotests.notrun('Python bindings to libnbd are not installed')
diff --git a/tests/qemu-iotests/tests/nbd-commands.out b/tests/qemu-iotests/tests/nbd-commands.out
new file mode 100644
index 00000000000..8d7e9967009
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-commands.out
@@ -0,0 +1,5 @@
+...
+----------------------------------------------------------------------
+Ran 3 tests
+
+OK
--
2.53.0
next prev parent reply other threads:[~2026-08-31 15:26 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 15:25 [PATCH hci-8.0 0/5] nbd/server: accept a large NBD_CMD_CACHE #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` Denis V. Lunev [this message]
2026-09-01 15:51 ` [PATCH hci-8.0 1/5] iotests: add coverage for NBD transmission commands #VSTOR-119829 Andrey Drobyshev
2026-08-31 15:25 ` [PATCH hci-8.0 2/5] nbd/server: accept NBD_CMD_CACHE above the maximum payload size #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 3/5] iotests/nbd-commands: exercise the simple and structured reply modes #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 4/5] iotests/nbd-commands: cover NBD_CMD_BLOCK_STATUS with a payload #VSTOR-119829 Denis V. Lunev
2026-08-31 15:25 ` [PATCH hci-8.0 5/5] iotests/nbd-commands: cover the command flags and sparse replies #VSTOR-119829 Denis V. Lunev
2026-09-01 15:50 ` [QEMU HCI-8.0 PATCH 6/5] VZ: iotests/nbd-commands: do not depend on cluster allocation order #VSTOR-119829 Andrey Drobyshev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831152528.1350583-2-den@openvz.org \
--to=den@openvz.org \
--cc=andrey.drobyshev@virtuozzo.com \
--cc=svt-core@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox