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

Reply via email to