[PATCH 0/2] allow migrations with the ext transport

Hello, I have a use case for live-migration where the socket of the destination libvirtd is proxied by an mTLS WebSocket proxy. Since there is no WebSocket support in Libvirt, I've decided to use the existing ext transport and point it to a proxy binary on the source host of live-migration. While doing that, I've discovered a couple of roadblocks: 1. There is no way of passing cmdline arguments to the ext program 2. Live-migrations using ext transport fail localhost checks This short series addresses those limitations. Sergey Dyasli (2): remote: allow passing cmd arguments to REMOTE_DRIVER_TRANSPORT_EXT remote: allow migrations with the ext transport src/libvirt-domain.c | 8 +++++--- src/remote/remote_driver.c | 30 ++++++++++++++++++++++++++---- src/util/viruri.c | 32 ++++++++++++++++++++++++++++++++ src/util/viruri.h | 2 ++ 4 files changed, 65 insertions(+), 7 deletions(-) -- 2.39.3

Allow passing up to 5 arguments to the ext program via the query parameters. URI example: qemu+ext:///system?command=/bin/prog&ext_arg1=192.168.0.10&ext_arg2=8080 Signed-off-by: Sergey Dyasli <sergey.dyasli@nutanix.com> --- src/remote/remote_driver.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed8762..e5f425da74bf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -805,6 +805,11 @@ static int doRemoteOpenExtractURIArgs(virConnectPtr conn, char **name, char **command, + char **extArg1, + char **extArg2, + char **extArg3, + char **extArg4, + char **extArg5, char **sockname, char **authtype, char **sshauth, @@ -829,6 +834,11 @@ doRemoteOpenExtractURIArgs(virConnectPtr conn, EXTRACT_URI_ARG_STR("name", *name); EXTRACT_URI_ARG_STR("command", *command); + EXTRACT_URI_ARG_STR("ext_arg1", *extArg1); + EXTRACT_URI_ARG_STR("ext_arg2", *extArg2); + EXTRACT_URI_ARG_STR("ext_arg3", *extArg3); + EXTRACT_URI_ARG_STR("ext_arg4", *extArg4); + EXTRACT_URI_ARG_STR("ext_arg5", *extArg5); EXTRACT_URI_ARG_STR("socket", *sockname); EXTRACT_URI_ARG_STR("auth", *authtype); EXTRACT_URI_ARG_STR("sshauth", *sshauth); @@ -895,6 +905,11 @@ doRemoteOpen(virConnectPtr conn, g_autofree char *tls_priority = NULL; g_autofree char *name = NULL; g_autofree char *command = NULL; + g_autofree char *extArg1 = NULL; + g_autofree char *extArg2 = NULL; + g_autofree char *extArg3 = NULL; + g_autofree char *extArg4 = NULL; + g_autofree char *extArg5 = NULL; g_autofree char *sockname = NULL; g_autofree char *netcat = NULL; g_autofree char *port = NULL; @@ -945,6 +960,11 @@ doRemoteOpen(virConnectPtr conn, if (doRemoteOpenExtractURIArgs(conn, &name, &command, + &extArg1, + &extArg2, + &extArg3, + &extArg4, + &extArg5, &sockname, &authtype, &sshauth, @@ -1195,7 +1215,8 @@ doRemoteOpen(virConnectPtr conn, break; case REMOTE_DRIVER_TRANSPORT_EXT: { - char const *cmd_argv[] = { command, NULL }; + char const *cmd_argv[] = { command, extArg1, extArg2, extArg3, + extArg4, extArg5, NULL }; if (!(priv->client = virNetClientNewExternal(cmd_argv))) goto error; -- 2.39.3

On Fri, Oct 10, 2025 at 08:58:17AM +0000, Sergey Dyasli wrote:
Allow passing up to 5 arguments to the ext program via the query parameters. URI example:
qemu+ext:///system?command=/bin/prog&ext_arg1=192.168.0.10&ext_arg2=8080
AFAIR, URI query parameters can be repeated arbitrarily many times, so we shouldn't need to have a different name for each parameter, nor any limit.
Signed-off-by: Sergey Dyasli <sergey.dyasli@nutanix.com> --- src/remote/remote_driver.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ec71eaed8762..e5f425da74bf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -805,6 +805,11 @@ static int doRemoteOpenExtractURIArgs(virConnectPtr conn, char **name, char **command, + char **extArg1, + char **extArg2, + char **extArg3, + char **extArg4, + char **extArg5, char **sockname, char **authtype, char **sshauth, @@ -829,6 +834,11 @@ doRemoteOpenExtractURIArgs(virConnectPtr conn,
EXTRACT_URI_ARG_STR("name", *name); EXTRACT_URI_ARG_STR("command", *command); + EXTRACT_URI_ARG_STR("ext_arg1", *extArg1); + EXTRACT_URI_ARG_STR("ext_arg2", *extArg2); + EXTRACT_URI_ARG_STR("ext_arg3", *extArg3); + EXTRACT_URI_ARG_STR("ext_arg4", *extArg4); + EXTRACT_URI_ARG_STR("ext_arg5", *extArg5); EXTRACT_URI_ARG_STR("socket", *sockname); EXTRACT_URI_ARG_STR("auth", *authtype); EXTRACT_URI_ARG_STR("sshauth", *sshauth); @@ -895,6 +905,11 @@ doRemoteOpen(virConnectPtr conn, g_autofree char *tls_priority = NULL; g_autofree char *name = NULL; g_autofree char *command = NULL; + g_autofree char *extArg1 = NULL; + g_autofree char *extArg2 = NULL; + g_autofree char *extArg3 = NULL; + g_autofree char *extArg4 = NULL; + g_autofree char *extArg5 = NULL; g_autofree char *sockname = NULL; g_autofree char *netcat = NULL; g_autofree char *port = NULL; @@ -945,6 +960,11 @@ doRemoteOpen(virConnectPtr conn, if (doRemoteOpenExtractURIArgs(conn, &name, &command, + &extArg1, + &extArg2, + &extArg3, + &extArg4, + &extArg5, &sockname, &authtype, &sshauth, @@ -1195,7 +1215,8 @@ doRemoteOpen(virConnectPtr conn, break;
case REMOTE_DRIVER_TRANSPORT_EXT: { - char const *cmd_argv[] = { command, NULL }; + char const *cmd_argv[] = { command, extArg1, extArg2, extArg3, + extArg4, extArg5, NULL }; if (!(priv->client = virNetClientNewExternal(cmd_argv))) goto error;
-- 2.39.3
With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

On 10/10/2025 10:18, Daniel P. Berrangé wrote:
On Fri, Oct 10, 2025 at 08:58:17AM +0000, Sergey Dyasli wrote:
Allow passing up to 5 arguments to the ext program via the query parameters. URI example:
qemu+ext:///system?command=/bin/prog&ext_arg1=192.168.0.10&ext_arg2=8080
AFAIR, URI query parameters can be repeated arbitrarily many times, so we shouldn't need to have a different name for each parameter, nor any limit. That indeed seems to be the case, thanks for pointing out! I plan to send out v2 at some point with "ext_arg" parameter that you can repeat multiple times.
Sergey

On Tue, Oct 14, 2025 at 03:13:10PM +0100, Sergey Dyasli wrote:
On 10/10/2025 10:18, Daniel P. Berrangé wrote:
On Fri, Oct 10, 2025 at 08:58:17AM +0000, Sergey Dyasli wrote:
Allow passing up to 5 arguments to the ext program via the query parameters. URI example:
qemu+ext:///system?command=/bin/prog&ext_arg1=192.168.0.10&ext_arg2=8080
AFAIR, URI query parameters can be repeated arbitrarily many times, so we shouldn't need to have a different name for each parameter, nor any limit. That indeed seems to be the case, thanks for pointing out! I plan to send out v2 at some point with "ext_arg" parameter that you can repeat multiple times.
I'd suggest just 'argv' as the parameter name. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|

Add virURICheckExtCommand() in a similar fashion to the existing virURICheckUnixSocket() and use it for (1) the same host migration check and (2) in remoteConnectOpen(). This allows to migrate VMs using the ext transport, as the external command can act as a proxy to the remote libvirt. Signed-off-by: Sergey Dyasli <sergey.dyasli@nutanix.com> --- src/libvirt-domain.c | 8 +++++--- src/remote/remote_driver.c | 7 ++++--- src/util/viruri.c | 32 ++++++++++++++++++++++++++++++++ src/util/viruri.h | 2 ++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ca110bdf8585..9d82b711a17c 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -3605,11 +3605,13 @@ virDomainMigrateCheckNotLocal(const char *dconnuri) return -1; /* - * If someone migrates explicitly to a unix socket, then they have to know - * what they are doing and it most probably was not a mistake. + * If someone migrates explicitly to a unix socket or an ext command, then + * they have to know what they are doing and it most probably was not + * a mistake. */ if ((tempuri->server && STRPREFIX(tempuri->server, "localhost")) || - (!tempuri->server && !virURICheckUnixSocket(tempuri))) { + (!tempuri->server && !virURICheckUnixSocket(tempuri) && + !virURICheckExtCommand(tempuri))) { virReportInvalidArg(dconnuri, "%s", _("Attempt to migrate guest to the same host")); return -1; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e5f425da74bf..0e7e8f2eb76b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -1377,15 +1377,16 @@ remoteConnectOpen(virConnectPtr conn, return VIR_DRV_OPEN_DECLINED; /* Handle deferring to local drivers if we are dealing with a default - * local URI. (Unknown local socket paths may be proxied to a remote - * host so they are treated as remote too). + * local URI. (Unknown local socket paths and commands may be proxied + * to a remote host so they are treated as remote too). * * Deferring to a local driver is needed if: * - the driver is registered in the current daemon * - if we are running monolithic libvirtd, in which case we consider * even un-registered drivers as local */ - if (!conn->uri->server && !virURICheckUnixSocket(conn->uri)) { + if (!conn->uri->server && !virURICheckUnixSocket(conn->uri) && + !virURICheckExtCommand(conn->uri)) { if (virHasDriverForURIScheme(driver)) return VIR_DRV_OPEN_DECLINED; diff --git a/src/util/viruri.c b/src/util/viruri.c index 64995da3420d..2e8c0acfb213 100644 --- a/src/util/viruri.c +++ b/src/util/viruri.c @@ -421,6 +421,38 @@ virURICheckUnixSocket(virURI *uri) } +/** + * virURICheckExtCommand: + * @uri: URI to check + * + * Check if the URI looks like it refers to a user specified command. In such + * scenario the command might do proxying to a remote server even though the URI + * looks like it is only local. + * + * The "command" parameter is looked for in case insensitive manner, by design. + * + * Returns: true if the URI might be proxied to a remote server + */ +bool +virURICheckExtCommand(virURI *uri) +{ + size_t i = 0; + + if (!uri->scheme) + return false; + + if (STRNEQ_NULLABLE(strchr(uri->scheme, '+'), "+ext")) + return false; + + for (i = 0; i < uri->paramsCount; i++) { + if (STRCASEEQ(uri->params[i].name, "command")) + return true; + } + + return false; +} + + void virURIParamsSetIgnore(virURI *uri, bool ignore, diff --git a/src/util/viruri.h b/src/util/viruri.h index ad00570b7f0d..172314bd1084 100644 --- a/src/util/viruri.h +++ b/src/util/viruri.h @@ -61,6 +61,8 @@ const char *virURIGetParam(virURI *uri, const char *name); bool virURICheckUnixSocket(virURI *uri); +bool virURICheckExtCommand(virURI *uri); + void virURIParamsSetIgnore(virURI *uri, bool ignore, const char *names[]); #define VIR_URI_SERVER(uri) ((uri) && (uri)->server ? (uri)->server : "localhost") -- 2.39.3
participants (2)
-
Daniel P. Berrangé
-
Sergey Dyasli