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> --- 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"); + 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