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 <[email protected]> --- 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 2139eb43..c2d668c1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -771,6 +771,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 \ @@ -779,6 +780,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..488eadb2 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_use_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 5d848c18..9e0e152c 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 _______________________________________________ Libguestfs mailing list [email protected] https://www.redhat.com/mailman/listinfo/libguestfs
