This adds support for custom command line arguments for the passt backend, similar to qemu:commandline. The feature allows passing additional arguments to the passt process for development and testing purposes.
The implementation: - Adds a passt XML namespace for custom arguments - Properly taints the domain when custom args are used - Includes comprehensive test coverage - Adds documentation for the new feature Usage example: <interface type='user'> <backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'> <passt:commandline> <passt:arg value='--debug'/> <passt:arg value='--verbose'/> </passt:commandline> </backend> </interface> Signed-off-by: Enrique Llorente <ellor...@redhat.com> --- v3: - Fix all test problems - Refactor domain_conf.c to use libvirt xml constructs to have proper indent - Rework documentation and make it more concise - Add domainpassttest.c to check that arguments are passed to passt docs/formatdomain.rst | 38 ++++ src/conf/domain_conf.c | 61 ++++++- src/conf/domain_conf.h | 6 + src/conf/schemas/domaincommon.rng | 15 ++ src/qemu/qemu_passt.c | 9 + tests/meson.build | 1 + tests/qemupassttest.c | 162 ++++++++++++++++++ ...-user-passt-custom-args.x86_64-latest.args | 35 ++++ ...t-user-passt-custom-args.x86_64-latest.xml | 67 ++++++++ .../net-user-passt-custom-args.xml | 64 +++++++ tests/qemuxmlconftest.c | 1 + 11 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 tests/qemupassttest.c create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 9a2f065590..4c01a07135 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -5464,6 +5464,44 @@ ports **with the exception of some subset**. </devices> ... +Custom passt commandline arguments +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. warning:: + + **This is an unsupported feature for development and testing only.** + Using it will taint the domain. Users are strongly advised to use the + proper libvirt XML elements for configuring passt instead. + + +:since:`Since 11.7.0` For development and testing purposes, it is +sometimes useful to be able to pass additional command-line arguments +directly to the passt process. This can be accomplished using a +special passt namespace in the domain XML that is similar to the qemu +commandline namespace: + +:: + + ... + <devices> + ... + <interface type='user'> + <backend type='passt'> + <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'> + <passt:arg value='--debug'/> + <passt:arg value='--verbose'/> + </passt:commandline> + </backend> + </interface> + </devices> + ... + +Any arguments provided using this method will be appended to the passt +command line, and will therefore override any default options set by +libvirt in the case of conflicts. **This can lead to unexpected behavior +and libvirt cannot guarantee functionality when its default configuration +is overridden.** + Generic ethernet connection ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1e24e41a48..9721763622 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2918,6 +2918,10 @@ virDomainNetDefFree(virDomainNetDef *def) g_free(def->backend.tap); g_free(def->backend.vhost); g_free(def->backend.logFile); + if (def->backend.passtCommandline) { + g_strfreev(def->backend.passtCommandline->args); + g_free(def->backend.passtCommandline); + } virDomainNetTeamingInfoFree(def->teaming); g_free(def->virtPortProfile); g_free(def->script); @@ -9772,6 +9776,7 @@ virDomainNetBackendParseXML(xmlNodePtr node, { g_autofree char *tap = virXMLPropString(node, "tap"); g_autofree char *vhost = virXMLPropString(node, "vhost"); + xmlNodePtr cur; /* In the case of NET_TYPE_USER, backend type can be unspecified * (i.e. VIR_DOMAIN_NET_BACKEND_DEFAULT) and that means 'use @@ -9808,6 +9813,38 @@ virDomainNetBackendParseXML(xmlNodePtr node, def->backend.vhost = virFileSanitizePath(vhost); } + /* Parse passt namespace commandline */ + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (cur->ns && + STREQ((const char *)cur->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") && + STREQ((const char *)cur->name, "commandline")) { + xmlNodePtr arg_node = cur->children; + g_autoptr(GPtrArray) args = g_ptr_array_new(); + + while (arg_node != NULL) { + if (arg_node->type == XML_ELEMENT_NODE && + arg_node->ns && + STREQ((const char *)arg_node->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") && + STREQ((const char *)arg_node->name, "arg")) { + g_autofree char *value = virXMLPropString(arg_node, "value"); + if (value) + g_ptr_array_add(args, g_strdup(value)); + } + arg_node = arg_node->next; + } + + if (args->len > 0) { + def->backend.passtCommandline = g_new0(virDomainNetBackendPasstCommandline, 1); + g_ptr_array_add(args, NULL); /* NULL-terminate */ + def->backend.passtCommandline->args = (char **)g_ptr_array_steal(args, NULL); + } + } + } + cur = cur->next; + } + return 0; } @@ -20802,6 +20839,7 @@ virDomainNetBackendIsEqual(virDomainNetBackend *src, STRNEQ_NULLABLE(src->logFile, dst->logFile)) { return false; } + return true; } @@ -24921,11 +24959,29 @@ virDomainNetTeamingInfoFormat(virDomainNetTeamingInfo *teaming, } +static void +virDomainNetBackendPasstCommandLineFormat(virBuffer *buf, + virDomainNetBackend *backend) +{ + g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); + GStrv n; + + if (backend->passtCommandline && backend->passtCommandline->args) { + for (n = backend->passtCommandline->args; n && *n; n++) + virBufferEscapeString(&childBuf, "<passt:arg value='%s'/>\n", *n); + virBufferAddLit(&attrBuf, " xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'"); + virXMLFormatElement(buf, "passt:commandline", &attrBuf, &childBuf); + } + +} + static void virDomainNetBackendFormat(virBuffer *buf, virDomainNetBackend *backend) { g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf); if (backend->type) { virBufferAsprintf(&attrBuf, " type='%s'", @@ -24934,7 +24990,10 @@ virDomainNetBackendFormat(virBuffer *buf, virBufferEscapeString(&attrBuf, " tap='%s'", backend->tap); virBufferEscapeString(&attrBuf, " vhost='%s'", backend->vhost); virBufferEscapeString(&attrBuf, " logFile='%s'", backend->logFile); - virXMLFormatElement(buf, "backend", &attrBuf, NULL); + + virDomainNetBackendPasstCommandLineFormat(&childBuf, backend); + + virXMLFormatElement(buf, "backend", &attrBuf, &childBuf); } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 6997cf7c09..1f51bad546 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1070,12 +1070,18 @@ struct _virDomainActualNetDef { unsigned int class_id; /* class ID for bandwidth 'floor' */ }; +typedef struct _virDomainNetBackendPasstCommandline virDomainNetBackendPasstCommandline; +struct _virDomainNetBackendPasstCommandline { + char **args; /* NULL-terminated array of arguments */ +}; + struct _virDomainNetBackend { virDomainNetBackendType type; char *tap; char *vhost; /* The following are currently only valid/used when backend type='passt' */ char *logFile; /* path to logfile used by passt process */ + virDomainNetBackendPasstCommandline *passtCommandline; /* for passt overrides */ }; struct _virDomainNetPortForwardRange { diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 183dd5db5e..e176073c6a 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -3908,6 +3908,9 @@ </optional> <optional> <element name="backend"> + <optional> + <ref name="passtcmdline"/> + </optional> <optional> <attribute name="type"> <choice> @@ -8877,6 +8880,18 @@ </attribute> </define> + <define name="passtcmdline"> + <element name="commandline" ns="http://libvirt.org/schemas/domain/passt/1.0"> + <interleave> + <zeroOrMore> + <element name="arg"> + <attribute name="value"/> + </element> + </zeroOrMore> + </interleave> + </element> + </define> + <define name="coalesce"> <element name="coalesce"> <interleave> diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c index fcc34de384..0163553cee 100644 --- a/src/qemu/qemu_passt.c +++ b/src/qemu/qemu_passt.c @@ -317,6 +317,15 @@ qemuPasstStart(virDomainObj *vm, virCommandAddArg(cmd, virBufferCurrentContent(&buf)); } + /* Add custom passt arguments from namespace */ + if (net->backend.passtCommandline && net->backend.passtCommandline->args) { + for (i = 0; net->backend.passtCommandline->args[i]; i++) { + virCommandAddArg(cmd, net->backend.passtCommandline->args[i]); + } + + /* Taint the domain when using custom passt arguments */ + qemuDomainObjTaint(driver, vm, VIR_DOMAIN_TAINT_CUSTOM_ARGV, NULL); + } if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0) return -1; diff --git a/tests/meson.build b/tests/meson.build index 0d76d37959..fe9013b600 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -269,6 +269,7 @@ tests += [ { 'name': 'networkxml2xmlupdatetest' }, { 'name': 'nodedevxml2xmltest' }, { 'name': 'nwfilterxml2xmltest' }, + { 'name': 'qemupassttest' }, { 'name': 'seclabeltest' }, { 'name': 'secretxml2xmltest' }, { 'name': 'sockettest' }, diff --git a/tests/qemupassttest.c b/tests/qemupassttest.c new file mode 100644 index 0000000000..84f4c1510a --- /dev/null +++ b/tests/qemupassttest.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include "testutils.h" +#include "conf/domain_conf.h" +#include "viralloc.h" +#include "virstring.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("tests.qemupassttest"); + +struct testPasstData { + const char *name; + const char *xmlfile; + const char * const *expectedArgs; + size_t nExpectedArgs; + bool expectCustomArgs; +}; + +static virDomainDef * +testParseDomainXML(const char *xmlfile) +{ + g_autofree char *xmlpath = NULL; + g_autofree char *xmldata = NULL; + virDomainDef *def = NULL; + g_autoptr(virDomainXMLOption) xmlopt = NULL; + + xmlpath = g_strdup_printf("%s/qemuxmlconfdata/%s", abs_srcdir, xmlfile); + + if (virTestLoadFile(xmlpath, &xmldata) < 0) + return NULL; + + if (!(xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL, NULL, NULL))) + return NULL; + + def = virDomainDefParseString(xmldata, xmlopt, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE); + + return def; +} + +static int +testPasstParseCustomArgs(const void *opaque) +{ + const struct testPasstData *data = opaque; + g_autoptr(virDomainDef) def = NULL; + virDomainNetDef *net = NULL; + size_t i; + + if (!(def = testParseDomainXML(data->xmlfile))) { + VIR_TEST_DEBUG("Failed to parse domain XML"); + return -1; + } + + /* Find the interface with passt backend */ + for (i = 0; i < def->nnets; i++) { + if (def->nets[i]->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) { + net = def->nets[i]; + break; + } + } + + if (!net) { + VIR_TEST_DEBUG("No passt interface found in domain XML"); + return -1; + } + + /* Check if we have custom arguments */ + if (data->expectCustomArgs) { + char **args; + + if (!net->backend.passtCommandline || !net->backend.passtCommandline->args) { + VIR_TEST_DEBUG("Expected custom args but none found"); + return -1; + } + + args = net->backend.passtCommandline->args; + + if (g_strv_length(args) != data->nExpectedArgs) { + VIR_TEST_DEBUG("Expected %zu arguments but found %u", + data->nExpectedArgs, g_strv_length(args)); + return -1; + } + + /* Verify all expected arguments are present */ + for (i = 0; i < data->nExpectedArgs; i++) { + if (!g_strv_contains((const char * const *)args, data->expectedArgs[i])) { + VIR_TEST_DEBUG("Missing expected argument: %s", data->expectedArgs[i]); + return -1; + } + } + } else { + /* Should not have custom arguments */ + if (net->backend.passtCommandline && + net->backend.passtCommandline->args && + *net->backend.passtCommandline->args) { + VIR_TEST_DEBUG("Found custom args but none expected"); + return -1; + } + } + + return 0; +} + +static int +mymain(void) +{ + int ret = 0; + + static const char * const customArgsExpected[] = { + "--debug", + "--verbose", + "--socket=/tmp/foo.socket" + }; + + struct testPasstData customArgsData = { + .name = "custom-args", + .xmlfile = "net-user-passt-custom-args.xml", + .expectedArgs = customArgsExpected, + .nExpectedArgs = G_N_ELEMENTS(customArgsExpected), + .expectCustomArgs = true, + }; + + struct testPasstData noCustomArgsData = { + .name = "no-custom-args", + .xmlfile = "net-user-passt.xml", + .expectedArgs = NULL, + .nExpectedArgs = 0, + .expectCustomArgs = false, + }; + + if (virTestRun("passt XML parsing with custom args", + testPasstParseCustomArgs, &customArgsData) < 0) + ret = -1; + + if (virTestRun("passt XML parsing without custom args", + testPasstParseCustomArgs, &noCustomArgsData) < 0) + ret = -1; + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args new file mode 100644 index 0000000000..48d2596594 --- /dev/null +++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args @@ -0,0 +1,35 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \ +-accel tcg \ +-cpu qemu64 \ +-m size=219136k \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-boot strict=on \ +-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","read-only":false}' \ +-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-storage","id":"ide0-0-0","bootindex":1}' \ +-netdev '{"type":"stream","addr":{"type":"unix","path":"/var/run/libvirt/qemu/passt/-1-QEMUGuest1-net0.socket"},"server":false,"reconnect-ms":5000,"id":"hostnet0"}' \ +-device '{"driver":"rtl8139","netdev":"hostnet0","id":"net0","mac":"00:11:22:33:44:55","bus":"pci.0","addr":"0x2"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml new file mode 100644 index 0000000000..6718893a52 --- /dev/null +++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml @@ -0,0 +1,67 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0' model='none'/> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <source dev='eth42'/> + <ip address='172.17.2.0' family='ipv4' prefix='24'/> + <ip address='2001:db8:ac10:fd01::feed' family='ipv6'/> + <portForward proto='tcp' address='2001:db8:ac10:fd01::1:10'> + <range start='22' to='2022'/> + <range start='1000' end='1050'/> + <range start='1020' exclude='yes'/> + <range start='1030' end='1040' exclude='yes'/> + </portForward> + <portForward proto='udp' address='1.2.3.4' dev='eth0'> + <range start='5000' end='5020' to='6000'/> + <range start='5010' end='5015' exclude='yes'/> + </portForward> + <portForward proto='tcp'> + <range start='80'/> + </portForward> + <portForward proto='tcp'> + <range start='443' to='344'/> + </portForward> + <model type='rtl8139'/> + <backend type='passt' logFile='/var/log/loglaw.blog'> + <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'> + <passt:arg value='--debug'/> + <passt:arg value='--verbose'/> + <passt:arg value='--socket=/tmp/foo.socket'/> + </passt:commandline> + </backend> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml new file mode 100644 index 0000000000..a2a0f4c245 --- /dev/null +++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml @@ -0,0 +1,64 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='block' device='disk'> + <driver name='qemu' type='raw'/> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' target='0' unit='0'/> + </disk> + <controller type='usb' index='0' model='none'/> + <controller type='ide' index='0'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <interface type='user'> + <mac address='00:11:22:33:44:55'/> + <source dev='eth42'/> + <ip address='172.17.2.0' family='ipv4' prefix='24'/> + <ip address='2001:db8:ac10:fd01::feed' family='ipv6'/> + <portForward proto='tcp' address='2001:db8:ac10:fd01::1:10'> + <range start='22' to='2022'/> + <range start='1000' end='1050'/> + <range start='1020' exclude='yes'/> + <range start='1030' end='1040' exclude='yes'/> + </portForward> + <portForward proto='udp' address='1.2.3.4' dev='eth0'> + <range start='5000' end='5020' to='6000'/> + <range start='5010' end='5015' exclude='yes'/> + </portForward> + <portForward proto='tcp'> + <range start='80'/> + </portForward> + <portForward proto='tcp'> + <range start='443' to='344'/> + </portForward> + <model type='rtl8139'/> + <backend type='passt' logFile='/var/log/loglaw.blog'> + <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'> + <passt:arg value='--debug'/> + <passt:arg value='--verbose'/> + <passt:arg value='--socket=/tmp/foo.socket'/> + </passt:commandline> + </backend> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </interface> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <memballoon model='none'/> + </devices> +</domain> diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index 9fba984290..839ae49ed4 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -1805,6 +1805,7 @@ mymain(void) DO_TEST_CAPS_LATEST("net-user-addr"); DO_TEST_CAPS_LATEST("net-user-passt"); DO_TEST_CAPS_VER("net-user-passt", "7.2.0"); + DO_TEST_CAPS_LATEST("net-user-passt-custom-args"); DO_TEST_CAPS_LATEST_PARSE_ERROR("net-user-slirp-portforward"); DO_TEST_CAPS_LATEST("net-vhostuser-passt"); DO_TEST_CAPS_LATEST_PARSE_ERROR("net-vhostuser-passt-no-shmem"); -- 2.50.0