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 <ellorent(a)redhat.com>
---
docs/formatdomain.rst | 40 +++++++++
src/conf/domain_conf.c | 84 ++++++++++++++++++-
src/conf/domain_conf.h | 6 ++
src/qemu/qemu_passt.c | 9 ++
...-user-passt-custom-args.x86_64-latest.args | 39 +++++++++
...t-user-passt-custom-args.x86_64-latest.xml | 57 +++++++++++++
.../net-user-passt-custom-args.xml | 52 ++++++++++++
tests/qemuxmlconftest.c | 1 +
8 files changed, 287 insertions(+), 1 deletion(-)
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..59899aaa4a 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -5464,6 +5464,46 @@ ports **with the exception of some subset**.
</devices>
...
+Custom passt commandline arguments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+: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'
xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:commandline>
+ <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 feature is intended for development and testing only.**
+Arguments are used as specified in the XML, and no validation beyond
+basic libvirt XML schema validation is performed. It is expected that
+this feature will primarily be used for testing new passt options, so
+that they can be evaluated for inclusion in libvirt's schema in a
+future release. It should not be used as a substitute for configuring
+passt with the proper libvirt XML elements and attributes. **When a
+domain uses this feature, it will be marked as "tainted" in the logs and
+the ``virsh dominfo`` output will include "custom-argv" in the list
+of reasons.**
+
Generic ethernet connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1e24e41a48..9d39bb39bd 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,40 @@ 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;
+ 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_free(args, FALSE);
+ } else {
+ g_ptr_array_unref(args);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+
return 0;
}
@@ -20802,6 +20841,33 @@ virDomainNetBackendIsEqual(virDomainNetBackend *src,
STRNEQ_NULLABLE(src->logFile, dst->logFile)) {
return false;
}
+
+ /* Compare passt commandline */
+ if ((src->passtCommandline && dst->passtCommandline) ||
+ (!src->passtCommandline && !dst->passtCommandline)) {
+ if (src->passtCommandline && dst->passtCommandline) {
+ /* Compare argument arrays */
+ char **srcArgs = src->passtCommandline->args;
+ char **dstArgs = dst->passtCommandline->args;
+
+ if (!srcArgs && !dstArgs) {
+ /* Both are NULL, continue */
+ } else if (!srcArgs || !dstArgs) {
+ return false; /* One is NULL, other is not */
+ } else {
+ /* Compare each argument */
+ int i = 0;
+ while (srcArgs[i] || dstArgs[i]) {
+ if (!srcArgs[i] || !dstArgs[i] || STRNEQ(srcArgs[i], dstArgs[i]))
+ return false;
+ i++;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
return true;
}
@@ -24926,6 +24992,7 @@ virDomainNetBackendFormat(virBuffer *buf,
virDomainNetBackend *backend)
{
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
if (backend->type) {
virBufferAsprintf(&attrBuf, " type='%s'",
@@ -24934,7 +25001,22 @@ 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);
+
+ /* Format passt commandline with namespace */
+ if (backend->passtCommandline && backend->passtCommandline->args) {
+ virBufferAddLit(&childBuf, "<passt:commandline
xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>\n&q...);
+ virBufferAdjustIndent(&childBuf, 2);
+
+ for (int i = 0; backend->passtCommandline->args[i]; i++) {
+ virBufferAsprintf(&childBuf, "<passt:arg
value='%s'/>\n",
+ backend->passtCommandline->args[i]);
+ }
+
+ virBufferAdjustIndent(&childBuf, -2);
+ virBufferAddLit(&childBuf, "</passt:commandline>\n");
+ }
+
+ 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/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
index fcc34de384..e64ccfa5aa 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/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..1715d4253c
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
@@ -0,0 +1,39 @@
+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-i386 \
+-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 qemu32 \
+-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 \
+-device
'{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}'
\
+-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \
+-device
'{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"drive-ide0-0-0","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":"52:54:00:12:34:56","bus":"pci.0","addr":"0x3"}'
\
+-vnc 127.0.0.1:0 \
+-device
'{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}'
\
+-device
'{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x4"}'
\
+-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..310ff6c39e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
@@ -0,0 +1,57 @@
+<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='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <cpu mode='custom' match='exact' check='none'>
+ <model fallback='forbid'>qemu32</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-i386</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='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <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='52:54:00:12:34:56'/>
+ <model type='rtl8139'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline
xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </memballoon>
+ <audio id='1' type='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..15eca80b5e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
@@ -0,0 +1,52 @@
+<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='i686' 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-i386</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='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x2'/>
+ </controller>
+ <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='52:54:00:12:34:56'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline
xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
\ No newline at end of file
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