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

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