[RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test

Yodel Eldar posted 1 patch 1 month, 1 week ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20251230002604.113632-1-yodel.eldar@yodel.dev
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Zhao Liu <zhao1.liu@intel.com>
.../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
1 file changed, 124 insertions(+)
create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
[RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month, 1 week ago
This introduces a functional test of vhost-user-bridge.

The test runs vhost-user-bridge and launches a guest VM that connects
to the internet through it. The test succeeds if and only if an attempt
to connect to a hard-coded well-known URL succeeds.

Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
---

This patch introduces a functional test of vhost-user-bridge by
automating the testing described in its initial commit, 8e3b0cbb72,
with adjustments like using hubports (formerly the vlan parameter) and
memfd for the memory backend; hugepages are also omitted to avoid
requiring root privileges on the host.

The test configures networking within the guest by invoking udhcpc, then
makes an http request via wget to a well-known URL, example.org, that
has a low risk of requiring https for connections (a limitation of the
the test). An assert on the retcode of wget determines success/failure.

Please let me know if there are objections to the use of wget's retcode
as the test's condition; determining wget success through its output is
straightforward ("remote file exists"), but out of concern of some
unknown failure message (besides "bad address") locking up the test,
I've resorted to checking the retcode instead; perhaps, this violates
some convention?

Also, I figured checking for memfd support on the host was unnecessary
in 2026 for the intended users of the test, but perhaps not?

The guest's kernel contains an integrated initramfs and was built with
buildroot; an attempt to ensure bit-for-bit reproducibility was made by
building it via Containerfile based on a snapshot container image and
use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
is "experimental," so future builds may differ slightly (though the
image in the repo will be left untouched). The image and associated
build files are hosted on my personal account here:
	https://github.com/yodel/vhost-user-bridge-test
and will continue to be well into the future, but if there's some other
preferred location for the asset, please let me know?

Lastly, special thanks to Cédric for inspiring me to write the test in
"<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".

Thanks,
Yodel

 .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py

diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
new file mode 100755
index 0000000000..61afdbceec
--- /dev/null
+++ b/tests/functional/x86_64/test_vhost_user_bridge.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2025 Software Freedom Conservancy, Inc.
+#
+# Author: Yodel Eldar <yodel.eldar@yodel.dev>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+"""
+Test vhost-user-bridge (vubr) functionality:
+
+    1) Run vhost-user-bridge on the host.
+    2) Launch a guest VM:
+        a) Instantiate a unix domain socket to the vubr-created path
+        b) Instantiate a vhost-user net backend on top of that socket
+        c) Expose vhost-user with a virtio-net-pci interface
+        d) Instantiate UDP socket and user-mode net backends
+        e) Hub the UDP and user-mode backends
+    3) Run udhcpc in the guest to auto-configure networking.
+    4) Run wget in the guest and check its retcode to test internet connectivity
+
+The test fails if wget returns 1 and succeeds on 0.
+"""
+
+import os
+import subprocess
+from qemu_test import Asset, QemuSystemTest, which
+from qemu_test import exec_command_and_wait_for_pattern
+from qemu_test import is_readable_executable_file
+from qemu_test import wait_for_console_pattern
+from qemu_test.ports import Ports
+
+class VhostUserBridge(QemuSystemTest):
+
+    ASSET_KERNEL_INITRAMFS = Asset(
+        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
+        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
+
+    def configure_vm(self, ud_socket_path, lport, rport):
+        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
+
+        self.require_accelerator("kvm")
+        self.require_netdev("vhost-user")
+        self.require_netdev("socket")
+        self.require_netdev("hubport")
+        self.require_netdev("user")
+        self.require_device("virtio-net-pci")
+        self.set_machine("q35")
+        self.vm.set_console()
+        self.vm.add_args(
+            "-cpu",      "host",
+            "-accel",    "kvm",
+            "-kernel",   kernel_path,
+            "-append",   "console=ttyS0",
+            "-smp",      "2",
+            "-m",        "128M",
+            "-object",   "memory-backend-memfd,id=mem0,"
+                         "size=128M,share=on,prealloc=on",
+            "-numa",     "node,memdev=mem0",
+            "-chardev", f"socket,id=char0,path={ud_socket_path}",
+            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
+            "-device",   "virtio-net-pci,netdev=vhost0",
+            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
+                        f"localaddr=localhost:{rport}",
+            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
+            "-netdev",   "user,id=user0",
+            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
+        )
+
+    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
+        vubr_args = []
+
+        if (stdbuf_path := which("stdbuf")) is None:
+            self.log.info("Could not find stdbuf: vhost-user-bridge "
+                          "log lines may appear out of order")
+        else:
+            vubr_args += [stdbuf_path, "-o0", "-e0"]
+
+        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
+                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
+
+        return vubr_args
+
+    def test_vhost_user_bridge(self):
+        prompt = "~ # "
+
+        vubr_path = self.build_file("tests", "vhost-user-bridge")
+        if is_readable_executable_file(vubr_path) is None:
+            self.skipTest("Could not find a readable and executable "
+                          "vhost-user-bridge")
+
+        with Ports() as ports:
+            sock_dir = self.socket_dir()
+            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
+            lport, rport = ports.find_free_ports(2)
+
+            self.configure_vm(ud_socket_path, lport, rport)
+
+            vubr_log_path = self.log_file("vhost-user-bridge.log")
+            self.log.info("For the vhost-user-bridge application log,"
+                         f" see: {vubr_log_path}")
+
+            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
+                                                lport, rport)
+
+            with open(vubr_log_path, "w") as vubr_log, \
+                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
+                                  stdout=vubr_log, stderr=subprocess.STDOUT):
+                self.vm.launch()
+
+                wait_for_console_pattern(self, prompt)
+                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
+                exec_command_and_wait_for_pattern(self,
+                    "wget -qT 2 --spider example.org", prompt)
+
+                try:
+                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
+                except AssertionError:
+                    self.log.error("Unable to confirm internet connectivity")
+                    raise
+                finally:
+                    self.vm.shutdown()
+
+if __name__ == '__main__':
+    QemuSystemTest.main()
-- 
2.52.0


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Thomas Huth 1 month ago
On 30/12/2025 01.25, Yodel Eldar wrote:
> This introduces a functional test of vhost-user-bridge.
> 
> The test runs vhost-user-bridge and launches a guest VM that connects
> to the internet through it. The test succeeds if and only if an attempt
> to connect to a hard-coded well-known URL succeeds.
> 
> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> ---
> 
> This patch introduces a functional test of vhost-user-bridge by
> automating the testing described in its initial commit, 8e3b0cbb72,
> with adjustments like using hubports (formerly the vlan parameter) and
> memfd for the memory backend; hugepages are also omitted to avoid
> requiring root privileges on the host.
> 
> The test configures networking within the guest by invoking udhcpc, then
> makes an http request via wget to a well-known URL, example.org, that
> has a low risk of requiring https for connections (a limitation of the
> the test). An assert on the retcode of wget determines success/failure.
> 
> Please let me know if there are objections to the use of wget's retcode
> as the test's condition; determining wget success through its output is
> straightforward ("remote file exists"), but out of concern of some
> unknown failure message (besides "bad address") locking up the test,
> I've resorted to checking the retcode instead; perhaps, this violates
> some convention?
> 
> Also, I figured checking for memfd support on the host was unnecessary
> in 2026 for the intended users of the test, but perhaps not?
> 
> The guest's kernel contains an integrated initramfs and was built with
> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
> building it via Containerfile based on a snapshot container image and
> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
> is "experimental," so future builds may differ slightly (though the
> image in the repo will be left untouched). The image and associated
> build files are hosted on my personal account here:
> 	https://github.com/yodel/vhost-user-bridge-test
> and will continue to be well into the future, but if there's some other
> preferred location for the asset, please let me know?
> 
> Lastly, special thanks to Cédric for inspiring me to write the test in
> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
> 
> Thanks,
> Yodel
> 
>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>   1 file changed, 124 insertions(+)
>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
> 
> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
> new file mode 100755
> index 0000000000..61afdbceec
> --- /dev/null
> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
> @@ -0,0 +1,124 @@
> +#!/usr/bin/env python3
> +#
> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
> +#
> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +"""
> +Test vhost-user-bridge (vubr) functionality:
> +
> +    1) Run vhost-user-bridge on the host.
> +    2) Launch a guest VM:
> +        a) Instantiate a unix domain socket to the vubr-created path
> +        b) Instantiate a vhost-user net backend on top of that socket
> +        c) Expose vhost-user with a virtio-net-pci interface
> +        d) Instantiate UDP socket and user-mode net backends
> +        e) Hub the UDP and user-mode backends
> +    3) Run udhcpc in the guest to auto-configure networking.
> +    4) Run wget in the guest and check its retcode to test internet connectivity
> +
> +The test fails if wget returns 1 and succeeds on 0.
> +"""
> +
> +import os
> +import subprocess
> +from qemu_test import Asset, QemuSystemTest, which
> +from qemu_test import exec_command_and_wait_for_pattern
> +from qemu_test import is_readable_executable_file
> +from qemu_test import wait_for_console_pattern
> +from qemu_test.ports import Ports
> +
> +class VhostUserBridge(QemuSystemTest):
> +
> +    ASSET_KERNEL_INITRAMFS = Asset(
> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
> +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
> +
> +    def configure_vm(self, ud_socket_path, lport, rport):
> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
> +
> +        self.require_accelerator("kvm")
> +        self.require_netdev("vhost-user")
> +        self.require_netdev("socket")
> +        self.require_netdev("hubport")
> +        self.require_netdev("user")
> +        self.require_device("virtio-net-pci")
> +        self.set_machine("q35")
> +        self.vm.set_console()
> +        self.vm.add_args(
> +            "-cpu",      "host",
> +            "-accel",    "kvm",
> +            "-kernel",   kernel_path,
> +            "-append",   "console=ttyS0",
> +            "-smp",      "2",
> +            "-m",        "128M",
> +            "-object",   "memory-backend-memfd,id=mem0,"
> +                         "size=128M,share=on,prealloc=on",
> +            "-numa",     "node,memdev=mem0",
> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
> +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
> +            "-device",   "virtio-net-pci,netdev=vhost0",
> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
> +                        f"localaddr=localhost:{rport}",
> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
> +            "-netdev",   "user,id=user0",
> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
> +        )
> +
> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
> +        vubr_args = []
> +
> +        if (stdbuf_path := which("stdbuf")) is None:
> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
> +                          "log lines may appear out of order")
> +        else:
> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
> +
> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
> +
> +        return vubr_args
> +
> +    def test_vhost_user_bridge(self):
> +        prompt = "~ # "
> +
> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
> +        if is_readable_executable_file(vubr_path) is None:
> +            self.skipTest("Could not find a readable and executable "
> +                          "vhost-user-bridge")
> +
> +        with Ports() as ports:
> +            sock_dir = self.socket_dir()
> +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
> +            lport, rport = ports.find_free_ports(2)
> +
> +            self.configure_vm(ud_socket_path, lport, rport)
> +
> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
> +            self.log.info("For the vhost-user-bridge application log,"
> +                         f" see: {vubr_log_path}")
> +
> +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
> +                                                lport, rport)
> +
> +            with open(vubr_log_path, "w") as vubr_log, \
> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
> +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
> +                self.vm.launch()
> +
> +                wait_for_console_pattern(self, prompt)
> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
> +                exec_command_and_wait_for_pattern(self,
> +                    "wget -qT 2 --spider example.org", prompt)

If you've got python in the guest, you could maybe also do it the other way 
round and host a httpd server in the guest, and download something to the 
host. See check_http_download in tests/functional/qemu_test/linuxkernel.py 
for the helper function. That way you don't depend on any external host.

  Thomas


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
On 06/01/2026 22:52, Thomas Huth wrote:
> On 30/12/2025 01.25, Yodel Eldar wrote:
>> This introduces a functional test of vhost-user-bridge.
>>
>> The test runs vhost-user-bridge and launches a guest VM that connects
>> to the internet through it. The test succeeds if and only if an attempt
>> to connect to a hard-coded well-known URL succeeds.
>>
>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>> ---
>>
>> This patch introduces a functional test of vhost-user-bridge by
>> automating the testing described in its initial commit, 8e3b0cbb72,
>> with adjustments like using hubports (formerly the vlan parameter) and
>> memfd for the memory backend; hugepages are also omitted to avoid
>> requiring root privileges on the host.
>>
>> The test configures networking within the guest by invoking udhcpc, then
>> makes an http request via wget to a well-known URL, example.org, that
>> has a low risk of requiring https for connections (a limitation of the
>> the test). An assert on the retcode of wget determines success/failure.
>>
>> Please let me know if there are objections to the use of wget's retcode
>> as the test's condition; determining wget success through its output is
>> straightforward ("remote file exists"), but out of concern of some
>> unknown failure message (besides "bad address") locking up the test,
>> I've resorted to checking the retcode instead; perhaps, this violates
>> some convention?
>>
>> Also, I figured checking for memfd support on the host was unnecessary
>> in 2026 for the intended users of the test, but perhaps not?
>>
>> The guest's kernel contains an integrated initramfs and was built with
>> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
>> building it via Containerfile based on a snapshot container image and
>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
>> is "experimental," so future builds may differ slightly (though the
>> image in the repo will be left untouched). The image and associated
>> build files are hosted on my personal account here:
>>     https://github.com/yodel/vhost-user-bridge-test
>> and will continue to be well into the future, but if there's some other
>> preferred location for the asset, please let me know?
>>
>> Lastly, special thanks to Cédric for inspiring me to write the test in
>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>
>> Thanks,
>> Yodel
>>
>>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>>   1 file changed, 124 insertions(+)
>>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>
>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/ 
>> tests/functional/x86_64/test_vhost_user_bridge.py
>> new file mode 100755
>> index 0000000000..61afdbceec
>> --- /dev/null
>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>> @@ -0,0 +1,124 @@
>> +#!/usr/bin/env python3
>> +#
>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>> +#
>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>> +#
>> +# SPDX-License-Identifier: GPL-2.0-or-later
>> +"""
>> +Test vhost-user-bridge (vubr) functionality:
>> +
>> +    1) Run vhost-user-bridge on the host.
>> +    2) Launch a guest VM:
>> +        a) Instantiate a unix domain socket to the vubr-created path
>> +        b) Instantiate a vhost-user net backend on top of that socket
>> +        c) Expose vhost-user with a virtio-net-pci interface
>> +        d) Instantiate UDP socket and user-mode net backends
>> +        e) Hub the UDP and user-mode backends
>> +    3) Run udhcpc in the guest to auto-configure networking.
>> +    4) Run wget in the guest and check its retcode to test internet 
>> connectivity
>> +
>> +The test fails if wget returns 1 and succeeds on 0.
>> +"""
>> +
>> +import os
>> +import subprocess
>> +from qemu_test import Asset, QemuSystemTest, which
>> +from qemu_test import exec_command_and_wait_for_pattern
>> +from qemu_test import is_readable_executable_file
>> +from qemu_test import wait_for_console_pattern
>> +from qemu_test.ports import Ports
>> +
>> +class VhostUserBridge(QemuSystemTest):
>> +
>> +    ASSET_KERNEL_INITRAMFS = Asset(
>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/ 
>> heads/main/bzImage",
>> +        
>> "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>> +
>> +    def configure_vm(self, ud_socket_path, lport, rport):
>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>> +
>> +        self.require_accelerator("kvm")
>> +        self.require_netdev("vhost-user")
>> +        self.require_netdev("socket")
>> +        self.require_netdev("hubport")
>> +        self.require_netdev("user")
>> +        self.require_device("virtio-net-pci")
>> +        self.set_machine("q35")
>> +        self.vm.set_console()
>> +        self.vm.add_args(
>> +            "-cpu",      "host",
>> +            "-accel",    "kvm",
>> +            "-kernel",   kernel_path,
>> +            "-append",   "console=ttyS0",
>> +            "-smp",      "2",
>> +            "-m",        "128M",
>> +            "-object",   "memory-backend-memfd,id=mem0,"
>> +                         "size=128M,share=on,prealloc=on",
>> +            "-numa",     "node,memdev=mem0",
>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>> +            "-netdev",   "vhost- 
>> user,id=vhost0,chardev=char0,vhostforce=on",
>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>> +                        f"localaddr=localhost:{rport}",
>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>> +            "-netdev",   "user,id=user0",
>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>> +        )
>> +
>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, 
>> rport):
>> +        vubr_args = []
>> +
>> +        if (stdbuf_path := which("stdbuf")) is None:
>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>> +                          "log lines may appear out of order")
>> +        else:
>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>> +
>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1: 
>> {rport}"]
>> +
>> +        return vubr_args
>> +
>> +    def test_vhost_user_bridge(self):
>> +        prompt = "~ # "
>> +
>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>> +        if is_readable_executable_file(vubr_path) is None:
>> +            self.skipTest("Could not find a readable and executable "
>> +                          "vhost-user-bridge")
>> +
>> +        with Ports() as ports:
>> +            sock_dir = self.socket_dir()
>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr- 
>> test.sock")
>> +            lport, rport = ports.find_free_ports(2)
>> +
>> +            self.configure_vm(ud_socket_path, lport, rport)
>> +
>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>> +            self.log.info("For the vhost-user-bridge application log,"
>> +                         f" see: {vubr_log_path}")
>> +
>> +            vubr_args = self.assemble_vubr_args(vubr_path, 
>> ud_socket_path,
>> +                                                lport, rport)
>> +
>> +            with open(vubr_log_path, "w") as vubr_log, \
>> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
>> +                                  stdout=vubr_log, 
>> stderr=subprocess.STDOUT):
>> +                self.vm.launch()
>> +
>> +                wait_for_console_pattern(self, prompt)
>> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 
>> 1", prompt)
>> +                exec_command_and_wait_for_pattern(self,
>> +                    "wget -qT 2 --spider example.org", prompt)
> 
> If you've got python in the guest, you could maybe also do it the other 
> way round and host a httpd server in the guest, and download something 
> to the host. See check_http_download in tests/functional/qemu_test/ 
> linuxkernel.py for the helper function. That way you don't depend on any 
> external host.
> 
>   Thomas
> 

Thanks for the suggestion and that gem of a helper; I had missed that
module when going over the framework.

I switched to using a ping pattern and checking vhost-user-bridge's log
for the payload, but I don't like how brittle that is, because even
though the log prints are hard-coded on right now, they might not be
later, so having the guest serve a file's a welcome option, although the
guest currently lacks python (and just about everything else).

Thanks,
Yodel

Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Thomas Huth 1 month ago
On 07/01/2026 18.32, Yodel Eldar wrote:
> 
> On 06/01/2026 22:52, Thomas Huth wrote:
>> On 30/12/2025 01.25, Yodel Eldar wrote:
>>> This introduces a functional test of vhost-user-bridge.
>>>
>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>> to the internet through it. The test succeeds if and only if an attempt
>>> to connect to a hard-coded well-known URL succeeds.
>>>
>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>> ---
>>>
>>> This patch introduces a functional test of vhost-user-bridge by
>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>> with adjustments like using hubports (formerly the vlan parameter) and
>>> memfd for the memory backend; hugepages are also omitted to avoid
>>> requiring root privileges on the host.
>>>
>>> The test configures networking within the guest by invoking udhcpc, then
>>> makes an http request via wget to a well-known URL, example.org, that
>>> has a low risk of requiring https for connections (a limitation of the
>>> the test). An assert on the retcode of wget determines success/failure.
>>>
>>> Please let me know if there are objections to the use of wget's retcode
>>> as the test's condition; determining wget success through its output is
>>> straightforward ("remote file exists"), but out of concern of some
>>> unknown failure message (besides "bad address") locking up the test,
>>> I've resorted to checking the retcode instead; perhaps, this violates
>>> some convention?
>>>
>>> Also, I figured checking for memfd support on the host was unnecessary
>>> in 2026 for the intended users of the test, but perhaps not?
>>>
>>> The guest's kernel contains an integrated initramfs and was built with
>>> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
>>> building it via Containerfile based on a snapshot container image and
>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
>>> is "experimental," so future builds may differ slightly (though the
>>> image in the repo will be left untouched). The image and associated
>>> build files are hosted on my personal account here:
>>>     https://github.com/yodel/vhost-user-bridge-test
>>> and will continue to be well into the future, but if there's some other
>>> preferred location for the asset, please let me know?
>>>
>>> Lastly, special thanks to Cédric for inspiring me to write the test in
>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>
>>> Thanks,
>>> Yodel
>>>
>>>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>>>   1 file changed, 124 insertions(+)
>>>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>>
>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/ tests/ 
>>> functional/x86_64/test_vhost_user_bridge.py
>>> new file mode 100755
>>> index 0000000000..61afdbceec
>>> --- /dev/null
>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>> @@ -0,0 +1,124 @@
>>> +#!/usr/bin/env python3
>>> +#
>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>> +#
>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>> +#
>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>> +"""
>>> +Test vhost-user-bridge (vubr) functionality:
>>> +
>>> +    1) Run vhost-user-bridge on the host.
>>> +    2) Launch a guest VM:
>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>> +        b) Instantiate a vhost-user net backend on top of that socket
>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>> +        d) Instantiate UDP socket and user-mode net backends
>>> +        e) Hub the UDP and user-mode backends
>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>> +    4) Run wget in the guest and check its retcode to test internet 
>>> connectivity
>>> +
>>> +The test fails if wget returns 1 and succeeds on 0.
>>> +"""
>>> +
>>> +import os
>>> +import subprocess
>>> +from qemu_test import Asset, QemuSystemTest, which
>>> +from qemu_test import exec_command_and_wait_for_pattern
>>> +from qemu_test import is_readable_executable_file
>>> +from qemu_test import wait_for_console_pattern
>>> +from qemu_test.ports import Ports
>>> +
>>> +class VhostUserBridge(QemuSystemTest):
>>> +
>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/ 
>>> heads/main/bzImage",
>>> + "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>> +
>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>> +
>>> +        self.require_accelerator("kvm")
>>> +        self.require_netdev("vhost-user")
>>> +        self.require_netdev("socket")
>>> +        self.require_netdev("hubport")
>>> +        self.require_netdev("user")
>>> +        self.require_device("virtio-net-pci")
>>> +        self.set_machine("q35")
>>> +        self.vm.set_console()
>>> +        self.vm.add_args(
>>> +            "-cpu",      "host",
>>> +            "-accel",    "kvm",
>>> +            "-kernel",   kernel_path,
>>> +            "-append",   "console=ttyS0",
>>> +            "-smp",      "2",
>>> +            "-m",        "128M",
>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>> +                         "size=128M,share=on,prealloc=on",
>>> +            "-numa",     "node,memdev=mem0",
>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>> +            "-netdev",   "vhost- 
>>> user,id=vhost0,chardev=char0,vhostforce=on",
>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>> +                        f"localaddr=localhost:{rport}",
>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>> +            "-netdev",   "user,id=user0",
>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>> +        )
>>> +
>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
>>> +        vubr_args = []
>>> +
>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>>> +                          "log lines may appear out of order")
>>> +        else:
>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>> +
>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1: 
>>> {rport}"]
>>> +
>>> +        return vubr_args
>>> +
>>> +    def test_vhost_user_bridge(self):
>>> +        prompt = "~ # "
>>> +
>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>> +        if is_readable_executable_file(vubr_path) is None:
>>> +            self.skipTest("Could not find a readable and executable "
>>> +                          "vhost-user-bridge")
>>> +
>>> +        with Ports() as ports:
>>> +            sock_dir = self.socket_dir()
>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr- test.sock")
>>> +            lport, rport = ports.find_free_ports(2)
>>> +
>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>> +
>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>> +            self.log.info("For the vhost-user-bridge application log,"
>>> +                         f" see: {vubr_log_path}")
>>> +
>>> +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
>>> +                                                lport, rport)
>>> +
>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
>>> +                                  stdout=vubr_log, 
>>> stderr=subprocess.STDOUT):
>>> +                self.vm.launch()
>>> +
>>> +                wait_for_console_pattern(self, prompt)
>>> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", 
>>> prompt)
>>> +                exec_command_and_wait_for_pattern(self,
>>> +                    "wget -qT 2 --spider example.org", prompt)
>>
>> If you've got python in the guest, you could maybe also do it the other 
>> way round and host a httpd server in the guest, and download something to 
>> the host. See check_http_download in tests/functional/qemu_test/ 
>> linuxkernel.py for the helper function. That way you don't depend on any 
>> external host.
>>
>>   Thomas
>>
> 
> Thanks for the suggestion and that gem of a helper; I had missed that
> module when going over the framework.
> 
> I switched to using a ping pattern and checking vhost-user-bridge's log
> for the payload, but I don't like how brittle that is, because even
> though the log prints are hard-coded on right now, they might not be
> later, so having the guest serve a file's a welcome option, although the
> guest currently lacks python (and just about everything else).

I never tried, but maybe it's also possible the other way round: Use python 
on the host to simulate a httpd server, and then use wget in the guest to 
download a file from the host?

Another idea, if you happen to have a "tftp" binary in the guest, use that 
to download a file from the built-in tftpd server from QEMU, see e.g. 
do_xmaton_le_test in tests/functional/microblaze/test_s3adsp1800.py as an 
example.

  Thomas


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
On 07/01/2026 23:52, Thomas Huth wrote:
> On 07/01/2026 18.32, Yodel Eldar wrote:
>>
>> On 06/01/2026 22:52, Thomas Huth wrote:
>>> On 30/12/2025 01.25, Yodel Eldar wrote:
>>>> This introduces a functional test of vhost-user-bridge.
>>>>
>>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>>> to the internet through it. The test succeeds if and only if an attempt
>>>> to connect to a hard-coded well-known URL succeeds.
>>>>
>>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>>> ---
>>>>
>>>> This patch introduces a functional test of vhost-user-bridge by
>>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>>> with adjustments like using hubports (formerly the vlan parameter) and
>>>> memfd for the memory backend; hugepages are also omitted to avoid
>>>> requiring root privileges on the host.
>>>>
>>>> The test configures networking within the guest by invoking udhcpc, 
>>>> then
>>>> makes an http request via wget to a well-known URL, example.org, that
>>>> has a low risk of requiring https for connections (a limitation of the
>>>> the test). An assert on the retcode of wget determines success/failure.
>>>>
>>>> Please let me know if there are objections to the use of wget's retcode
>>>> as the test's condition; determining wget success through its output is
>>>> straightforward ("remote file exists"), but out of concern of some
>>>> unknown failure message (besides "bad address") locking up the test,
>>>> I've resorted to checking the retcode instead; perhaps, this violates
>>>> some convention?
>>>>
>>>> Also, I figured checking for memfd support on the host was unnecessary
>>>> in 2026 for the intended users of the test, but perhaps not?
>>>>
>>>> The guest's kernel contains an integrated initramfs and was built with
>>>> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
>>>> building it via Containerfile based on a snapshot container image and
>>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
>>>> is "experimental," so future builds may differ slightly (though the
>>>> image in the repo will be left untouched). The image and associated
>>>> build files are hosted on my personal account here:
>>>>     https://github.com/yodel/vhost-user-bridge-test
>>>> and will continue to be well into the future, but if there's some other
>>>> preferred location for the asset, please let me know?
>>>>
>>>> Lastly, special thanks to Cédric for inspiring me to write the test in
>>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>>
>>>> Thanks,
>>>> Yodel
>>>>
>>>>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++ 
>>>> ++++
>>>>   1 file changed, 124 insertions(+)
>>>>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>>>
>>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/ 
>>>> tests/ functional/x86_64/test_vhost_user_bridge.py
>>>> new file mode 100755
>>>> index 0000000000..61afdbceec
>>>> --- /dev/null
>>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>>> @@ -0,0 +1,124 @@
>>>> +#!/usr/bin/env python3
>>>> +#
>>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>>> +#
>>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>>> +#
>>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>>> +"""
>>>> +Test vhost-user-bridge (vubr) functionality:
>>>> +
>>>> +    1) Run vhost-user-bridge on the host.
>>>> +    2) Launch a guest VM:
>>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>>> +        b) Instantiate a vhost-user net backend on top of that socket
>>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>>> +        d) Instantiate UDP socket and user-mode net backends
>>>> +        e) Hub the UDP and user-mode backends
>>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>>> +    4) Run wget in the guest and check its retcode to test internet 
>>>> connectivity
>>>> +
>>>> +The test fails if wget returns 1 and succeeds on 0.
>>>> +"""
>>>> +
>>>> +import os
>>>> +import subprocess
>>>> +from qemu_test import Asset, QemuSystemTest, which
>>>> +from qemu_test import exec_command_and_wait_for_pattern
>>>> +from qemu_test import is_readable_executable_file
>>>> +from qemu_test import wait_for_console_pattern
>>>> +from qemu_test.ports import Ports
>>>> +
>>>> +class VhostUserBridge(QemuSystemTest):
>>>> +
>>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/ 
>>>> heads/main/bzImage",
>>>> + "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>>> +
>>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>>> +
>>>> +        self.require_accelerator("kvm")
>>>> +        self.require_netdev("vhost-user")
>>>> +        self.require_netdev("socket")
>>>> +        self.require_netdev("hubport")
>>>> +        self.require_netdev("user")
>>>> +        self.require_device("virtio-net-pci")
>>>> +        self.set_machine("q35")
>>>> +        self.vm.set_console()
>>>> +        self.vm.add_args(
>>>> +            "-cpu",      "host",
>>>> +            "-accel",    "kvm",
>>>> +            "-kernel",   kernel_path,
>>>> +            "-append",   "console=ttyS0",
>>>> +            "-smp",      "2",
>>>> +            "-m",        "128M",
>>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>>> +                         "size=128M,share=on,prealloc=on",
>>>> +            "-numa",     "node,memdev=mem0",
>>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>>> +            "-netdev",   "vhost- 
>>>> user,id=vhost0,chardev=char0,vhostforce=on",
>>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>>> +                        f"localaddr=localhost:{rport}",
>>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>>> +            "-netdev",   "user,id=user0",
>>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>>> +        )
>>>> +
>>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, 
>>>> rport):
>>>> +        vubr_args = []
>>>> +
>>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>>>> +                          "log lines may appear out of order")
>>>> +        else:
>>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>>> +
>>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>>> +                      "-l", f"127.0.0.1:{lport}", "-r", 
>>>> f"127.0.0.1: {rport}"]
>>>> +
>>>> +        return vubr_args
>>>> +
>>>> +    def test_vhost_user_bridge(self):
>>>> +        prompt = "~ # "
>>>> +
>>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>>> +        if is_readable_executable_file(vubr_path) is None:
>>>> +            self.skipTest("Could not find a readable and executable "
>>>> +                          "vhost-user-bridge")
>>>> +
>>>> +        with Ports() as ports:
>>>> +            sock_dir = self.socket_dir()
>>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr- 
>>>> test.sock")
>>>> +            lport, rport = ports.find_free_ports(2)
>>>> +
>>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>>> +
>>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>>> +            self.log.info("For the vhost-user-bridge application log,"
>>>> +                         f" see: {vubr_log_path}")
>>>> +
>>>> +            vubr_args = self.assemble_vubr_args(vubr_path, 
>>>> ud_socket_path,
>>>> +                                                lport, rport)
>>>> +
>>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>>> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
>>>> +                                  stdout=vubr_log, 
>>>> stderr=subprocess.STDOUT):
>>>> +                self.vm.launch()
>>>> +
>>>> +                wait_for_console_pattern(self, prompt)
>>>> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 
>>>> 1", prompt)
>>>> +                exec_command_and_wait_for_pattern(self,
>>>> +                    "wget -qT 2 --spider example.org", prompt)
>>>
>>> If you've got python in the guest, you could maybe also do it the 
>>> other way round and host a httpd server in the guest, and download 
>>> something to the host. See check_http_download in tests/functional/ 
>>> qemu_test/ linuxkernel.py for the helper function. That way you don't 
>>> depend on any external host.
>>>
>>>   Thomas
>>>
>>
>> Thanks for the suggestion and that gem of a helper; I had missed that
>> module when going over the framework.
>>
>> I switched to using a ping pattern and checking vhost-user-bridge's log
>> for the payload, but I don't like how brittle that is, because even
>> though the log prints are hard-coded on right now, they might not be
>> later, so having the guest serve a file's a welcome option, although the
>> guest currently lacks python (and just about everything else).
> 
> I never tried, but maybe it's also possible the other way round: Use 
> python on the host to simulate a httpd server, and then use wget in the 
> guest to download a file from the host?
> 

Thread on an HTTPServer with a SimpleHTTPRequestHandler is what I had in
mind, too, for guest downloading from host.

For host downloading from guest, I went with check_http_download that
you mentioned, but I was wondering if we could pull it up into
QemuSystemTest? I've got the test inheriting from LinuxKernelTest now,
but it's not exactly "testing the boot process of a Linux kernel" as in
the class description in its header.

> Another idea, if you happen to have a "tftp" binary in the guest, use 
> that to download a file from the built-in tftpd server from QEMU, see 
> e.g. do_xmaton_le_test in tests/functional/microblaze/test_s3adsp1800.py 
> as an example.
> 

I will definitely check this out tomorrow; thanks for telling me about
it, and for all of your suggestions. Could I add a Suggested-by?

Yodel
>   Thomas
> 
> 


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
On 08/01/2026 00:28, Yodel Eldar wrote:
> On 07/01/2026 23:52, Thomas Huth wrote:
>> On 07/01/2026 18.32, Yodel Eldar wrote:
>>>
>>> On 06/01/2026 22:52, Thomas Huth wrote:
>>>> On 30/12/2025 01.25, Yodel Eldar wrote:
>>>>> This introduces a functional test of vhost-user-bridge.
>>>>>
>>>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>>>> to the internet through it. The test succeeds if and only if an 
>>>>> attempt
>>>>> to connect to a hard-coded well-known URL succeeds.
>>>>>
>>>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>> ---
>>>>>
>>>>> This patch introduces a functional test of vhost-user-bridge by
>>>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>>>> with adjustments like using hubports (formerly the vlan parameter) and
>>>>> memfd for the memory backend; hugepages are also omitted to avoid
>>>>> requiring root privileges on the host.
>>>>>
>>>>> The test configures networking within the guest by invoking udhcpc, 
>>>>> then
>>>>> makes an http request via wget to a well-known URL, example.org, that
>>>>> has a low risk of requiring https for connections (a limitation of the
>>>>> the test). An assert on the retcode of wget determines success/ 
>>>>> failure.
>>>>>
>>>>> Please let me know if there are objections to the use of wget's 
>>>>> retcode
>>>>> as the test's condition; determining wget success through its 
>>>>> output is
>>>>> straightforward ("remote file exists"), but out of concern of some
>>>>> unknown failure message (besides "bad address") locking up the test,
>>>>> I've resorted to checking the retcode instead; perhaps, this violates
>>>>> some convention?
>>>>>
>>>>> Also, I figured checking for memfd support on the host was unnecessary
>>>>> in 2026 for the intended users of the test, but perhaps not?
>>>>>
>>>>> The guest's kernel contains an integrated initramfs and was built with
>>>>> buildroot; an attempt to ensure bit-for-bit reproducibility was 
>>>>> made by
>>>>> building it via Containerfile based on a snapshot container image and
>>>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter 
>>>>> feature
>>>>> is "experimental," so future builds may differ slightly (though the
>>>>> image in the repo will be left untouched). The image and associated
>>>>> build files are hosted on my personal account here:
>>>>>     https://github.com/yodel/vhost-user-bridge-test
>>>>> and will continue to be well into the future, but if there's some 
>>>>> other
>>>>> preferred location for the asset, please let me know?
>>>>>
>>>>> Lastly, special thanks to Cédric for inspiring me to write the test in
>>>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>>>
>>>>> Thanks,
>>>>> Yodel
>>>>>
>>>>>   .../x86_64/test_vhost_user_bridge.py          | 124 +++++++++++++ 
>>>>> + ++++
>>>>>   1 file changed, 124 insertions(+)
>>>>>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>>>>
>>>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/ 
>>>>> tests/ functional/x86_64/test_vhost_user_bridge.py
>>>>> new file mode 100755
>>>>> index 0000000000..61afdbceec
>>>>> --- /dev/null
>>>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>>>> @@ -0,0 +1,124 @@
>>>>> +#!/usr/bin/env python3
>>>>> +#
>>>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>>>> +#
>>>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>> +#
>>>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>>>> +"""
>>>>> +Test vhost-user-bridge (vubr) functionality:
>>>>> +
>>>>> +    1) Run vhost-user-bridge on the host.
>>>>> +    2) Launch a guest VM:
>>>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>>>> +        b) Instantiate a vhost-user net backend on top of that socket
>>>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>>>> +        d) Instantiate UDP socket and user-mode net backends
>>>>> +        e) Hub the UDP and user-mode backends
>>>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>>>> +    4) Run wget in the guest and check its retcode to test 
>>>>> internet connectivity
>>>>> +
>>>>> +The test fails if wget returns 1 and succeeds on 0.
>>>>> +"""
>>>>> +
>>>>> +import os
>>>>> +import subprocess
>>>>> +from qemu_test import Asset, QemuSystemTest, which
>>>>> +from qemu_test import exec_command_and_wait_for_pattern
>>>>> +from qemu_test import is_readable_executable_file
>>>>> +from qemu_test import wait_for_console_pattern
>>>>> +from qemu_test.ports import Ports
>>>>> +
>>>>> +class VhostUserBridge(QemuSystemTest):
>>>>> +
>>>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/ 
>>>>> heads/main/bzImage",
>>>>> + "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>>>> +
>>>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>>>> +
>>>>> +        self.require_accelerator("kvm")
>>>>> +        self.require_netdev("vhost-user")
>>>>> +        self.require_netdev("socket")
>>>>> +        self.require_netdev("hubport")
>>>>> +        self.require_netdev("user")
>>>>> +        self.require_device("virtio-net-pci")
>>>>> +        self.set_machine("q35")
>>>>> +        self.vm.set_console()
>>>>> +        self.vm.add_args(
>>>>> +            "-cpu",      "host",
>>>>> +            "-accel",    "kvm",
>>>>> +            "-kernel",   kernel_path,
>>>>> +            "-append",   "console=ttyS0",
>>>>> +            "-smp",      "2",
>>>>> +            "-m",        "128M",
>>>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>>>> +                         "size=128M,share=on,prealloc=on",
>>>>> +            "-numa",     "node,memdev=mem0",
>>>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>>>> +            "-netdev",   "vhost- 
>>>>> user,id=vhost0,chardev=char0,vhostforce=on",
>>>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>>>> +                        f"localaddr=localhost:{rport}",
>>>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>>>> +            "-netdev",   "user,id=user0",
>>>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>>>> +        )
>>>>> +
>>>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, 
>>>>> rport):
>>>>> +        vubr_args = []
>>>>> +
>>>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>>>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>>>>> +                          "log lines may appear out of order")
>>>>> +        else:
>>>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>>>> +
>>>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>>>> +                      "-l", f"127.0.0.1:{lport}", "-r", 
>>>>> f"127.0.0.1: {rport}"]
>>>>> +
>>>>> +        return vubr_args
>>>>> +
>>>>> +    def test_vhost_user_bridge(self):
>>>>> +        prompt = "~ # "
>>>>> +
>>>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>>>> +        if is_readable_executable_file(vubr_path) is None:
>>>>> +            self.skipTest("Could not find a readable and executable "
>>>>> +                          "vhost-user-bridge")
>>>>> +
>>>>> +        with Ports() as ports:
>>>>> +            sock_dir = self.socket_dir()
>>>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr- 
>>>>> test.sock")
>>>>> +            lport, rport = ports.find_free_ports(2)
>>>>> +
>>>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>>>> +
>>>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>>>> +            self.log.info("For the vhost-user-bridge application 
>>>>> log,"
>>>>> +                         f" see: {vubr_log_path}")
>>>>> +
>>>>> +            vubr_args = self.assemble_vubr_args(vubr_path, 
>>>>> ud_socket_path,
>>>>> +                                                lport, rport)
>>>>> +
>>>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>>>> +                 subprocess.Popen(vubr_args, 
>>>>> stdin=subprocess.DEVNULL,
>>>>> +                                  stdout=vubr_log, 
>>>>> stderr=subprocess.STDOUT):
>>>>> +                self.vm.launch()
>>>>> +
>>>>> +                wait_for_console_pattern(self, prompt)
>>>>> +                exec_command_and_wait_for_pattern(self, "udhcpc - 
>>>>> nt 1", prompt)
>>>>> +                exec_command_and_wait_for_pattern(self,
>>>>> +                    "wget -qT 2 --spider example.org", prompt)
>>>>
>>>> If you've got python in the guest, you could maybe also do it the 
>>>> other way round and host a httpd server in the guest, and download 
>>>> something to the host. See check_http_download in tests/functional/ 
>>>> qemu_test/ linuxkernel.py for the helper function. That way you 
>>>> don't depend on any external host.
>>>>
>>>>   Thomas
>>>>
>>>
>>> Thanks for the suggestion and that gem of a helper; I had missed that
>>> module when going over the framework.
>>>
>>> I switched to using a ping pattern and checking vhost-user-bridge's log
>>> for the payload, but I don't like how brittle that is, because even
>>> though the log prints are hard-coded on right now, they might not be
>>> later, so having the guest serve a file's a welcome option, although the
>>> guest currently lacks python (and just about everything else).
>>
>> I never tried, but maybe it's also possible the other way round: Use 
>> python on the host to simulate a httpd server, and then use wget in 
>> the guest to download a file from the host?
>>
> 
> Thread on an HTTPServer with a SimpleHTTPRequestHandler is what I had in
> mind, too, for guest downloading from host.
> 
> For host downloading from guest, I went with check_http_download that
> you mentioned, but I was wondering if we could pull it up into
> QemuSystemTest? I've got the test inheriting from LinuxKernelTest now,
> but it's not exactly "testing the boot process of a Linux kernel" as in
> the class description in its header.
> 
>> Another idea, if you happen to have a "tftp" binary in the guest, use 
>> that to download a file from the built-in tftpd server from QEMU, see 
>> e.g. do_xmaton_le_test in tests/functional/microblaze/ 
>> test_s3adsp1800.py as an example.
>>

This would have greatly simplified the test, but unfortunately even
though the tftp request can be seen in the vhost-user-bridge log, the
transfer of the file appears to bypass the bridge as it goes to the
guest.

Yodel

> 
> I will definitely check this out tomorrow; thanks for telling me about
> it, and for all of your suggestions. Could I add a Suggested-by?
> 
> Yodel
>>   Thomas
>>
>>
> 
> 


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
On 08/01/2026 13:55, Yodel Eldar wrote:
> On 08/01/2026 00:28, Yodel Eldar wrote:
>> On 07/01/2026 23:52, Thomas Huth wrote:
>>> On 07/01/2026 18.32, Yodel Eldar wrote:
>>>>
>>>> On 06/01/2026 22:52, Thomas Huth wrote:
>>>>> On 30/12/2025 01.25, Yodel Eldar wrote:
>>>>>> This introduces a functional test of vhost-user-bridge.
>>>>>>
>>>>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>>>>> to the internet through it. The test succeeds if and only if an 
>>>>>> attempt
>>>>>> to connect to a hard-coded well-known URL succeeds.
>>>>>>
>>>>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>>> ---
>>>>>>
>>>>>> This patch introduces a functional test of vhost-user-bridge by
>>>>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>>>>> with adjustments like using hubports (formerly the vlan parameter) 
>>>>>> and
>>>>>> memfd for the memory backend; hugepages are also omitted to avoid
>>>>>> requiring root privileges on the host.
>>>>>>
>>>>>> The test configures networking within the guest by invoking 
>>>>>> udhcpc, then
>>>>>> makes an http request via wget to a well-known URL, example.org, that
>>>>>> has a low risk of requiring https for connections (a limitation of 
>>>>>> the
>>>>>> the test). An assert on the retcode of wget determines success/ 
>>>>>> failure.
>>>>>>
>>>>>> Please let me know if there are objections to the use of wget's 
>>>>>> retcode
>>>>>> as the test's condition; determining wget success through its 
>>>>>> output is
>>>>>> straightforward ("remote file exists"), but out of concern of some
>>>>>> unknown failure message (besides "bad address") locking up the test,
>>>>>> I've resorted to checking the retcode instead; perhaps, this violates
>>>>>> some convention?
>>>>>>
>>>>>> Also, I figured checking for memfd support on the host was 
>>>>>> unnecessary
>>>>>> in 2026 for the intended users of the test, but perhaps not?
>>>>>>
>>>>>> The guest's kernel contains an integrated initramfs and was built 
>>>>>> with
>>>>>> buildroot; an attempt to ensure bit-for-bit reproducibility was 
>>>>>> made by
>>>>>> building it via Containerfile based on a snapshot container image and
>>>>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter 
>>>>>> feature
>>>>>> is "experimental," so future builds may differ slightly (though the
>>>>>> image in the repo will be left untouched). The image and associated
>>>>>> build files are hosted on my personal account here:
>>>>>>     https://github.com/yodel/vhost-user-bridge-test
>>>>>> and will continue to be well into the future, but if there's some 
>>>>>> other
>>>>>> preferred location for the asset, please let me know?
>>>>>>
>>>>>> Lastly, special thanks to Cédric for inspiring me to write the 
>>>>>> test in
>>>>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>>>>
>>>>>> Thanks,
>>>>>> Yodel
>>>>>>
>>>>>>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++ 
>>>>>> + + ++++
>>>>>>   1 file changed, 124 insertions(+)
>>>>>>   create mode 100755 tests/functional/x86_64/ 
>>>>>> test_vhost_user_bridge.py
>>>>>>
>>>>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/ 
>>>>>> tests/ functional/x86_64/test_vhost_user_bridge.py
>>>>>> new file mode 100755
>>>>>> index 0000000000..61afdbceec
>>>>>> --- /dev/null
>>>>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>>>>> @@ -0,0 +1,124 @@
>>>>>> +#!/usr/bin/env python3
>>>>>> +#
>>>>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>>>>> +#
>>>>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>>> +#
>>>>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>>>>> +"""
>>>>>> +Test vhost-user-bridge (vubr) functionality:
>>>>>> +
>>>>>> +    1) Run vhost-user-bridge on the host.
>>>>>> +    2) Launch a guest VM:
>>>>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>>>>> +        b) Instantiate a vhost-user net backend on top of that 
>>>>>> socket
>>>>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>>>>> +        d) Instantiate UDP socket and user-mode net backends
>>>>>> +        e) Hub the UDP and user-mode backends
>>>>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>>>>> +    4) Run wget in the guest and check its retcode to test 
>>>>>> internet connectivity
>>>>>> +
>>>>>> +The test fails if wget returns 1 and succeeds on 0.
>>>>>> +"""
>>>>>> +
>>>>>> +import os
>>>>>> +import subprocess
>>>>>> +from qemu_test import Asset, QemuSystemTest, which
>>>>>> +from qemu_test import exec_command_and_wait_for_pattern
>>>>>> +from qemu_test import is_readable_executable_file
>>>>>> +from qemu_test import wait_for_console_pattern
>>>>>> +from qemu_test.ports import Ports
>>>>>> +
>>>>>> +class VhostUserBridge(QemuSystemTest):
>>>>>> +
>>>>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>>>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/ 
>>>>>> refs/ heads/main/bzImage",
>>>>>> + "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>>>>> +
>>>>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>>>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>>>>> +
>>>>>> +        self.require_accelerator("kvm")
>>>>>> +        self.require_netdev("vhost-user")
>>>>>> +        self.require_netdev("socket")
>>>>>> +        self.require_netdev("hubport")
>>>>>> +        self.require_netdev("user")
>>>>>> +        self.require_device("virtio-net-pci")
>>>>>> +        self.set_machine("q35")
>>>>>> +        self.vm.set_console()
>>>>>> +        self.vm.add_args(
>>>>>> +            "-cpu",      "host",
>>>>>> +            "-accel",    "kvm",
>>>>>> +            "-kernel",   kernel_path,
>>>>>> +            "-append",   "console=ttyS0",
>>>>>> +            "-smp",      "2",
>>>>>> +            "-m",        "128M",
>>>>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>>>>> +                         "size=128M,share=on,prealloc=on",
>>>>>> +            "-numa",     "node,memdev=mem0",
>>>>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>>>>> +            "-netdev",   "vhost- 
>>>>>> user,id=vhost0,chardev=char0,vhostforce=on",
>>>>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>>>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>>>>> +                        f"localaddr=localhost:{rport}",
>>>>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>>>>> +            "-netdev",   "user,id=user0",
>>>>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>>>>> +        )
>>>>>> +
>>>>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, 
>>>>>> lport, rport):
>>>>>> +        vubr_args = []
>>>>>> +
>>>>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>>>>> +            self.log.info("Could not find stdbuf: vhost-user- 
>>>>>> bridge "
>>>>>> +                          "log lines may appear out of order")
>>>>>> +        else:
>>>>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>>>>> +
>>>>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>>>>> +                      "-l", f"127.0.0.1:{lport}", "-r", 
>>>>>> f"127.0.0.1: {rport}"]
>>>>>> +
>>>>>> +        return vubr_args
>>>>>> +
>>>>>> +    def test_vhost_user_bridge(self):
>>>>>> +        prompt = "~ # "
>>>>>> +
>>>>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>>>>> +        if is_readable_executable_file(vubr_path) is None:
>>>>>> +            self.skipTest("Could not find a readable and 
>>>>>> executable "
>>>>>> +                          "vhost-user-bridge")
>>>>>> +
>>>>>> +        with Ports() as ports:
>>>>>> +            sock_dir = self.socket_dir()
>>>>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr- 
>>>>>> test.sock")
>>>>>> +            lport, rport = ports.find_free_ports(2)
>>>>>> +
>>>>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>>>>> +
>>>>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>>>>> +            self.log.info("For the vhost-user-bridge application 
>>>>>> log,"
>>>>>> +                         f" see: {vubr_log_path}")
>>>>>> +
>>>>>> +            vubr_args = self.assemble_vubr_args(vubr_path, 
>>>>>> ud_socket_path,
>>>>>> +                                                lport, rport)
>>>>>> +
>>>>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>>>>> +                 subprocess.Popen(vubr_args, 
>>>>>> stdin=subprocess.DEVNULL,
>>>>>> +                                  stdout=vubr_log, 
>>>>>> stderr=subprocess.STDOUT):
>>>>>> +                self.vm.launch()
>>>>>> +
>>>>>> +                wait_for_console_pattern(self, prompt)
>>>>>> +                exec_command_and_wait_for_pattern(self, "udhcpc - 
>>>>>> nt 1", prompt)
>>>>>> +                exec_command_and_wait_for_pattern(self,
>>>>>> +                    "wget -qT 2 --spider example.org", prompt)
>>>>>
>>>>> If you've got python in the guest, you could maybe also do it the 
>>>>> other way round and host a httpd server in the guest, and download 
>>>>> something to the host. See check_http_download in tests/functional/ 
>>>>> qemu_test/ linuxkernel.py for the helper function. That way you 
>>>>> don't depend on any external host.
>>>>>
>>>>>   Thomas
>>>>>
>>>>
>>>> Thanks for the suggestion and that gem of a helper; I had missed that
>>>> module when going over the framework.
>>>>
>>>> I switched to using a ping pattern and checking vhost-user-bridge's log
>>>> for the payload, but I don't like how brittle that is, because even
>>>> though the log prints are hard-coded on right now, they might not be
>>>> later, so having the guest serve a file's a welcome option, although 
>>>> the
>>>> guest currently lacks python (and just about everything else).
>>>
>>> I never tried, but maybe it's also possible the other way round: Use 
>>> python on the host to simulate a httpd server, and then use wget in 
>>> the guest to download a file from the host?
>>>
>>
>> Thread on an HTTPServer with a SimpleHTTPRequestHandler is what I had in
>> mind, too, for guest downloading from host.
>>
>> For host downloading from guest, I went with check_http_download that
>> you mentioned, but I was wondering if we could pull it up into
>> QemuSystemTest? I've got the test inheriting from LinuxKernelTest now,
>> but it's not exactly "testing the boot process of a Linux kernel" as in
>> the class description in its header.
>>
>>> Another idea, if you happen to have a "tftp" binary in the guest, use 
>>> that to download a file from the built-in tftpd server from QEMU, see 
>>> e.g. do_xmaton_le_test in tests/functional/microblaze/ 
>>> test_s3adsp1800.py as an example.
>>>
> 
> This would have greatly simplified the test, but unfortunately even
> though the tftp request can be seen in the vhost-user-bridge log, the
> transfer of the file appears to bypass the bridge as it goes to the
> guest.
> 
> Yodel
> 

Sorry about the confusion, but tftp does go through the bridge after
all: vhost-user-bridge logs TX but not RX, so when I didn't see the data
in the log, I thought that meant the tftp feature of the user-mode
backend achieved transfer via some shortcut.
In fact, when the guest connects to an http server on the host, the
request is also logged and the data is not, like tftp, but I must have
mistaken seeing the data with the http transfer (probably when going the
other direction where host downloads from guest), hence the wrong
shortcut assumption.
Anyway, I've added an RX iov_hexdump to vhost-user-bridge.c and all is
visible now.

Thanks,
Yodel

>>
>> I will definitely check this out tomorrow; thanks for telling me about
>> it, and for all of your suggestions. Could I add a Suggested-by?
>>
>> Yodel
>>>   Thomas
>>>
>>>
>>
>>
> 
> 


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Cédric Le Goater 1 month ago
+ Marc-André

(I don't know who maintains this test)

Thanks,

C.


On 12/30/25 01:25, Yodel Eldar wrote:
> This introduces a functional test of vhost-user-bridge.
> 
> The test runs vhost-user-bridge and launches a guest VM that connects
> to the internet through it. The test succeeds if and only if an attempt
> to connect to a hard-coded well-known URL succeeds.
> 
> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> ---
> 
> This patch introduces a functional test of vhost-user-bridge by
> automating the testing described in its initial commit, 8e3b0cbb72,
> with adjustments like using hubports (formerly the vlan parameter) and
> memfd for the memory backend; hugepages are also omitted to avoid
> requiring root privileges on the host.
> 
> The test configures networking within the guest by invoking udhcpc, then
> makes an http request via wget to a well-known URL, example.org, that
> has a low risk of requiring https for connections (a limitation of the
> the test). An assert on the retcode of wget determines success/failure.
> 
> Please let me know if there are objections to the use of wget's retcode
> as the test's condition; determining wget success through its output is
> straightforward ("remote file exists"), but out of concern of some
> unknown failure message (besides "bad address") locking up the test,
> I've resorted to checking the retcode instead; perhaps, this violates
> some convention?
> 
> Also, I figured checking for memfd support on the host was unnecessary
> in 2026 for the intended users of the test, but perhaps not?
> 
> The guest's kernel contains an integrated initramfs and was built with
> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
> building it via Containerfile based on a snapshot container image and
> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
> is "experimental," so future builds may differ slightly (though the
> image in the repo will be left untouched). The image and associated
> build files are hosted on my personal account here:
> 	https://github.com/yodel/vhost-user-bridge-test
> and will continue to be well into the future, but if there's some other
> preferred location for the asset, please let me know?
> 
> Lastly, special thanks to Cédric for inspiring me to write the test in
> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
> 
> Thanks,
> Yodel
> 
>   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>   1 file changed, 124 insertions(+)
>   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
> 
> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
> new file mode 100755
> index 0000000000..61afdbceec
> --- /dev/null
> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
> @@ -0,0 +1,124 @@
> +#!/usr/bin/env python3
> +#
> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
> +#
> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +"""
> +Test vhost-user-bridge (vubr) functionality:
> +
> +    1) Run vhost-user-bridge on the host.
> +    2) Launch a guest VM:
> +        a) Instantiate a unix domain socket to the vubr-created path
> +        b) Instantiate a vhost-user net backend on top of that socket
> +        c) Expose vhost-user with a virtio-net-pci interface
> +        d) Instantiate UDP socket and user-mode net backends
> +        e) Hub the UDP and user-mode backends
> +    3) Run udhcpc in the guest to auto-configure networking.
> +    4) Run wget in the guest and check its retcode to test internet connectivity
> +
> +The test fails if wget returns 1 and succeeds on 0.
> +"""
> +
> +import os
> +import subprocess
> +from qemu_test import Asset, QemuSystemTest, which
> +from qemu_test import exec_command_and_wait_for_pattern
> +from qemu_test import is_readable_executable_file
> +from qemu_test import wait_for_console_pattern
> +from qemu_test.ports import Ports
> +
> +class VhostUserBridge(QemuSystemTest):
> +
> +    ASSET_KERNEL_INITRAMFS = Asset(
> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
> +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
> +
> +    def configure_vm(self, ud_socket_path, lport, rport):
> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
> +
> +        self.require_accelerator("kvm")
> +        self.require_netdev("vhost-user")
> +        self.require_netdev("socket")
> +        self.require_netdev("hubport")
> +        self.require_netdev("user")
> +        self.require_device("virtio-net-pci")
> +        self.set_machine("q35")
> +        self.vm.set_console()
> +        self.vm.add_args(
> +            "-cpu",      "host",
> +            "-accel",    "kvm",
> +            "-kernel",   kernel_path,
> +            "-append",   "console=ttyS0",
> +            "-smp",      "2",
> +            "-m",        "128M",
> +            "-object",   "memory-backend-memfd,id=mem0,"
> +                         "size=128M,share=on,prealloc=on",
> +            "-numa",     "node,memdev=mem0",
> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
> +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
> +            "-device",   "virtio-net-pci,netdev=vhost0",
> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
> +                        f"localaddr=localhost:{rport}",
> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
> +            "-netdev",   "user,id=user0",
> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
> +        )
> +
> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
> +        vubr_args = []
> +
> +        if (stdbuf_path := which("stdbuf")) is None:
> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
> +                          "log lines may appear out of order")
> +        else:
> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
> +
> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
> +
> +        return vubr_args
> +
> +    def test_vhost_user_bridge(self):
> +        prompt = "~ # "
> +
> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
> +        if is_readable_executable_file(vubr_path) is None:
> +            self.skipTest("Could not find a readable and executable "
> +                          "vhost-user-bridge")
> +
> +        with Ports() as ports:
> +            sock_dir = self.socket_dir()
> +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
> +            lport, rport = ports.find_free_ports(2)
> +
> +            self.configure_vm(ud_socket_path, lport, rport)
> +
> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
> +            self.log.info("For the vhost-user-bridge application log,"
> +                         f" see: {vubr_log_path}")
> +
> +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
> +                                                lport, rport)
> +
> +            with open(vubr_log_path, "w") as vubr_log, \
> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
> +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
> +                self.vm.launch()
> +
> +                wait_for_console_pattern(self, prompt)
> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
> +                exec_command_and_wait_for_pattern(self,
> +                    "wget -qT 2 --spider example.org", prompt)
> +
> +                try:
> +                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
> +                except AssertionError:
> +                    self.log.error("Unable to confirm internet connectivity")
> +                    raise
> +                finally:
> +                    self.vm.shutdown()
> +
> +if __name__ == '__main__':
> +    QemuSystemTest.main()


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Marc-André Lureau 1 month ago
Hi

On Mon, Jan 5, 2026 at 2:36 PM Cédric Le Goater <clg@redhat.com> wrote:
>
> + Marc-André
>
> (I don't know who maintains this test)

oh vhost-user-bridge is unmaintained...

> Thanks,
>
> C.
>
>
> On 12/30/25 01:25, Yodel Eldar wrote:
> > This introduces a functional test of vhost-user-bridge.
> >
> > The test runs vhost-user-bridge and launches a guest VM that connects
> > to the internet through it. The test succeeds if and only if an attempt
> > to connect to a hard-coded well-known URL succeeds.
> >
> > Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> > ---
> >
> > This patch introduces a functional test of vhost-user-bridge by
> > automating the testing described in its initial commit, 8e3b0cbb72,
> > with adjustments like using hubports (formerly the vlan parameter) and
> > memfd for the memory backend; hugepages are also omitted to avoid
> > requiring root privileges on the host.
> >
> > The test configures networking within the guest by invoking udhcpc, then
> > makes an http request via wget to a well-known URL, example.org, that
> > has a low risk of requiring https for connections (a limitation of the
> > the test). An assert on the retcode of wget determines success/failure.
> >
> > Please let me know if there are objections to the use of wget's retcode
> > as the test's condition; determining wget success through its output is
> > straightforward ("remote file exists"), but out of concern of some
> > unknown failure message (besides "bad address") locking up the test,
> > I've resorted to checking the retcode instead; perhaps, this violates
> > some convention?

Imho, the test shouldn't need internet connectivity. It could possibly
send custom crafted IP packets instead. wdyt?


> >
> > Also, I figured checking for memfd support on the host was unnecessary
> > in 2026 for the intended users of the test, but perhaps not?
> >
> > The guest's kernel contains an integrated initramfs and was built with
> > buildroot; an attempt to ensure bit-for-bit reproducibility was made by
> > building it via Containerfile based on a snapshot container image and
> > use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
> > is "experimental," so future builds may differ slightly (though the
> > image in the repo will be left untouched). The image and associated
> > build files are hosted on my personal account here:
> >       https://github.com/yodel/vhost-user-bridge-test
> > and will continue to be well into the future, but if there's some other
> > preferred location for the asset, please let me know?

Do we need a new kernel asset? I like the reproducible from scratch
way, maybe we should try to consolidate other tests around it. That's
just some thoughts.

> >
> > Lastly, special thanks to Cédric for inspiring me to write the test in
> > "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
> >
> > Thanks,
> > Yodel
> >
> >   .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
> >   1 file changed, 124 insertions(+)
> >   create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
> >
> > diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
> > new file mode 100755
> > index 0000000000..61afdbceec
> > --- /dev/null
> > +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
> > @@ -0,0 +1,124 @@
> > +#!/usr/bin/env python3
> > +#
> > +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
> > +#
> > +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
> > +#
> > +# SPDX-License-Identifier: GPL-2.0-or-later
> > +"""
> > +Test vhost-user-bridge (vubr) functionality:
> > +
> > +    1) Run vhost-user-bridge on the host.
> > +    2) Launch a guest VM:
> > +        a) Instantiate a unix domain socket to the vubr-created path
> > +        b) Instantiate a vhost-user net backend on top of that socket
> > +        c) Expose vhost-user with a virtio-net-pci interface
> > +        d) Instantiate UDP socket and user-mode net backends
> > +        e) Hub the UDP and user-mode backends
> > +    3) Run udhcpc in the guest to auto-configure networking.
> > +    4) Run wget in the guest and check its retcode to test internet connectivity
> > +
> > +The test fails if wget returns 1 and succeeds on 0.
> > +"""
> > +
> > +import os
> > +import subprocess
> > +from qemu_test import Asset, QemuSystemTest, which
> > +from qemu_test import exec_command_and_wait_for_pattern
> > +from qemu_test import is_readable_executable_file
> > +from qemu_test import wait_for_console_pattern
> > +from qemu_test.ports import Ports
> > +
> > +class VhostUserBridge(QemuSystemTest):
> > +
> > +    ASSET_KERNEL_INITRAMFS = Asset(
> > +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
> > +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
> > +
> > +    def configure_vm(self, ud_socket_path, lport, rport):
> > +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
> > +
> > +        self.require_accelerator("kvm")
> > +        self.require_netdev("vhost-user")
> > +        self.require_netdev("socket")
> > +        self.require_netdev("hubport")
> > +        self.require_netdev("user")
> > +        self.require_device("virtio-net-pci")
> > +        self.set_machine("q35")
> > +        self.vm.set_console()
> > +        self.vm.add_args(
> > +            "-cpu",      "host",
> > +            "-accel",    "kvm",
> > +            "-kernel",   kernel_path,
> > +            "-append",   "console=ttyS0",
> > +            "-smp",      "2",
> > +            "-m",        "128M",
> > +            "-object",   "memory-backend-memfd,id=mem0,"
> > +                         "size=128M,share=on,prealloc=on",
> > +            "-numa",     "node,memdev=mem0",
> > +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
> > +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
> > +            "-device",   "virtio-net-pci,netdev=vhost0",
> > +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
> > +                        f"localaddr=localhost:{rport}",
> > +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
> > +            "-netdev",   "user,id=user0",
> > +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
> > +        )
> > +
> > +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
> > +        vubr_args = []
> > +
> > +        if (stdbuf_path := which("stdbuf")) is None:
> > +            self.log.info("Could not find stdbuf: vhost-user-bridge "
> > +                          "log lines may appear out of order")
> > +        else:
> > +            vubr_args += [stdbuf_path, "-o0", "-e0"]
> > +
> > +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
> > +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
> > +
> > +        return vubr_args
> > +
> > +    def test_vhost_user_bridge(self):
> > +        prompt = "~ # "
> > +
> > +        vubr_path = self.build_file("tests", "vhost-user-bridge")
> > +        if is_readable_executable_file(vubr_path) is None:
> > +            self.skipTest("Could not find a readable and executable "
> > +                          "vhost-user-bridge")
> > +
> > +        with Ports() as ports:
> > +            sock_dir = self.socket_dir()
> > +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
> > +            lport, rport = ports.find_free_ports(2)
> > +
> > +            self.configure_vm(ud_socket_path, lport, rport)
> > +
> > +            vubr_log_path = self.log_file("vhost-user-bridge.log")
> > +            self.log.info("For the vhost-user-bridge application log,"
> > +                         f" see: {vubr_log_path}")
> > +
> > +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
> > +                                                lport, rport)
> > +
> > +            with open(vubr_log_path, "w") as vubr_log, \
> > +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
> > +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
> > +                self.vm.launch()
> > +
> > +                wait_for_console_pattern(self, prompt)
> > +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
> > +                exec_command_and_wait_for_pattern(self,
> > +                    "wget -qT 2 --spider example.org", prompt)
> > +
> > +                try:
> > +                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
> > +                except AssertionError:
> > +                    self.log.error("Unable to confirm internet connectivity")
> > +                    raise
> > +                finally:
> > +                    self.vm.shutdown()
> > +
> > +if __name__ == '__main__':
> > +    QemuSystemTest.main()
>
>


-- 
Marc-André Lureau
Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
+ MST + Stefano

Hi!

On 05/01/2026 05:21, Marc-André Lureau wrote:
> Hi
> 
> On Mon, Jan 5, 2026 at 2:36 PM Cédric Le Goater <clg@redhat.com> wrote:
>>
>> + Marc-André
>>
>> (I don't know who maintains this test)
> 
> oh vhost-user-bridge is unmaintained...
> 

And, it's not covered by MAINTAINERS.

Is vhost-user-bridge in tests/ instead of contrib/vhost-user-bridge/,
because it preceded libvhost-user and the first contrib/vhost-user-*
(vhost-user-scsi), or is tests/ really the best home for it?

If there's interest, I can submit an RFC moving it and this functional
test and creating a section for it in MAINTAINERS along these lines:

     diff --git a/MAINTAINERS b/MAINTAINERS
     index cca9b57c02..742f865a7e 100644
     --- a/MAINTAINERS
     +++ b/MAINTAINERS
     @@ -2774,0 +2775,5 @@ F: hw/display/vhost-user-*
     +vhost-user-bridge
     +S: Orphan
     +F: contrib/vhost-user-bridge/
     +F: tests/functional/x86_64/test_vhost_user_bridge.py
     +

>> Thanks,
>>
>> C.
>>
>>
>> On 12/30/25 01:25, Yodel Eldar wrote:
>>> This introduces a functional test of vhost-user-bridge.
>>>
>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>> to the internet through it. The test succeeds if and only if an attempt
>>> to connect to a hard-coded well-known URL succeeds.
>>>
>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>> ---
>>>
>>> This patch introduces a functional test of vhost-user-bridge by
>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>> with adjustments like using hubports (formerly the vlan parameter) and
>>> memfd for the memory backend; hugepages are also omitted to avoid
>>> requiring root privileges on the host.
>>>
>>> The test configures networking within the guest by invoking udhcpc, then
>>> makes an http request via wget to a well-known URL, example.org, that
>>> has a low risk of requiring https for connections (a limitation of the
>>> the test). An assert on the retcode of wget determines success/failure.
>>>
>>> Please let me know if there are objections to the use of wget's retcode
>>> as the test's condition; determining wget success through its output is
>>> straightforward ("remote file exists"), but out of concern of some
>>> unknown failure message (besides "bad address") locking up the test,
>>> I've resorted to checking the retcode instead; perhaps, this violates
>>> some convention?
> 
> Imho, the test shouldn't need internet connectivity. It could possibly
> send custom crafted IP packets instead. wdyt?
> 
> 

That certainly alleviates concerns about depending on example.org to be
up! The guest can ping the udhcpc-assigned gateway at 10.0.2.2, so that
the test checks ping's output in the guest and for the guest's MAC
address and the ping payload (with its -p option) in the vubr log. How
does that sound? I'm open to other ideas.

>>>
>>> Also, I figured checking for memfd support on the host was unnecessary
>>> in 2026 for the intended users of the test, but perhaps not?
>>>
>>> The guest's kernel contains an integrated initramfs and was built with
>>> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
>>> building it via Containerfile based on a snapshot container image and
>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
>>> is "experimental," so future builds may differ slightly (though the
>>> image in the repo will be left untouched). The image and associated
>>> build files are hosted on my personal account here:
>>>        https://github.com/yodel/vhost-user-bridge-test
>>> and will continue to be well into the future, but if there's some other
>>> preferred location for the asset, please let me know?
> 
> Do we need a new kernel asset? I like the reproducible from scratch
> way, maybe we should try to consolidate other tests around it. That's
> just some thoughts.
> 

I'm glad you like it! The kernel is significantly stripped of features,
and has a minimally viable initramfs, so it may need some extending to
be usable by the plethora of tests we've got.

If you meant informing the community about a preferred approach to image
generation, I can add documentation in the manual to gently persuade
folks to use a similar approach.

On the other hand, if we want to use the same image for all applicable
tests, it may be beneficial to have the image/build files in-tree,
because I anticipate folks will want to make changes to it as needed. If
there's interest in that, I can prepare an RFC proposing it, though I'd
look to the community for long-term maintenance of it.

Or, was there another option you had closer to mind?

Thanks,
Yodel

>>>
>>> Lastly, special thanks to Cédric for inspiring me to write the test in
>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>
>>> Thanks,
>>> Yodel
>>>
>>>    .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>>>    1 file changed, 124 insertions(+)
>>>    create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>>
>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
>>> new file mode 100755
>>> index 0000000000..61afdbceec
>>> --- /dev/null
>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>> @@ -0,0 +1,124 @@
>>> +#!/usr/bin/env python3
>>> +#
>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>> +#
>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>> +#
>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>> +"""
>>> +Test vhost-user-bridge (vubr) functionality:
>>> +
>>> +    1) Run vhost-user-bridge on the host.
>>> +    2) Launch a guest VM:
>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>> +        b) Instantiate a vhost-user net backend on top of that socket
>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>> +        d) Instantiate UDP socket and user-mode net backends
>>> +        e) Hub the UDP and user-mode backends
>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>> +    4) Run wget in the guest and check its retcode to test internet connectivity
>>> +
>>> +The test fails if wget returns 1 and succeeds on 0.
>>> +"""
>>> +
>>> +import os
>>> +import subprocess
>>> +from qemu_test import Asset, QemuSystemTest, which
>>> +from qemu_test import exec_command_and_wait_for_pattern
>>> +from qemu_test import is_readable_executable_file
>>> +from qemu_test import wait_for_console_pattern
>>> +from qemu_test.ports import Ports
>>> +
>>> +class VhostUserBridge(QemuSystemTest):
>>> +
>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
>>> +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>> +
>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>> +
>>> +        self.require_accelerator("kvm")
>>> +        self.require_netdev("vhost-user")
>>> +        self.require_netdev("socket")
>>> +        self.require_netdev("hubport")
>>> +        self.require_netdev("user")
>>> +        self.require_device("virtio-net-pci")
>>> +        self.set_machine("q35")
>>> +        self.vm.set_console()
>>> +        self.vm.add_args(
>>> +            "-cpu",      "host",
>>> +            "-accel",    "kvm",
>>> +            "-kernel",   kernel_path,
>>> +            "-append",   "console=ttyS0",
>>> +            "-smp",      "2",
>>> +            "-m",        "128M",
>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>> +                         "size=128M,share=on,prealloc=on",
>>> +            "-numa",     "node,memdev=mem0",
>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>> +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>> +                        f"localaddr=localhost:{rport}",
>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>> +            "-netdev",   "user,id=user0",
>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>> +        )
>>> +
>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
>>> +        vubr_args = []
>>> +
>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>>> +                          "log lines may appear out of order")
>>> +        else:
>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>> +
>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
>>> +
>>> +        return vubr_args
>>> +
>>> +    def test_vhost_user_bridge(self):
>>> +        prompt = "~ # "
>>> +
>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>> +        if is_readable_executable_file(vubr_path) is None:
>>> +            self.skipTest("Could not find a readable and executable "
>>> +                          "vhost-user-bridge")
>>> +
>>> +        with Ports() as ports:
>>> +            sock_dir = self.socket_dir()
>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
>>> +            lport, rport = ports.find_free_ports(2)
>>> +
>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>> +
>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>> +            self.log.info("For the vhost-user-bridge application log,"
>>> +                         f" see: {vubr_log_path}")
>>> +
>>> +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
>>> +                                                lport, rport)
>>> +
>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
>>> +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
>>> +                self.vm.launch()
>>> +
>>> +                wait_for_console_pattern(self, prompt)
>>> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
>>> +                exec_command_and_wait_for_pattern(self,
>>> +                    "wget -qT 2 --spider example.org", prompt)
>>> +
>>> +                try:
>>> +                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
>>> +                except AssertionError:
>>> +                    self.log.error("Unable to confirm internet connectivity")
>>> +                    raise
>>> +                finally:
>>> +                    self.vm.shutdown()
>>> +
>>> +if __name__ == '__main__':
>>> +    QemuSystemTest.main()
>>
>>
> 
> 


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Michael S. Tsirkin 1 month ago
On Mon, Jan 05, 2026 at 02:28:53PM -0600, Yodel Eldar wrote:
> + MST + Stefano
> 
> Hi!
> 
> On 05/01/2026 05:21, Marc-André Lureau wrote:
> > Hi
> > 
> > On Mon, Jan 5, 2026 at 2:36 PM Cédric Le Goater <clg@redhat.com> wrote:
> > > 
> > > + Marc-André
> > > 
> > > (I don't know who maintains this test)
> > 
> > oh vhost-user-bridge is unmaintained...
> > 
> 
> And, it's not covered by MAINTAINERS.
> 
> Is vhost-user-bridge in tests/ instead of contrib/vhost-user-bridge/,
> because it preceded libvhost-user and the first contrib/vhost-user-*
> (vhost-user-scsi), or is tests/ really the best home for it?

I don't remember really.


> If there's interest, I can submit an RFC moving it and this functional
> test and creating a section for it in MAINTAINERS along these lines:
> 
>     diff --git a/MAINTAINERS b/MAINTAINERS
>     index cca9b57c02..742f865a7e 100644
>     --- a/MAINTAINERS
>     +++ b/MAINTAINERS
>     @@ -2774,0 +2775,5 @@ F: hw/display/vhost-user-*
>     +vhost-user-bridge
>     +S: Orphan
>     +F: contrib/vhost-user-bridge/
>     +F: tests/functional/x86_64/test_vhost_user_bridge.py
>     +

I'd rather add it with rest of vhost-user not mark it orphan.

> > > Thanks,
> > > 
> > > C.
> > > 
> > > 
> > > On 12/30/25 01:25, Yodel Eldar wrote:
> > > > This introduces a functional test of vhost-user-bridge.
> > > > 
> > > > The test runs vhost-user-bridge and launches a guest VM that connects
> > > > to the internet through it. The test succeeds if and only if an attempt
> > > > to connect to a hard-coded well-known URL succeeds.
> > > > 
> > > > Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
> > > > ---
> > > > 
> > > > This patch introduces a functional test of vhost-user-bridge by
> > > > automating the testing described in its initial commit, 8e3b0cbb72,
> > > > with adjustments like using hubports (formerly the vlan parameter) and
> > > > memfd for the memory backend; hugepages are also omitted to avoid
> > > > requiring root privileges on the host.
> > > > 
> > > > The test configures networking within the guest by invoking udhcpc, then
> > > > makes an http request via wget to a well-known URL, example.org, that
> > > > has a low risk of requiring https for connections (a limitation of the
> > > > the test). An assert on the retcode of wget determines success/failure.
> > > > 
> > > > Please let me know if there are objections to the use of wget's retcode
> > > > as the test's condition; determining wget success through its output is
> > > > straightforward ("remote file exists"), but out of concern of some
> > > > unknown failure message (besides "bad address") locking up the test,
> > > > I've resorted to checking the retcode instead; perhaps, this violates
> > > > some convention?
> > 
> > Imho, the test shouldn't need internet connectivity. It could possibly
> > send custom crafted IP packets instead. wdyt?
> > 
> > 
> 
> That certainly alleviates concerns about depending on example.org to be
> up! The guest can ping the udhcpc-assigned gateway at 10.0.2.2, so that
> the test checks ping's output in the guest and for the guest's MAC
> address and the ping payload (with its -p option) in the vubr log. How
> does that sound? I'm open to other ideas.


The whole idea with vhost user bridge is to use slirp and not
depend on anything expernal.

> > > > 
> > > > Also, I figured checking for memfd support on the host was unnecessary
> > > > in 2026 for the intended users of the test, but perhaps not?
> > > > 
> > > > The guest's kernel contains an integrated initramfs and was built with
> > > > buildroot; an attempt to ensure bit-for-bit reproducibility was made by
> > > > building it via Containerfile based on a snapshot container image and
> > > > use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
> > > > is "experimental," so future builds may differ slightly (though the
> > > > image in the repo will be left untouched). The image and associated
> > > > build files are hosted on my personal account here:
> > > >        https://github.com/yodel/vhost-user-bridge-test
> > > > and will continue to be well into the future, but if there's some other
> > > > preferred location for the asset, please let me know?
> > 
> > Do we need a new kernel asset? I like the reproducible from scratch
> > way, maybe we should try to consolidate other tests around it. That's
> > just some thoughts.
> > 
> 
> I'm glad you like it! The kernel is significantly stripped of features,
> and has a minimally viable initramfs, so it may need some extending to
> be usable by the plethora of tests we've got.
> 
> If you meant informing the community about a preferred approach to image
> generation, I can add documentation in the manual to gently persuade
> folks to use a similar approach.
> 
> On the other hand, if we want to use the same image for all applicable
> tests, it may be beneficial to have the image/build files in-tree,
> because I anticipate folks will want to make changes to it as needed. If
> there's interest in that, I can prepare an RFC proposing it, though I'd
> look to the community for long-term maintenance of it.
> 
> Or, was there another option you had closer to mind?
> 
> Thanks,
> Yodel
> 
> > > > 
> > > > Lastly, special thanks to Cédric for inspiring me to write the test in
> > > > "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
> > > > 
> > > > Thanks,
> > > > Yodel
> > > > 
> > > >    .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
> > > >    1 file changed, 124 insertions(+)
> > > >    create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
> > > > 
> > > > diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
> > > > new file mode 100755
> > > > index 0000000000..61afdbceec
> > > > --- /dev/null
> > > > +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
> > > > @@ -0,0 +1,124 @@
> > > > +#!/usr/bin/env python3
> > > > +#
> > > > +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
> > > > +#
> > > > +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
> > > > +#
> > > > +# SPDX-License-Identifier: GPL-2.0-or-later
> > > > +"""
> > > > +Test vhost-user-bridge (vubr) functionality:
> > > > +
> > > > +    1) Run vhost-user-bridge on the host.
> > > > +    2) Launch a guest VM:
> > > > +        a) Instantiate a unix domain socket to the vubr-created path
> > > > +        b) Instantiate a vhost-user net backend on top of that socket
> > > > +        c) Expose vhost-user with a virtio-net-pci interface
> > > > +        d) Instantiate UDP socket and user-mode net backends
> > > > +        e) Hub the UDP and user-mode backends
> > > > +    3) Run udhcpc in the guest to auto-configure networking.
> > > > +    4) Run wget in the guest and check its retcode to test internet connectivity
> > > > +
> > > > +The test fails if wget returns 1 and succeeds on 0.
> > > > +"""
> > > > +
> > > > +import os
> > > > +import subprocess
> > > > +from qemu_test import Asset, QemuSystemTest, which
> > > > +from qemu_test import exec_command_and_wait_for_pattern
> > > > +from qemu_test import is_readable_executable_file
> > > > +from qemu_test import wait_for_console_pattern
> > > > +from qemu_test.ports import Ports
> > > > +
> > > > +class VhostUserBridge(QemuSystemTest):
> > > > +
> > > > +    ASSET_KERNEL_INITRAMFS = Asset(
> > > > +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
> > > > +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
> > > > +
> > > > +    def configure_vm(self, ud_socket_path, lport, rport):
> > > > +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
> > > > +
> > > > +        self.require_accelerator("kvm")
> > > > +        self.require_netdev("vhost-user")
> > > > +        self.require_netdev("socket")
> > > > +        self.require_netdev("hubport")
> > > > +        self.require_netdev("user")
> > > > +        self.require_device("virtio-net-pci")
> > > > +        self.set_machine("q35")
> > > > +        self.vm.set_console()
> > > > +        self.vm.add_args(
> > > > +            "-cpu",      "host",
> > > > +            "-accel",    "kvm",
> > > > +            "-kernel",   kernel_path,
> > > > +            "-append",   "console=ttyS0",
> > > > +            "-smp",      "2",
> > > > +            "-m",        "128M",
> > > > +            "-object",   "memory-backend-memfd,id=mem0,"
> > > > +                         "size=128M,share=on,prealloc=on",
> > > > +            "-numa",     "node,memdev=mem0",
> > > > +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
> > > > +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
> > > > +            "-device",   "virtio-net-pci,netdev=vhost0",
> > > > +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
> > > > +                        f"localaddr=localhost:{rport}",
> > > > +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
> > > > +            "-netdev",   "user,id=user0",
> > > > +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
> > > > +        )
> > > > +
> > > > +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
> > > > +        vubr_args = []
> > > > +
> > > > +        if (stdbuf_path := which("stdbuf")) is None:
> > > > +            self.log.info("Could not find stdbuf: vhost-user-bridge "
> > > > +                          "log lines may appear out of order")
> > > > +        else:
> > > > +            vubr_args += [stdbuf_path, "-o0", "-e0"]
> > > > +
> > > > +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
> > > > +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
> > > > +
> > > > +        return vubr_args
> > > > +
> > > > +    def test_vhost_user_bridge(self):
> > > > +        prompt = "~ # "
> > > > +
> > > > +        vubr_path = self.build_file("tests", "vhost-user-bridge")
> > > > +        if is_readable_executable_file(vubr_path) is None:
> > > > +            self.skipTest("Could not find a readable and executable "
> > > > +                          "vhost-user-bridge")
> > > > +
> > > > +        with Ports() as ports:
> > > > +            sock_dir = self.socket_dir()
> > > > +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
> > > > +            lport, rport = ports.find_free_ports(2)
> > > > +
> > > > +            self.configure_vm(ud_socket_path, lport, rport)
> > > > +
> > > > +            vubr_log_path = self.log_file("vhost-user-bridge.log")
> > > > +            self.log.info("For the vhost-user-bridge application log,"
> > > > +                         f" see: {vubr_log_path}")
> > > > +
> > > > +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
> > > > +                                                lport, rport)
> > > > +
> > > > +            with open(vubr_log_path, "w") as vubr_log, \
> > > > +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
> > > > +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
> > > > +                self.vm.launch()
> > > > +
> > > > +                wait_for_console_pattern(self, prompt)
> > > > +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
> > > > +                exec_command_and_wait_for_pattern(self,
> > > > +                    "wget -qT 2 --spider example.org", prompt)
> > > > +
> > > > +                try:
> > > > +                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
> > > > +                except AssertionError:
> > > > +                    self.log.error("Unable to confirm internet connectivity")
> > > > +                    raise
> > > > +                finally:
> > > > +                    self.vm.shutdown()
> > > > +
> > > > +if __name__ == '__main__':
> > > > +    QemuSystemTest.main()
> > > 
> > > 
> > 
> > 


Re: [RFC PATCH] tests/functional/x86_64: Add vhost-user-bridge test
Posted by Yodel Eldar 1 month ago
On 05/01/2026 14:53, Michael S. Tsirkin wrote:
> On Mon, Jan 05, 2026 at 02:28:53PM -0600, Yodel Eldar wrote:
>> + MST + Stefano
>>
>> Hi!
>>
>> On 05/01/2026 05:21, Marc-André Lureau wrote:
>>> Hi
>>>
>>> On Mon, Jan 5, 2026 at 2:36 PM Cédric Le Goater <clg@redhat.com> wrote:
>>>>
>>>> + Marc-André
>>>>
>>>> (I don't know who maintains this test)
>>>
>>> oh vhost-user-bridge is unmaintained...
>>>
>>
>> And, it's not covered by MAINTAINERS.
>>
>> Is vhost-user-bridge in tests/ instead of contrib/vhost-user-bridge/,
>> because it preceded libvhost-user and the first contrib/vhost-user-*
>> (vhost-user-scsi), or is tests/ really the best home for it?
> 
> I don't remember really.
> 
> 

It's served the community for quite a while! Uniting it with its younger
cousins seems correct, so I'll propose the move in the forthcoming RFC.

>> If there's interest, I can submit an RFC moving it and this functional
>> test and creating a section for it in MAINTAINERS along these lines:
>>
>>      diff --git a/MAINTAINERS b/MAINTAINERS
>>      index cca9b57c02..742f865a7e 100644
>>      --- a/MAINTAINERS
>>      +++ b/MAINTAINERS
>>      @@ -2774,0 +2775,5 @@ F: hw/display/vhost-user-*
>>      +vhost-user-bridge
>>      +S: Orphan
>>      +F: contrib/vhost-user-bridge/
>>      +F: tests/functional/x86_64/test_vhost_user_bridge.py
>>      +
> 
> I'd rather add it with rest of vhost-user not mark it orphan.
> 

Thanks for catching this early! I wrongly assumed a move to contrib/
would necessitate the creation of a dedicated section.

If we move vhost-user-bridge to contrib/vhost-user-bridge/, then it'll
automatically be covered by MAINTAINERS; otherwise, I could revise the
RFC to add it with its current location and this functional test as
individual files under the vhost-user section instead.

>>>> Thanks,
>>>>
>>>> C.
>>>>
>>>>
>>>> On 12/30/25 01:25, Yodel Eldar wrote:
>>>>> This introduces a functional test of vhost-user-bridge.
>>>>>
>>>>> The test runs vhost-user-bridge and launches a guest VM that connects
>>>>> to the internet through it. The test succeeds if and only if an attempt
>>>>> to connect to a hard-coded well-known URL succeeds.
>>>>>
>>>>> Signed-off-by: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>> ---
>>>>>
>>>>> This patch introduces a functional test of vhost-user-bridge by
>>>>> automating the testing described in its initial commit, 8e3b0cbb72,
>>>>> with adjustments like using hubports (formerly the vlan parameter) and
>>>>> memfd for the memory backend; hugepages are also omitted to avoid
>>>>> requiring root privileges on the host.
>>>>>
>>>>> The test configures networking within the guest by invoking udhcpc, then
>>>>> makes an http request via wget to a well-known URL, example.org, that
>>>>> has a low risk of requiring https for connections (a limitation of the
>>>>> the test). An assert on the retcode of wget determines success/failure.
>>>>>
>>>>> Please let me know if there are objections to the use of wget's retcode
>>>>> as the test's condition; determining wget success through its output is
>>>>> straightforward ("remote file exists"), but out of concern of some
>>>>> unknown failure message (besides "bad address") locking up the test,
>>>>> I've resorted to checking the retcode instead; perhaps, this violates
>>>>> some convention?
>>>
>>> Imho, the test shouldn't need internet connectivity. It could possibly
>>> send custom crafted IP packets instead. wdyt?
>>>
>>>
>>
>> That certainly alleviates concerns about depending on example.org to be
>> up! The guest can ping the udhcpc-assigned gateway at 10.0.2.2, so that
>> the test checks ping's output in the guest and for the guest's MAC
>> address and the ping payload (with its -p option) in the vubr log. How
>> does that sound? I'm open to other ideas.
> 
> 
> The whole idea with vhost user bridge is to use slirp and not
> depend on anything expernal.
> 

Thanks for the insight! I figured connecting to example.org would
provide a more comprehensive log but was concerned about introducing
such an external dependency to the test; I'm glad we could forgo it by
pinging the slirp-assigned gateway obtained by udhcpc instead.

>>>>>
>>>>> Also, I figured checking for memfd support on the host was unnecessary
>>>>> in 2026 for the intended users of the test, but perhaps not?
>>>>>
>>>>> The guest's kernel contains an integrated initramfs and was built with
>>>>> buildroot; an attempt to ensure bit-for-bit reproducibility was made by
>>>>> building it via Containerfile based on a snapshot container image and
>>>>> use of the BR2_REPRODUCIBLE option of buildroot, but the latter feature
>>>>> is "experimental," so future builds may differ slightly (though the
>>>>> image in the repo will be left untouched). The image and associated
>>>>> build files are hosted on my personal account here:
>>>>>         https://github.com/yodel/vhost-user-bridge-test
>>>>> and will continue to be well into the future, but if there's some other
>>>>> preferred location for the asset, please let me know?
>>>
>>> Do we need a new kernel asset? I like the reproducible from scratch
>>> way, maybe we should try to consolidate other tests around it. That's
>>> just some thoughts.
>>>
>>
>> I'm glad you like it! The kernel is significantly stripped of features,
>> and has a minimally viable initramfs, so it may need some extending to
>> be usable by the plethora of tests we've got.
>>
>> If you meant informing the community about a preferred approach to image
>> generation, I can add documentation in the manual to gently persuade
>> folks to use a similar approach.
>>
>> On the other hand, if we want to use the same image for all applicable
>> tests, it may be beneficial to have the image/build files in-tree,
>> because I anticipate folks will want to make changes to it as needed. If
>> there's interest in that, I can prepare an RFC proposing it, though I'd
>> look to the community for long-term maintenance of it.
>>
>> Or, was there another option you had closer to mind?
>>
>> Thanks,
>> Yodel
>>
>>>>>
>>>>> Lastly, special thanks to Cédric for inspiring me to write the test in
>>>>> "<11454796-30d2-4a57-85a5-d42ff0dce2e6@redhat.com>".
>>>>>
>>>>> Thanks,
>>>>> Yodel
>>>>>
>>>>>     .../x86_64/test_vhost_user_bridge.py          | 124 ++++++++++++++++++
>>>>>     1 file changed, 124 insertions(+)
>>>>>     create mode 100755 tests/functional/x86_64/test_vhost_user_bridge.py
>>>>>
>>>>> diff --git a/tests/functional/x86_64/test_vhost_user_bridge.py b/tests/functional/x86_64/test_vhost_user_bridge.py
>>>>> new file mode 100755
>>>>> index 0000000000..61afdbceec
>>>>> --- /dev/null
>>>>> +++ b/tests/functional/x86_64/test_vhost_user_bridge.py
>>>>> @@ -0,0 +1,124 @@
>>>>> +#!/usr/bin/env python3
>>>>> +#
>>>>> +# Copyright (c) 2025 Software Freedom Conservancy, Inc.
>>>>> +#
>>>>> +# Author: Yodel Eldar <yodel.eldar@yodel.dev>
>>>>> +#
>>>>> +# SPDX-License-Identifier: GPL-2.0-or-later
>>>>> +"""
>>>>> +Test vhost-user-bridge (vubr) functionality:
>>>>> +
>>>>> +    1) Run vhost-user-bridge on the host.
>>>>> +    2) Launch a guest VM:
>>>>> +        a) Instantiate a unix domain socket to the vubr-created path
>>>>> +        b) Instantiate a vhost-user net backend on top of that socket
>>>>> +        c) Expose vhost-user with a virtio-net-pci interface
>>>>> +        d) Instantiate UDP socket and user-mode net backends
>>>>> +        e) Hub the UDP and user-mode backends
>>>>> +    3) Run udhcpc in the guest to auto-configure networking.
>>>>> +    4) Run wget in the guest and check its retcode to test internet connectivity
>>>>> +
>>>>> +The test fails if wget returns 1 and succeeds on 0.
>>>>> +"""
>>>>> +
>>>>> +import os
>>>>> +import subprocess
>>>>> +from qemu_test import Asset, QemuSystemTest, which
>>>>> +from qemu_test import exec_command_and_wait_for_pattern
>>>>> +from qemu_test import is_readable_executable_file
>>>>> +from qemu_test import wait_for_console_pattern
>>>>> +from qemu_test.ports import Ports
>>>>> +
>>>>> +class VhostUserBridge(QemuSystemTest):
>>>>> +
>>>>> +    ASSET_KERNEL_INITRAMFS = Asset(
>>>>> +        "https://github.com/yodel/vhost-user-bridge-test/raw/refs/heads/main/bzImage",
>>>>> +        "3790bf35e4ddfe062425bca45e923df5a5ee4de44e456d6b00cf47f04991d549")
>>>>> +
>>>>> +    def configure_vm(self, ud_socket_path, lport, rport):
>>>>> +        kernel_path = self.ASSET_KERNEL_INITRAMFS.fetch()
>>>>> +
>>>>> +        self.require_accelerator("kvm")
>>>>> +        self.require_netdev("vhost-user")
>>>>> +        self.require_netdev("socket")
>>>>> +        self.require_netdev("hubport")
>>>>> +        self.require_netdev("user")
>>>>> +        self.require_device("virtio-net-pci")
>>>>> +        self.set_machine("q35")
>>>>> +        self.vm.set_console()
>>>>> +        self.vm.add_args(
>>>>> +            "-cpu",      "host",
>>>>> +            "-accel",    "kvm",
>>>>> +            "-kernel",   kernel_path,
>>>>> +            "-append",   "console=ttyS0",
>>>>> +            "-smp",      "2",
>>>>> +            "-m",        "128M",
>>>>> +            "-object",   "memory-backend-memfd,id=mem0,"
>>>>> +                         "size=128M,share=on,prealloc=on",
>>>>> +            "-numa",     "node,memdev=mem0",
>>>>> +            "-chardev", f"socket,id=char0,path={ud_socket_path}",
>>>>> +            "-netdev",   "vhost-user,id=vhost0,chardev=char0,vhostforce=on",
>>>>> +            "-device",   "virtio-net-pci,netdev=vhost0",
>>>>> +            "-netdev",  f"socket,id=udp0,udp=localhost:{lport},"
>>>>> +                        f"localaddr=localhost:{rport}",
>>>>> +            "-netdev",   "hubport,id=hub0,hubid=0,netdev=udp0",
>>>>> +            "-netdev",   "user,id=user0",
>>>>> +            "-netdev",   "hubport,id=hub1,hubid=0,netdev=user0"
>>>>> +        )
>>>>> +
>>>>> +    def assemble_vubr_args(self, vubr_path, ud_socket_path, lport, rport):
>>>>> +        vubr_args = []
>>>>> +
>>>>> +        if (stdbuf_path := which("stdbuf")) is None:
>>>>> +            self.log.info("Could not find stdbuf: vhost-user-bridge "
>>>>> +                          "log lines may appear out of order")
>>>>> +        else:
>>>>> +            vubr_args += [stdbuf_path, "-o0", "-e0"]
>>>>> +
>>>>> +        vubr_args += [vubr_path, "-u", f"{ud_socket_path}",
>>>>> +                      "-l", f"127.0.0.1:{lport}", "-r", f"127.0.0.1:{rport}"]
>>>>> +
>>>>> +        return vubr_args
>>>>> +
>>>>> +    def test_vhost_user_bridge(self):
>>>>> +        prompt = "~ # "
>>>>> +
>>>>> +        vubr_path = self.build_file("tests", "vhost-user-bridge")
>>>>> +        if is_readable_executable_file(vubr_path) is None:
>>>>> +            self.skipTest("Could not find a readable and executable "
>>>>> +                          "vhost-user-bridge")
>>>>> +
>>>>> +        with Ports() as ports:
>>>>> +            sock_dir = self.socket_dir()
>>>>> +            ud_socket_path = os.path.join(sock_dir.name, "vubr-test.sock")
>>>>> +            lport, rport = ports.find_free_ports(2)
>>>>> +
>>>>> +            self.configure_vm(ud_socket_path, lport, rport)
>>>>> +
>>>>> +            vubr_log_path = self.log_file("vhost-user-bridge.log")
>>>>> +            self.log.info("For the vhost-user-bridge application log,"
>>>>> +                         f" see: {vubr_log_path}")
>>>>> +
>>>>> +            vubr_args = self.assemble_vubr_args(vubr_path, ud_socket_path,
>>>>> +                                                lport, rport)
>>>>> +
>>>>> +            with open(vubr_log_path, "w") as vubr_log, \
>>>>> +                 subprocess.Popen(vubr_args, stdin=subprocess.DEVNULL,
>>>>> +                                  stdout=vubr_log, stderr=subprocess.STDOUT):
>>>>> +                self.vm.launch()
>>>>> +
>>>>> +                wait_for_console_pattern(self, prompt)
>>>>> +                exec_command_and_wait_for_pattern(self, "udhcpc -nt 1", prompt)
>>>>> +                exec_command_and_wait_for_pattern(self,
>>>>> +                    "wget -qT 2 --spider example.org", prompt)
>>>>> +
>>>>> +                try:
>>>>> +                    exec_command_and_wait_for_pattern(self, "echo $?", "0", "1")
>>>>> +                except AssertionError:
>>>>> +                    self.log.error("Unable to confirm internet connectivity")
>>>>> +                    raise
>>>>> +                finally:
>>>>> +                    self.vm.shutdown()
>>>>> +
>>>>> +if __name__ == '__main__':
>>>>> +    QemuSystemTest.main()
>>>>
>>>>
>>>
>>>
> 
>