Eric Blake
2020-Aug-28 21:22 UTC
[Libguestfs] [nbdkit PATCH 0/3] .list_exports in nbd plugin
Another series on top of my exportname filter, marking off another todo bullet point. With this, you can now use the NBD plugin as a transparent passthrough of all export names served by the remote server in both directions (list advertisement server to client, and export name from client to server). Eric Blake (3): nbd: Implement .default_export, .export_description nbd: Add dynamic-export=true option nbd: Implement .list_exports plugins/nbd/nbdkit-nbd-plugin.pod | 30 +++- tests/Makefile.am | 4 + plugins/nbd/nbd.c | 250 +++++++++++++++++++++++++----- tests/test-nbd-dynamic-content.sh | 75 +++++++++ tests/test-nbd-dynamic-list.sh | 162 +++++++++++++++++++ 5 files changed, 482 insertions(+), 39 deletions(-) create mode 100755 tests/test-nbd-dynamic-content.sh create mode 100755 tests/test-nbd-dynamic-list.sh -- 2.28.0
Eric Blake
2020-Aug-28 21:22 UTC
[Libguestfs] [nbdkit PATCH 1/3] nbd: Implement .default_export, .export_description
As long as we can only connect to one export name of the server, we don't need .list_exports, and our default export name was hard-coded on the command line. With new libnbd 1.4 functionality, we can also report any description that the server handed us. (Of course, limiting ourselves to one export name is boring, so the next patch will take further advantage of libnbd 1.4 to lift that restriction.) Signed-off-by: Eric Blake <eblake@redhat.com> --- plugins/nbd/nbd.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c index 34194950..e8ce4124 100644 --- a/plugins/nbd/nbd.c +++ b/plugins/nbd/nbd.c @@ -558,6 +558,10 @@ nbdplug_open_handle (int readonly) goto errnbd; if (nbd_add_meta_context (h->nbd, LIBNBD_CONTEXT_BASE_ALLOCATION) == -1) goto errnbd; +#if LIBNBD_HAVE_NBD_SET_FULL_INFO + if (nbd_set_full_info (h->nbd, 1) == -1) + goto errnbd; +#endif if (nbd_set_tls (h->nbd, tls) == -1) goto errnbd; if (tls_certificates && @@ -619,6 +623,13 @@ nbdplug_open_handle (int readonly) return NULL; } +/* Canonical name of default export. */ +static const char * +nbdplug_default_export (int readonly, int is_tls) +{ + return export; +} + /* Create the per-connection handle. */ static void * nbdplug_open (int readonly) @@ -652,6 +663,19 @@ nbdplug_close (void *handle) nbdplug_close_handle (h); } +/* Description. */ +static const char * +nbdplug_export_description (void *handle) +{ +#if LIBNBD_HAVE_NBD_GET_EXPORT_DESCRIPTION + struct handle *h = handle; + CLEANUP_FREE char *desc = nbd_get_export_description (h->nbd); + if (desc) + return nbdkit_strdup_intern (desc); +#endif + return NULL; +} + /* Get the file size. */ static int64_t nbdplug_get_size (void *handle) @@ -947,8 +971,10 @@ static struct nbdkit_plugin plugin = { .magic_config_key = "uri", .after_fork = nbdplug_after_fork, .dump_plugin = nbdplug_dump_plugin, + .default_export = nbdplug_default_export, .open = nbdplug_open, .close = nbdplug_close, + .export_description = nbdplug_export_description, .get_size = nbdplug_get_size, .can_write = nbdplug_can_write, .can_flush = nbdplug_can_flush, -- 2.28.0
Eric Blake
2020-Aug-28 21:22 UTC
[Libguestfs] [nbdkit PATCH 2/3] nbd: Add dynamic-export=true option
Now that nbdkit gives us the ability to know what export name the client wants, we can pass that name on to the server. This only works for non-shared connections (in a shared connection, we only connect to the server once, but we can't differentiate content without different connections); and when uri is specified, it requires libnbd 1.4 (as we need opt_mode to override the export name from the uri with the export name from the client). libnbd 1.4 also lets us report the canonical name of the "" export according to the server. With this patch, it will merely change our advertisement from [""] to ["name"], but the next patch will further add .list_exports so that a client can see the same advertisement that the server sent. Signed-off-by: Eric Blake <eblake@redhat.com> --- plugins/nbd/nbdkit-nbd-plugin.pod | 23 +++- tests/Makefile.am | 2 + plugins/nbd/nbd.c | 173 +++++++++++++++++++++++------- tests/test-nbd-dynamic-content.sh | 75 +++++++++++++ 4 files changed, 233 insertions(+), 40 deletions(-) create mode 100755 tests/test-nbd-dynamic-content.sh diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod index 0c3271e2..55f6fff0 100644 --- a/plugins/nbd/nbdkit-nbd-plugin.pod +++ b/plugins/nbd/nbdkit-nbd-plugin.pod @@ -10,7 +10,7 @@ nbdkit-nbd-plugin - proxy / forward to another NBD server socket=SOCKNAME | socket-fd=FD | [uri=]URI } - [export=NAME] [retry=N] [shared=BOOL] + [dynamic-export=BOOL] [export=NAME] [retry=N] [shared=BOOL] [tls=MODE] [tls-certificates=DIR] [tls-verify=BOOL] [tls-username=NAME] [tls-psk=FILE] @@ -112,6 +112,9 @@ The C<uri> parameter is only available when the plugin was compiled against libnbd with URI support; C<nbdkit --dump-plugin nbd> will contain C<libnbd_uri=1> if this is the case. +The export portion of the URI is ignored when using +C<dynamic-export=true>. + C<uri=> is a magic config key and may be omitted in most cases. See L<nbdkit(1)/Magic parameters>. @@ -126,7 +129,8 @@ Other parameters control the NBD connection: If this parameter is given, and the server speaks new style protocol, then connect to the named export instead of the default export (the empty string). If C<uri> is supplied, the export name should be -embedded in the URI instead. +embedded in the URI instead. This is incompatible with +C<dynamic-export=true>. =item B<retry=>N @@ -149,6 +153,20 @@ If this parameter is set to C<true>, the plugin will open a single connection to the server when nbdkit is first started (the C<retry> parameter may be necessary to coordinate timing of the remote server startup), and all clients to nbdkit will share that single connection. +This mode is incompatible with B<dynamic-export=true>. + +=item B<dynamic-export=false> + +=item B<dynamic-export=true> + +This parameter defaults to false, in which case all clients to nbdkit +use the same export of the server, as set by C<export> or C<uri>, +regardless of the client's export name request. If it is set to true, +nbdkit will pass the client's requested export name over to the final +NBD server, which means clients requesting different export names can +see different content when the server differentiates content by export +name. Dynamic exports prevent the use of C<shared> mode, and thus are +not usable with C<command> or C<socket-fd>. =item B<tls=off> @@ -283,6 +301,7 @@ C<nbdkit-nbd-plugin> first appeared in nbdkit 1.2. L<nbdkit(1)>, L<nbdkit-captive(1)>, L<nbdkit-filter(3)>, +L<nbdkit-exportname-filter(1)>, L<nbdkit-tls(1)>, L<nbdkit-plugin(3)>, L<libnbd(3)>, diff --git a/tests/Makefile.am b/tests/Makefile.am index 911fa2b3..14e9abdb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -762,6 +762,7 @@ if HAVE_LIBNBD # nbd plugin test. LIBGUESTFS_TESTS += test-nbd TESTS += \ + test-nbd-dynamic-content.sh \ test-nbd-extents.sh \ test-nbd-qcow2.sh \ test-nbd-tls.sh \ @@ -769,6 +770,7 @@ TESTS += \ test-nbd-vsock.sh \ $(NULL) EXTRA_DIST += \ + test-nbd-dynamic-content.sh \ test-nbd-extents.sh \ test-nbd-qcow2.sh \ test-nbd-tls.sh \ diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c index e8ce4124..7389b6d9 100644 --- a/plugins/nbd/nbd.c +++ b/plugins/nbd/nbd.c @@ -109,8 +109,11 @@ static string_vector command = empty_vector; /* Connect to a socket file descriptor. */ static int socket_fd = -1; -/* Name of export on remote server, default '', ignored for oldstyle */ +/* Name of export on remote server, default '', ignored for oldstyle, + * NULL if dynamic. + */ static const char *export; +static bool dynamic_export; /* Number of retries */ static unsigned retry; @@ -126,7 +129,8 @@ static int tls_verify = -1; static const char *tls_username; static char *tls_psk; -static struct handle *nbdplug_open_handle (int readonly); +static struct handle *nbdplug_open_handle (int readonly, + const char *client_export); static void nbdplug_close_handle (struct handle *h); static void @@ -180,6 +184,12 @@ nbdplug_config (const char *key, const char *value) } else if (strcmp (key, "export") == 0) export = value; + else if (strcmp (key, "dynamic-export") == 0) { + r = nbdkit_parse_bool (value); + if (r == -1) + return -1; + dynamic_export = r; + } else if (strcmp (key, "retry") == 0) { if (nbdkit_parse_unsigned ("retry", value, &retry) == -1) return -1; @@ -308,10 +318,31 @@ nbdplug_config_complete (void) abort (); /* can't happen, if checks above were correct */ } - /* Check the other parameters. */ - if (!export) + /* Can't mix dynamic-export with export or shared (including + * connection modes that imply shared). Also, it requires + * new-enough libnbd if uri was used. + */ + if (dynamic_export) { + if (export) { + nbdkit_error ("cannot mix 'dynamic-export' with explicit export name"); + return -1; + } + if (shared) { + nbdkit_error ("cannot use 'dynamic-export' with shared connection"); + return -1; + } +#if !LIBNBD_HAVE_NBD_SET_OPT_MODE + if (uri) { + nbdkit_error ("libnbd too old to support 'dynamic-export' with uri " + "connection"); + return -1; + } +#endif + } + else if (!export) export = ""; + /* Check the other parameters. */ if (tls == -1) tls = (tls_certificates || tls_verify >= 0 || tls_username || tls_psk) ? LIBNBD_TLS_ALLOW : LIBNBD_TLS_DISABLE; @@ -338,7 +369,7 @@ nbdplug_config_complete (void) static int nbdplug_after_fork (void) { - if (shared && (shared_handle = nbdplug_open_handle (false)) == NULL) + if (shared && (shared_handle = nbdplug_open_handle (false, NULL)) == NULL) return -1; return 0; } @@ -353,6 +384,7 @@ nbdplug_after_fork (void) "arg=<ARG> Parameters for command.\n" \ "socket-fd=<FD> Socket file descriptor to connect to.\n" \ "export=<NAME> Export name to connect to (default \"\").\n" \ + "dynamic-export=<BOOL> True to enable export name pass-through.\n" \ "retry=<N> Retry connection up to N seconds (default 0).\n" \ "shared=<BOOL> True to share one server connection among all clients,\n" \ " rather than a connection per client (default false).\n" \ @@ -510,12 +542,46 @@ nbdplug_reply (struct handle *h, struct transaction *trans) return err ? -1 : 0; } +/* Move an nbd handle from created to negotiating/ready. Error reporting + * is left to the caller. + */ +static int +nbdplug_connect (struct nbd_handle *nbd) +{ + if (tls_certificates && + nbd_set_tls_certificates (nbd, tls_certificates) == -1) + return -1; + if (tls_verify >= 0 && nbd_set_tls_verify_peer (nbd, tls_verify) == -1) + return -1; + if (tls_username && nbd_set_tls_username (nbd, tls_username) == -1) + return -1; + if (tls_psk && nbd_set_tls_psk_file (nbd, tls_psk) == -1) + return -1; + if (uri) + return nbd_connect_uri (nbd, uri); + else if (sockname) + return nbd_connect_unix (nbd, sockname); + else if (hostname) + return nbd_connect_tcp (nbd, hostname, port); + else if (raw_cid) +#if !USE_VSOCK + abort (); +#else + return nbd_connect_vsock (nbd, cid, vport); +#endif + else if (command.size > 0) + return nbd_connect_systemd_socket_activation (nbd, (char **) command.ptr); + else if (socket_fd >= 0) + return nbd_connect_socket (nbd, socket_fd); + else + abort (); +} + /* Create the shared or per-connection handle. */ static struct handle * -nbdplug_open_handle (int readonly) +nbdplug_open_handle (int readonly, const char *client_export) { struct handle *h; - int r; unsigned long retries = retry; h = calloc (1, sizeof *h); @@ -550,11 +616,16 @@ nbdplug_open_handle (int readonly) } #endif + if (dynamic_export) + assert (client_export); + else + client_export = export; + retry: h->nbd = nbd_create (); if (!h->nbd) goto errnbd; - if (nbd_set_export_name (h->nbd, export) == -1) + if (nbd_set_export_name (h->nbd, client_export) == -1) goto errnbd; if (nbd_add_meta_context (h->nbd, LIBNBD_CONTEXT_BASE_ALLOCATION) == -1) goto errnbd; @@ -562,36 +633,17 @@ nbdplug_open_handle (int readonly) if (nbd_set_full_info (h->nbd, 1) == -1) goto errnbd; #endif + if (dynamic_export && uri) { +#if LIBNBD_HAVE_NBD_SET_OPT_MODE + if (nbd_set_opt_mode (h->nbd, 1) == -1) + goto errnbd; +#else + abort (); /* Prevented by .config_complete */ +#endif + } if (nbd_set_tls (h->nbd, tls) == -1) goto errnbd; - if (tls_certificates && - nbd_set_tls_certificates (h->nbd, tls_certificates) == -1) - goto errnbd; - if (tls_verify >= 0 && nbd_set_tls_verify_peer (h->nbd, tls_verify) == -1) - goto errnbd; - if (tls_username && nbd_set_tls_username (h->nbd, tls_username) == -1) - goto errnbd; - if (tls_psk && nbd_set_tls_psk_file (h->nbd, tls_psk) == -1) - goto errnbd; - if (uri) - r = nbd_connect_uri (h->nbd, uri); - else if (sockname) - r = nbd_connect_unix (h->nbd, sockname); - else if (hostname) - r = nbd_connect_tcp (h->nbd, hostname, port); - else if (raw_cid) -#if !USE_VSOCK - abort (); -#else - r = nbd_connect_vsock (h->nbd, cid, vport); -#endif - else if (command.size > 0) - r = nbd_connect_systemd_socket_activation (h->nbd, (char **) command.ptr); - else if (socket_fd >= 0) - r = nbd_connect_socket (h->nbd, socket_fd); - else - abort (); - if (r == -1) { + if (nbdplug_connect (h->nbd) == -1) { if (retries--) { nbdkit_debug ("connect failed; will try again: %s", nbd_get_error ()); nbd_close (h->nbd); @@ -601,6 +653,16 @@ nbdplug_open_handle (int readonly) goto errnbd; } +#if LIBNBD_HAVE_NBD_SET_OPT_MODE + /* Oldstyle servers can't change export name, but that's okay. */ + if (uri && dynamic_export && nbd_aio_is_negotiating (h->nbd)) { + if (nbd_set_export_name (h->nbd, client_export) == -1) + goto errnbd; + if (nbd_opt_go (h->nbd) == -1) + goto errnbd; + } +#endif + if (readonly) h->readonly = true; @@ -627,7 +689,42 @@ nbdplug_open_handle (int readonly) static const char * nbdplug_default_export (int readonly, int is_tls) { - return export; + const char *ret = ""; + CLEANUP_FREE char *name = NULL; + + if (!dynamic_export) + return export; +#if LIBNBD_HAVE_NBD_SET_FULL_INFO + /* Best effort determination of server's canonical name. If it + * fails, we're fine using the default name on our end (NBD_OPT_GO + * might still work on "" later on). + */ + struct nbd_handle *nbd = nbd_create (); + + if (!nbd) + return ""; + if (nbd_set_full_info (nbd, 1) == -1) + goto out; + if (nbd_set_opt_mode (nbd, 1) == -1) + goto out; + if (nbdplug_connect (nbd) == -1) + goto out; + if (nbd_set_export_name (nbd, "") == -1) + goto out; + if (nbd_opt_info (nbd) == -1) + goto out; + name = nbd_get_canonical_export_name (nbd); + if (name) + ret = nbdkit_strdup_intern (name); + + out: + if (nbd_aio_is_negotiating (nbd)) + nbd_opt_abort (nbd); + else if (nbd_aio_is_ready (nbd)) + nbd_shutdown (nbd, 0); + nbd_close (nbd); +#endif + return ret; } /* Create the per-connection handle. */ @@ -636,7 +733,7 @@ nbdplug_open (int readonly) { if (shared) return shared_handle; - return nbdplug_open_handle (readonly); + return nbdplug_open_handle (readonly, nbdkit_export_name ()); } /* Free up the shared or per-connection handle. */ diff --git a/tests/test-nbd-dynamic-content.sh b/tests/test-nbd-dynamic-content.sh new file mode 100755 index 00000000..bbd889e4 --- /dev/null +++ b/tests/test-nbd-dynamic-content.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright (C) 2019-2020 Red Hat Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +source ./functions.sh +set -e +set -x + +# This test works with older libnbd, showing that dynamic mode affects +# content. XXX Also write a test, requiring newer libnbd, to show export list +requires_plugin info +requires nbdsh --version + +sock1=`mktemp -u` +export sock2=`mktemp -u` +pid1="test-nbd-dynamic-content.pid1" +pid2="test-nbd-dynamic-content.pid2" +files="$sock1 $sock2 $pid1 $pid2" +rm -f $files +cleanup_fn rm -f $files + +# Long-running info server, for content sensitive to export name +start_nbdkit -P $pid1 -U $sock1 info exportname + +# Long-running nbd bridge, which should pass export names through +start_nbdkit -P $pid2 -U $sock2 nbd socket=$sock1 dynamic-export=true + +long=$(printf %04096d 1) +export e +for e in "" "test" "/" "//" " " "/ " "?" "ใในใ" "-n" '\\' $'\n' "%%" "$long" +do + nbdsh -c ' +import os + +e = os.environ["e"] +h.set_export_name (e) +h.connect_unix (os.environ["sock2"]) + +size = h.get_size () +assert size == len (e.encode("utf-8")) + +# Zero-sized reads are not defined in the NBD protocol. +if size > 0: + buf = h.pread (size, 0) + assert buf == e.encode("utf-8") +' +done -- 2.28.0
Eric Blake
2020-Aug-28 21:22 UTC
[Libguestfs] [nbdkit PATCH 3/3] nbd: Implement .list_exports
With new-enough libnbd and our new dynamic-export mode, we can grab the export list from the server for replay to the client. Signed-off-by: Eric Blake <eblake@redhat.com> --- plugins/nbd/nbdkit-nbd-plugin.pod | 7 ++ tests/Makefile.am | 2 + plugins/nbd/nbd.c | 53 ++++++++++ tests/test-nbd-dynamic-content.sh | 2 +- tests/test-nbd-dynamic-list.sh | 162 ++++++++++++++++++++++++++++++ 5 files changed, 225 insertions(+), 1 deletion(-) create mode 100755 tests/test-nbd-dynamic-list.sh diff --git a/plugins/nbd/nbdkit-nbd-plugin.pod b/plugins/nbd/nbdkit-nbd-plugin.pod index 55f6fff0..5820ada8 100644 --- a/plugins/nbd/nbdkit-nbd-plugin.pod +++ b/plugins/nbd/nbdkit-nbd-plugin.pod @@ -168,6 +168,13 @@ see different content when the server differentiates content by export name. Dynamic exports prevent the use of C<shared> mode, and thus are not usable with C<command> or C<socket-fd>. +If libnbd is new enough, dynamic export mode is able to advertise the +same exports as listed by the server; C<nbdkit --dump-plugin nbd> will +contain C<libnbd_dynamic_list=1> if this is the case. Regardless of +what this plugin lists, it is also possible to use +L<nbdkit-exportname-filter(1)> to adjust what export names the client +sees or uses as a default. + =item B<tls=off> =item B<tls=on> diff --git a/tests/Makefile.am b/tests/Makefile.am index 14e9abdb..cbbc750a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -763,6 +763,7 @@ if HAVE_LIBNBD LIBGUESTFS_TESTS += test-nbd TESTS += \ test-nbd-dynamic-content.sh \ + test-nbd-dynamic-list.sh \ test-nbd-extents.sh \ test-nbd-qcow2.sh \ test-nbd-tls.sh \ @@ -771,6 +772,7 @@ TESTS += \ $(NULL) EXTRA_DIST += \ test-nbd-dynamic-content.sh \ + test-nbd-dynamic-list.sh \ test-nbd-extents.sh \ test-nbd-qcow2.sh \ test-nbd-tls.sh \ diff --git a/plugins/nbd/nbd.c b/plugins/nbd/nbd.c index 7389b6d9..c2d2d166 100644 --- a/plugins/nbd/nbd.c +++ b/plugins/nbd/nbd.c @@ -407,6 +407,11 @@ nbdplug_dump_plugin (void) printf ("libnbd_tls=%d\n", nbd_supports_tls (nbd)); printf ("libnbd_uri=%d\n", nbd_supports_uri (nbd)); printf ("libnbd_vsock=%d\n", USE_VSOCK); +#if LIBNBD_HAVE_NBD_OPT_LIST + printf ("libnbd_dynamic_list=1\n"); +#else + printf ("libnbd_dynamic_list=0\n"); +#endif nbd_close (nbd); } @@ -685,6 +690,53 @@ nbdplug_open_handle (int readonly, const char *client_export) return NULL; } +#if LIBNBD_HAVE_NBD_OPT_LIST +static int +collect_one (void *opaque, const char *name, const char *desc) +{ + struct nbdkit_exports *exports = opaque; + + if (nbdkit_add_export (exports, name, desc) == -1) + nbdkit_debug ("Unable to share export %s: %s", name, nbd_get_error ()); + return 0; +} +#endif /* LIBNBD_HAVE_NBD_OPT_LIST */ + +/* Export list. */ +static int +nbdplug_list_exports (int readonly, int is_tls, struct nbdkit_exports *exports) +{ +#if LIBNBD_HAVE_NBD_OPT_LIST + if (dynamic_export) { + struct nbd_handle *nbd = nbd_create (); + int r = -1; + + if (!nbd) + goto out; + if (nbd_set_opt_mode (nbd, 1) == -1) + goto out; + if (nbdplug_connect (nbd) == -1) + goto out; + if (nbd_opt_list (nbd, (nbd_list_callback) { .callback = collect_one, + .user_data = exports }) == -1) + goto out; + r = 0; + out: + if (r == -1) + nbdkit_error ("Unable to get list: %s", nbd_get_error ()); + if (nbd) { + if (nbd_aio_is_negotiating (nbd)) + nbd_opt_abort (nbd); + else if (nbd_aio_is_ready (nbd)) + nbd_shutdown (nbd, 0); + nbd_close (nbd); + } + return r; + } +#endif + return nbdkit_add_default_export (exports); +} + /* Canonical name of default export. */ static const char * nbdplug_default_export (int readonly, int is_tls) @@ -1068,6 +1120,7 @@ static struct nbdkit_plugin plugin = { .magic_config_key = "uri", .after_fork = nbdplug_after_fork, .dump_plugin = nbdplug_dump_plugin, + .list_exports = nbdplug_list_exports, .default_export = nbdplug_default_export, .open = nbdplug_open, .close = nbdplug_close, diff --git a/tests/test-nbd-dynamic-content.sh b/tests/test-nbd-dynamic-content.sh index bbd889e4..48d2c30c 100755 --- a/tests/test-nbd-dynamic-content.sh +++ b/tests/test-nbd-dynamic-content.sh @@ -35,7 +35,7 @@ set -e set -x # This test works with older libnbd, showing that dynamic mode affects -# content. XXX Also write a test, requiring newer libnbd, to show export list +# content. requires_plugin info requires nbdsh --version diff --git a/tests/test-nbd-dynamic-list.sh b/tests/test-nbd-dynamic-list.sh new file mode 100755 index 00000000..419ec9bb --- /dev/null +++ b/tests/test-nbd-dynamic-list.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# nbdkit +# Copyright (C) 2019-2020 Red Hat Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Red Hat nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +source ./functions.sh +set -e +set -x + +# This test works with newer libnbd, showing that dynamic mode affects +# export listing. +requires_plugin sh +requires nbdinfo --version + +# Does the nbd plugin support dynamic lists? +if ! nbdkit --dump-plugin nbd | grep -sq libnbd_dynamic_list=1; then + echo "$0: nbd plugin built without dynamic export list support" + exit 77 +fi + +base=test-nbd-dynamic-list +sock1=`mktemp -u` +sock2=`mktemp -u` +pid1="$base.pid1" +pid2="$base.pid2" +files="$sock1 $sock2 $pid1 $pid2 $base.list $base.out1 $base.out2" +rm -f $files +cleanup_fn rm -f $files + +fail=0 + +# Start a long-running server with .list_exports and .default_export +# set to varying contents +start_nbdkit -P $pid1 -U $sock1 eval get_size='echo "$2"|wc -c' \ + open='echo "$3"' list_exports="cat '$PWD/$base.list'" \ + default_export="cat '$PWD/$base.list'" + +# Long-running nbd bridge, which should pass export list through +start_nbdkit -P $pid2 -U $sock2 nbd socket=$sock1 dynamic-export=true + +# check_success_one EXPORT +# - nbdinfo of EXPORT on both servers should succeed, with matching output +check_success_one () +{ + nbdinfo --no-content "nbd+unix:///$1?socket=$sock1" > $base.out1 + nbdinfo --no-content "nbd+unix:///$1?socket=$sock2" > $base.out2 + cat $base.out2 + diff -u $base.out1 $base.out2 +} + +# check_success_list +# - nbdinfo --list on both servers should succeed, with matching output +check_success_list () +{ + nbdinfo --list --json nbd+unix://\?socket=$sock1 > $base.out1 + nbdinfo --list --json nbd+unix://\?socket=$sock2 > $base.out2 + cat $base.out2 + diff -u $base.out1 $base.out2 +} + +# check_success EXPORT... - both sub-tests, on all EXPORTs +check_success() +{ + for exp; do + check_success_one "$exp" + done + check_success_list +} + +# check_fail_one EXPORT +# - nbdinfo of EXPORT on both servers should fail +check_fail_one () +{ + if nbdinfo --no-content "nbd+unix:///$1?socket=$sock1" > $base.out1; then + fail=1 + fi + if nbdinfo --no-content "nbd+unix:///$1?socket=$sock2" > $base.out2; then + fail=1 + fi +} + +# check_fail_list +# - nbdinfo --list on both servers should fail +check_fail_list () +{ + if nbdinfo --list --json nbd+unix://\?socket=$sock1 > $base.out1; then + fail=1 + fi + if nbdinfo --list --json nbd+unix://\?socket=$sock2 > $base.out2; then + fail=1 + fi +} + +# With no file, list_exports and the default export fail, +# but other exports work +check_fail_one "" +check_success_one name +check_fail_list + +# With an empty list, there are 0 exports, and any export works +touch $base.list +check_success "" name + +# An explicit advertisement of the default export, any export works +echo > $base.list +check_success "" name + +# A non-empty default name +echo name > $base.list +check_success "" name + +# Multiple exports, with descriptions +cat > $base.list <<EOF +INTERLEAVED +name1 +desc1 +name2 +desc2 +EOF +echo name > $base.list +check_success "" name1 + +# Longest possible name and description +long=$(printf %04096d 1) +echo NAMES+DESCRIPTIONS > $base.list +echo $long >> $base.list +echo $long >> $base.list +check_success "" $long + +# An invalid name prevents list, but we can still connect +echo 2$long >> $base.list +check_success_one "" +check_fail_list + +exit $fail -- 2.28.0
Richard W.M. Jones
2020-Sep-01 14:33 UTC
Re: [Libguestfs] [nbdkit PATCH 3/3] nbd: Implement .list_exports
This series looks sensible, ACK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
Reasonably Related Threads
- [nbdkit PATCH 0/3] .list_exports in nbd plugin
- [nbdkit PATCH] tests: Swap nbdkit process order in test-nbd-tls-psk.sh
- Re: [nbdkit PATCH] tests: Swap nbdkit process order in test-nbd-tls-psk.sh
- [nbdkit PATCH v3 00/14] exportname filter
- [nbdkit PATCH v2] tests: Accommodate qemu-img 4.1 output change