This test proves that an attempt to list meta contexts goes to the server rather than being rejected at the client, even with older nbdkit that fails the attempt[1]; and includes ports to the various languages. Done as a separate commit to allow temporary reordering of the series to prove the test works.
[1] nbdkit didn't accept LIST_META_CONTENT before STRUCTURED_REPLY until commit c39cba73 [v1.27.3, backported to 1.26.6]. Similarly, qemu-nbd didn't accept it until commit da24597d [v6.2]. So, I was able to test the graceful skip by adding a build of nbdkit without that support at the front of my PATH. --- python/t/240-opt-list-meta.py | 25 ++++++++++ ocaml/tests/test_240_opt_list_meta.ml | 30 ++++++++++++ tests/opt-list-meta.c | 34 ++++++++++++-- golang/libnbd_240_opt_list_meta_test.go | 61 ++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 6 deletions(-) diff --git a/python/t/240-opt-list-meta.py b/python/t/240-opt-list-meta.py index 2b66eb5d..50fcfd69 100644 --- a/python/t/240-opt-list-meta.py +++ b/python/t/240-opt-list-meta.py @@ -31,6 +31,7 @@ def f(user_data, name): seen = True +# Get into negotiating state. h = nbd.NBD() h.set_opt_mode(True) h.connect_command(["nbdkit", "-s", "--exit-with-parent", "-v", @@ -77,3 +78,27 @@ def f(user_data, name): assert seen is True h.opt_abort() + +# Repeat but this time without structured replies. Deal gracefully +# with older servers that don't allow the attempt. +h = nbd.NBD() +h.set_opt_mode(True) +h.set_request_structured_replies(False) +h.connect_command(["nbdkit", "-s", "--exit-with-parent", "-v", + "memory", "size=1M"]) +bytes = h.stats_bytes_sent() + +try: + count = 0 + seen = False + r = h.opt_list_meta_context(lambda *args: f(42, *args)) + assert r == count + assert r >= 1 + assert seen is True +except nbd.Error as ex: + assert h.stats_bytes_sent() > bytes + print("ignoring failure from old server: %s" % ex.string) + +# FIXME: Once nbd_opt_structured_reply exists, use it here and retry. + +h.opt_abort() diff --git a/ocaml/tests/test_240_opt_list_meta.ml b/ocaml/tests/test_240_opt_list_meta.ml index 639d33c8..64159185 100644 --- a/ocaml/tests/test_240_opt_list_meta.ml +++ b/ocaml/tests/test_240_opt_list_meta.ml @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *) +open Printf + let count = ref 0 let seen = ref false let f user_data name = @@ -27,6 +29,7 @@ let 0 let () = + (* Get into negotiating state. *) let nbd = NBD.create () in NBD.set_opt_mode nbd true; NBD.connect_command nbd @@ -75,6 +78,33 @@ let assert (r = !count); assert !seen; + NBD.opt_abort nbd; + + (* Repeat but this time without structured replies. Deal gracefully + * with older servers that don't allow the attempt. + *) + let nbd = NBD.create () in + NBD.set_opt_mode nbd true; + NBD.set_request_structured_replies nbd false; + NBD.connect_command nbd + ["nbdkit"; "-s"; "--exit-with-parent"; "-v"; + "memory"; "size=1M"]; + let bytes = NBD.stats_bytes_sent nbd in + (try + count := 0; + seen := false; + let r = NBD.opt_list_meta_context nbd (f 42) in + assert (r = !count); + assert (r >= 1); + assert !seen + with + NBD.Error (errstr, errno) -> + assert (NBD.stats_bytes_sent nbd > bytes); + printf "ignoring failure from old server %s\n" errstr + ); + + (* FIXME: Once nbd_opt_structured_reply exists, use it here and retry. *) + NBD.opt_abort nbd let () = Gc.compact () diff --git a/tests/opt-list-meta.c b/tests/opt-list-meta.c index 9ad8e37e..dc9c6799 100644 --- a/tests/opt-list-meta.c +++ b/tests/opt-list-meta.c @@ -17,6 +17,7 @@ */ /* Test behavior of nbd_opt_list_meta_context. */ +/* See also unit test 240 in the various language ports. */ #include <config.h> @@ -56,6 +57,7 @@ main (int argc, char *argv[]) "memory", "size=1M", NULL }; int max; char *tmp; + uint64_t bytes; /* Get into negotiating state. */ nbd = nbd_create (); @@ -164,7 +166,9 @@ main (int argc, char *argv[]) nbd_opt_abort (nbd); nbd_close (nbd); - /* Repeat but this time without structured replies. */ + /* Repeat but this time without structured replies. Deal gracefully + * with older servers that don't allow the attempt. + */ nbd = nbd_create (); if (nbd == NULL || nbd_set_opt_mode (nbd, true) == -1 || @@ -173,16 +177,36 @@ main (int argc, char *argv[]) fprintf (stderr, "%s\n", nbd_get_error ()); exit (EXIT_FAILURE); } + bytes = nbd_stats_bytes_sent (nbd); - /* FIXME: For now, we reject this client-side, but it is overly strict. */ p = (struct progress) { .count = 0 }; r = nbd_opt_list_meta_context (nbd, (nbd_context_callback) { .callback = check, .user_data = &p}); - if (r != -1) { - fprintf (stderr, "not expecting command to succeed\n"); - exit (EXIT_FAILURE); + if (r == -1) { + if (nbd_stats_bytes_sent (nbd) == bytes) { + fprintf (stderr, "bug: client failed to send request\n"); + exit (EXIT_FAILURE); + } + fprintf (stdout, "ignoring failure from old server: %s\n", + nbd_get_error ()); } + else { + if (r != p.count) { + fprintf (stderr, "inconsistent return value %d, expected %d\n", + r, p.count); + exit (EXIT_FAILURE); + } + if (r < 1 || !p.seen) { + fprintf (stderr, "server did not reply with base:allocation\n"); + exit (EXIT_FAILURE); + } + } + + /* FIXME: Once nbd_opt_structured_reply() exists, use it here and retry. */ + + nbd_opt_abort (nbd); + nbd_close (nbd); exit (EXIT_SUCCESS); } diff --git a/golang/libnbd_240_opt_list_meta_test.go b/golang/libnbd_240_opt_list_meta_test.go index d7322752..2b49c895 100644 --- a/golang/libnbd_240_opt_list_meta_test.go +++ b/golang/libnbd_240_opt_list_meta_test.go @@ -1,5 +1,5 @@ /* libnbd golang tests - * Copyright (C) 2013-2021 Red Hat Inc. + * Copyright (C) 2013-2022 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +19,7 @@ package libnbd import ( + "fmt"; "testing" ) @@ -37,6 +38,7 @@ func listmetaf(user_data int, name string) int { } func Test240OptListMeta(t *testing.T) { + /* Get into negotiating state. */ h, err := Create() if err != nil { t.Fatalf("could not create handle: %s", err) @@ -143,4 +145,61 @@ func Test240OptListMeta(t *testing.T) { if err != nil { t.Fatalf("could not request opt_abort: %s", err) } + + /* Repeat but this time without structured replies. Deal gracefully + * with older servers that don't allow the attempt. + */ + h, err = Create() + if err != nil { + t.Fatalf("could not create handle: %s", err) + } + defer h.Close() + + err = h.SetOptMode(true) + if err != nil { + t.Fatalf("could not set opt mode: %s", err) + } + + err = h.SetRequestStructuredReplies(false) + if err != nil { + t.Fatalf("could not set request structured replies: %s", err) + } + + err = h.ConnectCommand([]string{ + "nbdkit", "-s", "--exit-with-parent", "-v", + "memory", "size=1M", + }) + if err != nil { + t.Fatalf("could not connect: %s", err) + } + + bytes, err := h.StatsBytesSent() + if err != nil { + t.Fatalf("could not collect stats: %s", err) + } + + count = 0 + seen = false + r, err = h.OptListMetaContext(func(name string) int { + return listmetaf(42, name) + }) + if err != nil { + bytes2, err2 := h.StatsBytesSent() + if err2 != nil { + t.Fatalf("could not collect stats: %s", err2) + } + if bytes2 <= bytes { + t.Fatalf("unexpected bytes sent after opt_list_meta_context") + } + fmt.Printf("ignoring failure from old server: %s", err) + } else if r < 1 || r != count || !seen { + t.Fatalf("unexpected count after opt_list_meta_context") + } + + /* FIXME: Once nbd_opt_structured_reply exists, use it here and retry. */ + + err = h.OptAbort() + if err != nil { + t.Fatalf("could not request opt_abort: %s", err) + } } -- 2.37.3 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs