[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v3 17/19] qemu-nbd: Add --list option
From: |
Vladimir Sementsov-Ogievskiy |
Subject: |
Re: [Qemu-devel] [PATCH v3 17/19] qemu-nbd: Add --list option |
Date: |
Thu, 17 Jan 2019 10:05:56 +0000 |
12.01.2019 20:58, Eric Blake wrote:
> We want to be able to detect whether a given qemu NBD server is
> exposing the right export(s) and dirty bitmaps, at least for
> regression testing. We could use 'nbd-client -l' from the upstream
> NBD project to list exports, but it's annoying to rely on
> out-of-tree binaries; furthermore, nbd-client doesn't necessarily
> know about all of the qemu NBD extensions. Thus, it is time to add
> a new mode to qemu-nbd that merely sniffs all possible information
> from the server during handshake phase, then disconnects and dumps
> the information.
>
> This patch actually implements --list/-L, while reusing other
> options such as --tls-creds for now designating how to connect
> as the client (rather than their non-list usage of how to operate
> as the server).
>
> I debated about adding this functionality to something akin to
> 'qemu-img info' - but that tool does not readily lend itself
> to connecting to an arbitrary NBD server without also tying to
> a specific export (I may, however, still add ImageInfoSpecificNBD
> for reporting the bitmaps available when connecting to a single
> export). And, while it may feel a bit odd that normally
> qemu-nbd is a server but 'qemu-nbd -L' is a client, we are not
> really making the qemu-nbd binary that much larger, because
> 'qemu-nbd -c' has to operate as both server and client
> simultaneously across two threads when feeding the kernel module
> for /dev/nbdN access.
>
> Sample output:
> $ qemu-nbd -L
> exports available: 1
> export: ''
> size: 65536
> flags: 0x4ed ( flush fua trim zeroes df cache )
> min block: 512
> opt block: 4096
> max block: 33554432
> available meta contexts: 1
> base:allocation
>
> Note that the output only lists sizes if the server sent
> NBD_FLAG_HAS_FLAGS, because a newstyle server does not give
> the size otherwise. It has the side effect that for really
> old servers that did not send any flags, the size is not
> output even though it was available. However, I'm not too
> concerned about that - oldstyle servers are (rightfully)
> getting less common to encounter (qemu 3.0 was the last
> version where we even serve it), and most existing servers
> that still even offer oldstyle negotiation (such as nbdkit)
> still send flags (since that was added to the NBD protocol
> in 2007 to permit read-only connections).
>
> Not done here, but maybe worth future experiments: capture
> the meat of NBDExportInfo into a QAPI struct, and use the
> generated QAPI pretty-printers instead of hand-rolling our
> output loop. It would also permit us to add a JSON output
> mode for machine parsing.
>
> Signed-off-by: Eric Blake <address@hidden>
> Message-Id: <address@hidden>
> Reviewed-by: Richard W.M. Jones <address@hidden>
>
> ---
> v3: comment tweak [Rich], rebase to earlier changes
> ---
> qemu-nbd.texi | 27 +++++++--
> qemu-nbd.c | 155 +++++++++++++++++++++++++++++++++++++++++++++-----
> 2 files changed, 165 insertions(+), 17 deletions(-)
>
> diff --git a/qemu-nbd.texi b/qemu-nbd.texi
> index 3f22559beb4..65caeb7874a 100644
> --- a/qemu-nbd.texi
> +++ b/qemu-nbd.texi
> @@ -2,6 +2,8 @@
> @c man begin SYNOPSIS
> @command{qemu-nbd} [OPTION]... @var{filename}
>
> address@hidden @option{-L} [OPTION]...
> +
> @command{qemu-nbd} @option{-d} @var{dev}
> @c man end
> @end example
> @@ -14,6 +16,8 @@ Other uses:
> @itemize
> @item
> Bind a /dev/nbdX block device to a QEMU server (on Linux).
> address@hidden
> +As a client to query exports of a remote NBD server.
> @end itemize
>
> @c man end
> @@ -31,13 +35,15 @@ See the @code{qemu(1)} manual page for full details of
> the properties
> supported. The common object types that it makes sense to define are the
> @code{secret} object, which is used to supply passwords and/or encryption
> keys, and the @code{tls-creds} object, which is used to supply TLS
> -credentials for the qemu-nbd server.
> +credentials for the qemu-nbd server or client.
> @item -p, address@hidden
> -The TCP port to listen on (default @samp{10809}).
> +The TCP port to listen on as a server, or connect to as a client
> +(default @samp{10809}).
> @item -o, address@hidden
> The offset into the image.
> @item -b, address@hidden
> -The interface to bind to (default @samp{0.0.0.0}).
> +The interface to bind to as a server, or connect to as a client
> +(default @samp{0.0.0.0}).
> @item -k, address@hidden
> Use a unix socket with path @var{path}.
> @item --image-opts
> @@ -97,10 +103,14 @@ Set the NBD volume export name (default of a zero-length
> string).
> @item -D, address@hidden
> Set the NBD volume export description, as a human-readable
> string.
> address@hidden -L, --list
> +Connect as a client and list all details about the exports exposed by
> +a remote NBD server.
> @item --tls-creds=ID
> Enable mandatory TLS encryption for the server by setting the ID
> of the TLS credentials object previously created with the --object
> -option.
> +option; or provide the credentials needed for connecting as a client
> +in list mode.
may be "list mode (--list)", as "list mode" is not directly defined. On the
other hand,
list option is extremely close to tls-creds, so it is obvious anyway.
> @item --fork
> Fork off the server process and exit the parent once the server is running.
> @item -v, --verbose
> @@ -159,6 +169,15 @@ qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2
> qemu-nbd -d /dev/nbd0
> @end example
>
> +Query a remote server to see details about what export(s) it is
> +serving on port 10809, and authenticating via PSK:
> +
> address@hidden
> +qemu-nbd \
> + --object
> tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \
> + --tls-creds tls0 -L -b remote.example.com
> address@hidden example
> +
> @c man end
>
> @ignore
> diff --git a/qemu-nbd.c b/qemu-nbd.c
> index f1c24683129..daccb86d0d7 100644
> --- a/qemu-nbd.c
> +++ b/qemu-nbd.c
> @@ -76,7 +76,8 @@ static void usage(const char *name)
> {
> (printf) (
> "Usage: %s [OPTIONS] FILE\n"
> -"QEMU Disk Network Block Device Server\n"
> +" or: %s -L [OPTIONS]\n"
> +"QEMU Disk Network Block Device Utility\n"
> "\n"
> " -h, --help display this help and exit\n"
> " -V, --version output version information and exit\n"
> @@ -98,6 +99,7 @@ static void usage(const char *name)
> " -B, --bitmap=NAME expose a persistent dirty bitmap\n"
> "\n"
> "General purpose options:\n"
> +" -L, --list list exports available from another NBD
> server\n"
> " --object type,id=ID,... define an object such as 'secret' for
> providing\n"
> " passwords and/or encryption keys\n"
> " --tls-creds=ID use id of an earlier --object to provide TLS\n"
> @@ -131,7 +133,7 @@ static void usage(const char *name)
> " --image-opts treat FILE as a full set of image options\n"
> "\n"
> QEMU_HELP_BOTTOM "\n"
> - , name, NBD_DEFAULT_PORT, "DEVICE");
> + , name, name, NBD_DEFAULT_PORT, "DEVICE");
> }
>
> static void version(const char *name)
> @@ -243,6 +245,92 @@ static void termsig_handler(int signum)
> }
>
>
> +static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls,
> + const char *hostname)
> +{
> + int ret = EXIT_FAILURE;
> + int rc;
> + Error *err = NULL;
> + QIOChannelSocket *sioc;
> + NBDExportInfo *list;
> + int i, j;
> +
> + sioc = qio_channel_socket_new();
> + if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) {
> + error_report_err(err);
> + goto out;
May be just return EXIT_FAUILURE here;
remove out label;
s/out_socket/out
> + }
> + rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list,
> + &err);
> + if (rc < 0) {
> + if (err) {
> + error_report_err(err);
> + }
> + goto out_socket;
> + }
> + printf("exports available: %d\n", rc);
> + for (i = 0; i < rc; i++) {
> + printf(" export: '%s'\n", list[i].name);
> + if (list[i].description && *list[i].description) {
> + printf(" description: %s\n", list[i].description);
> + }
> + if (list[i].flags & NBD_FLAG_HAS_FLAGS) {
actually this is
if (server not have a bug of not setting NBD_FLAG_HAS_FLAGS) {
...
}
Why not to print @size for example, if @flags field has a bug?
Or, then, why to print flags, if @size has a bug?
> + printf(" size: %" PRIu64 "\n", list[i].size);
> + printf(" flags: 0x%x (", list[i].flags);
> + if (list[i].flags & NBD_FLAG_READ_ONLY) {
> + printf(" readonly");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_FLUSH) {
> + printf(" flush");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_FUA) {
> + printf(" fua");
> + }
> + if (list[i].flags & NBD_FLAG_ROTATIONAL) {
> + printf(" rotational");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_TRIM) {
> + printf(" trim");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) {
> + printf(" zeroes");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_DF) {
> + printf(" df");
> + }
> + if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) {
> + printf(" multi");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_RESIZE) {
> + printf(" resize");
> + }
> + if (list[i].flags & NBD_FLAG_SEND_CACHE) {
> + printf(" cache");
> + }
> + printf(" )\n");
> + }
> + if (list[i].min_block) {
> + printf(" min block: %u\n", list[i].min_block);
> + printf(" opt block: %u\n", list[i].opt_block);
> + printf(" max block: %u\n", list[i].max_block);
> + }
> + if (list[i].n_contexts) {
> + printf(" available meta contexts: %d\n", list[i].n_contexts);
> + for (j = 0; j < list[i].n_contexts; j++) {
> + printf(" %s\n", list[i].contexts[j]);
> + }
> + }
> + }
> + nbd_free_export_list(list, rc);
> +
> + ret = EXIT_SUCCESS;
> + out_socket:
> + object_unref(OBJECT(sioc));
> + out:
> + return ret;
> +}
> +
> +
> #if HAVE_NBD_DEVICE
> static void *show_parts(void *arg)
> {
> @@ -425,7 +513,8 @@ static QemuOptsList qemu_object_opts = {
>
>
>
> -static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
> +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list,
> + Error **errp)
> {
> Object *obj;
> QCryptoTLSCreds *creds;
> @@ -445,10 +534,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char
> *id, Error **errp)
> return NULL;
> }
>
> - if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> - error_setg(errp,
> - "Expecting TLS credentials with a server endpoint");
> - return NULL;
> + if (list) {
> + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) {
> + error_setg(errp,
> + "Expecting TLS credentials with a client endpoint");
> + return NULL;
> + }
> + } else {
> + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
> + error_setg(errp,
> + "Expecting TLS credentials with a server endpoint");
> + return NULL;
> + }
> }
> object_ref(obj);
> return creds;
> @@ -471,7 +568,8 @@ static void setup_address_and_port(const char **address,
> const char **port)
> static const char *socket_activation_validate_opts(const char *device,
> const char *sockpath,
> const char *address,
> - const char *port)
> + const char *port,
> + bool list)
> {
> if (device != NULL) {
> return "NBD device can't be set when using socket activation";
> @@ -489,6 +587,10 @@ static const char *socket_activation_validate_opts(const
> char *device,
> return "TCP port number can't be set when using socket activation";
> }
>
> + if (list) {
> + return "List mode is incompatible with socket activation";
> + }
> +
> return NULL;
> }
>
> @@ -512,7 +614,7 @@ int main(int argc, char **argv)
> int64_t fd_size;
> QemuOpts *sn_opts = NULL;
> const char *sn_id_or_name = NULL;
> - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:";
> + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L";
> struct option lopt[] = {
> { "help", no_argument, NULL, 'h' },
> { "version", no_argument, NULL, 'V' },
> @@ -525,6 +627,7 @@ int main(int argc, char **argv)
> { "bitmap", required_argument, NULL, 'B' },
> { "connect", required_argument, NULL, 'c' },
> { "disconnect", no_argument, NULL, 'd' },
> + { "list", no_argument, NULL, 'L' },
> { "snapshot", no_argument, NULL, 's' },
> { "load-snapshot", required_argument, NULL, 'l' },
> { "nocache", no_argument, NULL, 'n' },
> @@ -559,7 +662,7 @@ int main(int argc, char **argv)
> Error *local_err = NULL;
> BlockdevDetectZeroesOptions detect_zeroes =
> BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;
> QDict *options = NULL;
> - const char *export_name = ""; /* Default export name */
> + const char *export_name = NULL; /* defaults to "" later for server mode
> */
> const char *export_description = NULL;
> const char *bitmap = NULL;
> const char *tlscredsid = NULL;
> @@ -567,6 +670,7 @@ int main(int argc, char **argv)
> bool writethrough = true;
> char *trace_file = NULL;
> bool fork_process = false;
> + bool list = false;
> int old_stderr = -1;
> unsigned socket_activation;
>
> @@ -760,13 +864,32 @@ int main(int argc, char **argv)
> case QEMU_NBD_OPT_FORK:
> fork_process = true;
> break;
> + case 'L':
> + list = true;
> + break;
> }
> }
>
> - if ((argc - optind) != 1) {
> + if (list) {
> + if (argc != optind) {
> + error_report("List mode is incompatible with a file name");
> + exit(EXIT_FAILURE);
> + }
> + if (export_name || export_description || dev_offset || partition ||
> + device || disconnect || fmt || sn_id_or_name || bitmap) {
> + error_report("List mode is incompatible with per-device
> settings");
> + exit(EXIT_FAILURE);
and what about aio, discard, etc? Also, I think, it would be good to specify in
Usage
(or in man), which options are available for list mode.
Hm, note, --help has grouping of options and man don't.
> + }
> + if (fork_process) {
> + error_report("List mode is incompatible with forking");
> + exit(EXIT_FAILURE);
> + }
> + } else if ((argc - optind) != 1) {
> error_report("Invalid number of arguments");
> error_printf("Try `%s --help' for more information.\n", argv[0]);
> exit(EXIT_FAILURE);
> + } else if (!export_name) {
> + export_name = "";
> }
>
> qemu_opts_foreach(&qemu_object_opts,
> @@ -785,7 +908,8 @@ int main(int argc, char **argv)
> } else {
> /* Using socket activation - check user didn't use -p etc. */
> const char *err_msg = socket_activation_validate_opts(device,
> sockpath,
> - bindto, port);
> + bindto, port,
> + list);
> if (err_msg != NULL) {
> error_report("%s", err_msg);
> exit(EXIT_FAILURE);
> @@ -808,7 +932,7 @@ int main(int argc, char **argv)
> error_report("TLS is not supported with a host device");
> exit(EXIT_FAILURE);
> }
> - tlscreds = nbd_get_tls_creds(tlscredsid, &local_err);
> + tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err);
> if (local_err) {
> error_report("Failed to get TLS creds %s",
> error_get_pretty(local_err));
> @@ -816,6 +940,11 @@ int main(int argc, char **argv)
> }
> }
>
> + if (list) {
> + saddr = nbd_build_socket_address(sockpath, bindto, port);
> + return qemu_nbd_client_list(saddr, tlscreds, bindto);
> + }
> +
> #if !HAVE_NBD_DEVICE
> if (disconnect || device) {
> error_report("Kernel /dev/nbdN support not available");
>
I don't have good understanding of tls related things, the rest looks OK,
my suggestions are optional, so, if you don't want to improve docs and
option conflict checking now:
Reviewed-by: Vladimir Sementsov-Ogievskiy <address@hidden>
--
Best regards,
Vladimir