Blame SOURCES/kvm-qemu-nbd-Add-list-option.patch

383d26
From 7d72cea18905a4b635ac75997d5ce719ebc5ffb3 Mon Sep 17 00:00:00 2001
383d26
From: John Snow <jsnow@redhat.com>
383d26
Date: Wed, 27 Mar 2019 17:22:53 +0100
383d26
Subject: [PATCH 115/163] qemu-nbd: Add --list option
383d26
383d26
RH-Author: John Snow <jsnow@redhat.com>
383d26
Message-id: <20190327172308.31077-41-jsnow@redhat.com>
383d26
Patchwork-id: 85204
383d26
O-Subject: [RHEL-7.7 qemu-kvm-rhev PATCH 40/55] qemu-nbd: Add --list option
383d26
Bugzilla: 1691009
383d26
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
383d26
RH-Acked-by: Max Reitz <mreitz@redhat.com>
383d26
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
383d26
383d26
From: Eric Blake <eblake@redhat.com>
383d26
383d26
We want to be able to detect whether a given qemu NBD server is
383d26
exposing the right export(s) and dirty bitmaps, at least for
383d26
regression testing.  We could use 'nbd-client -l' from the upstream
383d26
NBD project to list exports, but it's annoying to rely on
383d26
out-of-tree binaries; furthermore, nbd-client doesn't necessarily
383d26
know about all of the qemu NBD extensions.  Thus, it is time to add
383d26
a new mode to qemu-nbd that merely sniffs all possible information
383d26
from the server during handshake phase, then disconnects and dumps
383d26
the information.
383d26
383d26
This patch actually implements --list/-L, while reusing other
383d26
options such as --tls-creds for now designating how to connect
383d26
as the client (rather than their non-list usage of how to operate
383d26
as the server).
383d26
383d26
I debated about adding this functionality to something akin to
383d26
'qemu-img info' - but that tool does not readily lend itself
383d26
to connecting to an arbitrary NBD server without also tying to
383d26
a specific export (I may, however, still add ImageInfoSpecificNBD
383d26
for reporting the bitmaps available when connecting to a single
383d26
export).  And, while it may feel a bit odd that normally
383d26
qemu-nbd is a server but 'qemu-nbd -L' is a client, we are not
383d26
really making the qemu-nbd binary that much larger, because
383d26
'qemu-nbd -c' has to operate as both server and client
383d26
simultaneously across two threads when feeding the kernel module
383d26
for /dev/nbdN access.
383d26
383d26
Sample output:
383d26
$ qemu-nbd -L
383d26
exports available: 1
383d26
 export: ''
383d26
  size:  65536
383d26
  flags: 0x4ed ( flush fua trim zeroes df cache )
383d26
  min block: 512
383d26
  opt block: 4096
383d26
  max block: 33554432
383d26
  available meta contexts: 1
383d26
   base:allocation
383d26
383d26
Note that the output only lists sizes if the server sent
383d26
NBD_FLAG_HAS_FLAGS, because a newstyle server does not give
383d26
the size otherwise.  It has the side effect that for really
383d26
old servers that did not send any flags, the size is not
383d26
output even though it was available.  However, I'm not too
383d26
concerned about that - oldstyle servers are (rightfully)
383d26
getting less common to encounter (qemu 3.0 was the last
383d26
version where we even serve it), and most existing servers
383d26
that still even offer oldstyle negotiation (such as nbdkit)
383d26
still send flags (since that was added to the NBD protocol
383d26
in 2007 to permit read-only connections).
383d26
383d26
Not done here, but maybe worth future experiments: capture
383d26
the meat of NBDExportInfo into a QAPI struct, and use the
383d26
generated QAPI pretty-printers instead of hand-rolling our
383d26
output loop.  It would also permit us to add a JSON output
383d26
mode for machine parsing.
383d26
383d26
Signed-off-by: Eric Blake <eblake@redhat.com>
383d26
Reviewed-by: Richard W.M. Jones <rjones@redhat.com>
383d26
Message-Id: <20190117193658.16413-20-eblake@redhat.com>
383d26
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
383d26
(cherry picked from commit 68b96f15838d309ef791cb83b5eec1bd7da271c2)
383d26
Signed-off-by: John Snow <jsnow@redhat.com>
383d26
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
383d26
---
383d26
 qemu-nbd.c    | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
383d26
 qemu-nbd.texi |  29 +++++++++--
383d26
 2 files changed, 167 insertions(+), 17 deletions(-)
383d26
383d26
diff --git a/qemu-nbd.c b/qemu-nbd.c
383d26
index 3c53870..1f7b2a0 100644
383d26
--- a/qemu-nbd.c
383d26
+++ b/qemu-nbd.c
383d26
@@ -76,7 +76,8 @@ static void usage(const char *name)
383d26
 {
383d26
     (printf) (
383d26
 "Usage: %s [OPTIONS] FILE\n"
383d26
-"QEMU Disk Network Block Device Server\n"
383d26
+"  or:  %s -L [OPTIONS]\n"
383d26
+"QEMU Disk Network Block Device Utility\n"
383d26
 "\n"
383d26
 "  -h, --help                display this help and exit\n"
383d26
 "  -V, --version             output version information and exit\n"
383d26
@@ -98,6 +99,7 @@ static void usage(const char *name)
383d26
 "  -B, --bitmap=NAME         expose a persistent dirty bitmap\n"
383d26
 "\n"
383d26
 "General purpose options:\n"
383d26
+"  -L, --list                list exports available from another NBD server\n"
383d26
 "  --object type,id=ID,...   define an object such as 'secret' for providing\n"
383d26
 "                            passwords and/or encryption keys\n"
383d26
 "  --tls-creds=ID            use id of an earlier --object to provide TLS\n"
383d26
@@ -131,7 +133,7 @@ static void usage(const char *name)
383d26
 "      --image-opts          treat FILE as a full set of image options\n"
383d26
 "\n"
383d26
 QEMU_HELP_BOTTOM "\n"
383d26
-    , name, NBD_DEFAULT_PORT, "DEVICE");
383d26
+    , name, name, NBD_DEFAULT_PORT, "DEVICE");
383d26
 }
383d26
 
383d26
 static void version(const char *name)
383d26
@@ -243,6 +245,91 @@ static void termsig_handler(int signum)
383d26
 }
383d26
 
383d26
 
383d26
+static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls,
383d26
+                                const char *hostname)
383d26
+{
383d26
+    int ret = EXIT_FAILURE;
383d26
+    int rc;
383d26
+    Error *err = NULL;
383d26
+    QIOChannelSocket *sioc;
383d26
+    NBDExportInfo *list;
383d26
+    int i, j;
383d26
+
383d26
+    sioc = qio_channel_socket_new();
383d26
+    if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) {
383d26
+        error_report_err(err);
383d26
+        return EXIT_FAILURE;
383d26
+    }
383d26
+    rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list,
383d26
+                                 &err;;
383d26
+    if (rc < 0) {
383d26
+        if (err) {
383d26
+            error_report_err(err);
383d26
+        }
383d26
+        goto out;
383d26
+    }
383d26
+    printf("exports available: %d\n", rc);
383d26
+    for (i = 0; i < rc; i++) {
383d26
+        printf(" export: '%s'\n", list[i].name);
383d26
+        if (list[i].description && *list[i].description) {
383d26
+            printf("  description: %s\n", list[i].description);
383d26
+        }
383d26
+        if (list[i].flags & NBD_FLAG_HAS_FLAGS) {
383d26
+            printf("  size:  %" PRIu64 "\n", list[i].size);
383d26
+            printf("  flags: 0x%x (", list[i].flags);
383d26
+            if (list[i].flags & NBD_FLAG_READ_ONLY) {
383d26
+                printf(" readonly");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_FLUSH) {
383d26
+                printf(" flush");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_FUA) {
383d26
+                printf(" fua");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_ROTATIONAL) {
383d26
+                printf(" rotational");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_TRIM) {
383d26
+                printf(" trim");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) {
383d26
+                printf(" zeroes");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_DF) {
383d26
+                printf(" df");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) {
383d26
+                printf(" multi");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_RESIZE) {
383d26
+                printf(" resize");
383d26
+            }
383d26
+            if (list[i].flags & NBD_FLAG_SEND_CACHE) {
383d26
+                printf(" cache");
383d26
+            }
383d26
+            printf(" )\n");
383d26
+        }
383d26
+        if (list[i].min_block) {
383d26
+            printf("  min block: %u\n", list[i].min_block);
383d26
+            printf("  opt block: %u\n", list[i].opt_block);
383d26
+            printf("  max block: %u\n", list[i].max_block);
383d26
+        }
383d26
+        if (list[i].n_contexts) {
383d26
+            printf("  available meta contexts: %d\n", list[i].n_contexts);
383d26
+            for (j = 0; j < list[i].n_contexts; j++) {
383d26
+                printf("   %s\n", list[i].contexts[j]);
383d26
+            }
383d26
+        }
383d26
+    }
383d26
+    nbd_free_export_list(list, rc);
383d26
+
383d26
+    ret = EXIT_SUCCESS;
383d26
+ out:
383d26
+    object_unref(OBJECT(sioc));
383d26
+    return ret;
383d26
+}
383d26
+
383d26
+
383d26
 #if HAVE_NBD_DEVICE
383d26
 static void *show_parts(void *arg)
383d26
 {
383d26
@@ -425,7 +512,8 @@ static QemuOptsList qemu_object_opts = {
383d26
 
383d26
 
383d26
 
383d26
-static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
383d26
+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
383d26
+                                          Error **errp)
383d26
 {
383d26
     Object *obj;
383d26
     QCryptoTLSCreds *creds;
383d26
@@ -445,10 +533,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
383d26
         return NULL;
383d26
     }
383d26
 
383d26
-    if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
383d26
-        error_setg(errp,
383d26
-                   "Expecting TLS credentials with a server endpoint");
383d26
-        return NULL;
383d26
+    if (list) {
383d26
+        if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
383d26
+            error_setg(errp,
383d26
+                       "Expecting TLS credentials with a client endpoint");
383d26
+            return NULL;
383d26
+        }
383d26
+    } else {
383d26
+        if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
383d26
+            error_setg(errp,
383d26
+                       "Expecting TLS credentials with a server endpoint");
383d26
+            return NULL;
383d26
+        }
383d26
     }
383d26
     object_ref(obj);
383d26
     return creds;
383d26
@@ -471,7 +567,8 @@ static void setup_address_and_port(const char **address, const char **port)
383d26
 static const char *socket_activation_validate_opts(const char *device,
383d26
                                                    const char *sockpath,
383d26
                                                    const char *address,
383d26
-                                                   const char *port)
383d26
+                                                   const char *port,
383d26
+                                                   bool list)
383d26
 {
383d26
     if (device != NULL) {
383d26
         return "NBD device can't be set when using socket activation";
383d26
@@ -489,6 +586,10 @@ static const char *socket_activation_validate_opts(const char *device,
383d26
         return "TCP port number can't be set when using socket activation";
383d26
     }
383d26
 
383d26
+    if (list) {
383d26
+        return "List mode is incompatible with socket activation";
383d26
+    }
383d26
+
383d26
     return NULL;
383d26
 }
383d26
 
383d26
@@ -512,7 +613,7 @@ int main(int argc, char **argv)
383d26
     int64_t fd_size;
383d26
     QemuOpts *sn_opts = NULL;
383d26
     const char *sn_id_or_name = NULL;
383d26
-    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:";
383d26
+    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L";
383d26
     struct option lopt[] = {
383d26
         { "help", no_argument, NULL, 'h' },
383d26
         { "version", no_argument, NULL, 'V' },
383d26
@@ -525,6 +626,7 @@ int main(int argc, char **argv)
383d26
         { "bitmap", required_argument, NULL, 'B' },
383d26
         { "connect", required_argument, NULL, 'c' },
383d26
         { "disconnect", no_argument, NULL, 'd' },
383d26
+        { "list", no_argument, NULL, 'L' },
383d26
         { "snapshot", no_argument, NULL, 's' },
383d26
         { "load-snapshot", required_argument, NULL, 'l' },
383d26
         { "nocache", no_argument, NULL, 'n' },
383d26
@@ -559,7 +661,7 @@ int main(int argc, char **argv)
383d26
     Error *local_err = NULL;
383d26
     BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
383d26
     QDict *options = NULL;
383d26
-    const char *export_name = ""; /* Default export name */
383d26
+    const char *export_name = NULL; /* defaults to "" later for server mode */
383d26
     const char *export_description = NULL;
383d26
     const char *bitmap = NULL;
383d26
     const char *tlscredsid = NULL;
383d26
@@ -567,6 +669,7 @@ int main(int argc, char **argv)
383d26
     bool writethrough = true;
383d26
     char *trace_file = NULL;
383d26
     bool fork_process = false;
383d26
+    bool list = false;
383d26
     int old_stderr = -1;
383d26
     unsigned socket_activation;
383d26
 
383d26
@@ -760,13 +863,33 @@ int main(int argc, char **argv)
383d26
         case QEMU_NBD_OPT_FORK:
383d26
             fork_process = true;
383d26
             break;
383d26
+        case 'L':
383d26
+            list = true;
383d26
+            break;
383d26
         }
383d26
     }
383d26
 
383d26
-    if ((argc - optind) != 1) {
383d26
+    if (list) {
383d26
+        if (argc != optind) {
383d26
+            error_report("List mode is incompatible with a file name");
383d26
+            exit(EXIT_FAILURE);
383d26
+        }
383d26
+        if (export_name || export_description || dev_offset || partition ||
383d26
+            device || disconnect || fmt || sn_id_or_name || bitmap ||
383d26
+            seen_aio || seen_discard || seen_cache) {
383d26
+            error_report("List mode is incompatible with per-device settings");
383d26
+            exit(EXIT_FAILURE);
383d26
+        }
383d26
+        if (fork_process) {
383d26
+            error_report("List mode is incompatible with forking");
383d26
+            exit(EXIT_FAILURE);
383d26
+        }
383d26
+    } else if ((argc - optind) != 1) {
383d26
         error_report("Invalid number of arguments");
383d26
         error_printf("Try `%s --help' for more information.\n", argv[0]);
383d26
         exit(EXIT_FAILURE);
383d26
+    } else if (!export_name) {
383d26
+        export_name = "";
383d26
     }
383d26
 
383d26
     qemu_opts_foreach(&qemu_object_opts,
383d26
@@ -785,7 +908,8 @@ int main(int argc, char **argv)
383d26
     } else {
383d26
         /* Using socket activation - check user didn't use -p etc. */
383d26
         const char *err_msg = socket_activation_validate_opts(device, sockpath,
383d26
-                                                              bindto, port);
383d26
+                                                              bindto, port,
383d26
+                                                              list);
383d26
         if (err_msg != NULL) {
383d26
             error_report("%s", err_msg);
383d26
             exit(EXIT_FAILURE);
383d26
@@ -808,7 +932,7 @@ int main(int argc, char **argv)
383d26
             error_report("TLS is not supported with a host device");
383d26
             exit(EXIT_FAILURE);
383d26
         }
383d26
-        tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
383d26
+        tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err);
383d26
         if (local_err) {
383d26
             error_report("Failed to get TLS creds %s",
383d26
                          error_get_pretty(local_err));
383d26
@@ -816,6 +940,11 @@ int main(int argc, char **argv)
383d26
         }
383d26
     }
383d26
 
383d26
+    if (list) {
383d26
+        saddr = nbd_build_socket_address(sockpath, bindto, port);
383d26
+        return qemu_nbd_client_list(saddr, tlscreds, bindto);
383d26
+    }
383d26
+
383d26
 #if !HAVE_NBD_DEVICE
383d26
     if (disconnect || device) {
383d26
         error_report("Kernel /dev/nbdN support not available");
383d26
diff --git a/qemu-nbd.texi b/qemu-nbd.texi
383d26
index f218291..386bece 100644
383d26
--- a/qemu-nbd.texi
383d26
+++ b/qemu-nbd.texi
383d26
@@ -2,6 +2,8 @@
383d26
 @c man begin SYNOPSIS
383d26
 @command{qemu-nbd} [OPTION]... @var{filename}
383d26
 
383d26
+@command{qemu-nbd} @option{-L} [OPTION]...
383d26
+
383d26
 @command{qemu-nbd} @option{-d} @var{dev}
383d26
 @c man end
383d26
 @end example
383d26
@@ -14,6 +16,8 @@ Other uses:
383d26
 @itemize
383d26
 @item
383d26
 Bind a /dev/nbdX block device to a QEMU server (on Linux).
383d26
+@item
383d26
+As a client to query exports of a remote NBD server.
383d26
 @end itemize
383d26
 
383d26
 @c man end
383d26
@@ -31,13 +35,15 @@ See the @code{qemu(1)} manual page for full details of the properties
383d26
 supported. The common object types that it makes sense to define are the
383d26
 @code{secret} object, which is used to supply passwords and/or encryption
383d26
 keys, and the @code{tls-creds} object, which is used to supply TLS
383d26
-credentials for the qemu-nbd server.
383d26
+credentials for the qemu-nbd server or client.
383d26
 @item -p, --port=@var{port}
383d26
-The TCP port to listen on (default @samp{10809}).
383d26
+The TCP port to listen on as a server, or connect to as a client
383d26
+(default @samp{10809}).
383d26
 @item -o, --offset=@var{offset}
383d26
 The offset into the image.
383d26
 @item -b, --bind=@var{iface}
383d26
-The interface to bind to (default @samp{0.0.0.0}).
383d26
+The interface to bind to as a server, or connect to as a client
383d26
+(default @samp{0.0.0.0}).
383d26
 @item -k, --socket=@var{path}
383d26
 Use a unix socket with path @var{path}.
383d26
 @item --image-opts
383d26
@@ -97,10 +103,16 @@ Set the NBD volume export name (default of a zero-length string).
383d26
 @item -D, --description=@var{description}
383d26
 Set the NBD volume export description, as a human-readable
383d26
 string.
383d26
+@item -L, --list
383d26
+Connect as a client and list all details about the exports exposed by
383d26
+a remote NBD server.  This enables list mode, and is incompatible
383d26
+with options that change behavior related to a specific export (such as
383d26
+@option{--export-name}, @option{--offset}, ...).
383d26
 @item --tls-creds=ID
383d26
 Enable mandatory TLS encryption for the server by setting the ID
383d26
 of the TLS credentials object previously created with the --object
383d26
-option.
383d26
+option; or provide the credentials needed for connecting as a client
383d26
+in list mode.
383d26
 @item --fork
383d26
 Fork off the server process and exit the parent once the server is running.
383d26
 @item -v, --verbose
383d26
@@ -162,6 +174,15 @@ qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2
383d26
 qemu-nbd -d /dev/nbd0
383d26
 @end example
383d26
 
383d26
+Query a remote server to see details about what export(s) it is
383d26
+serving on port 10809, and authenticating via PSK:
383d26
+
383d26
+@example
383d26
+qemu-nbd \
383d26
+  --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \
383d26
+  --tls-creds tls0 -L -b remote.example.com
383d26
+@end example
383d26
+
383d26
 @c man end
383d26
 
383d26
 @ignore
383d26
-- 
383d26
1.8.3.1
383d26