[PATCH 0/4] Add progress bars
As discussed previously: https://www.redhat.com/archives/libguestfs/2010-July/msg00003.html https://www.redhat.com/archives/libguestfs/2010-July/msg00024.html To do: (1) Implement progress notifications for many more daemon operations. (2) OCaml bindings to the callback. (3) Perl bindings to the callback. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
There was a small bug in patch 1/4: it would always send a notification message at 100%, even for "short" (< 1 second) calls. This fixes the bug. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://et.redhat.com/~rjones/virt-df/
On 28/08/10 13:16, Richard W.M. Jones wrote:
From 978997bb41015eb85f009d4d48ca1716c84d9a50 Mon Sep 17 00:00:00 2001 From: Richard Jones<rjones@redhat.com> Date: Sat, 28 Aug 2010 10:33:24 +0100 Subject: [PATCH 1/4] Implement progress messages in the daemon and library.
This implements progress notification messages in the daemon, and adds a callback in the library to handle them.
No calls are changed so far, so in fact no progress messages can be generated by this commit.
For more details, see: https://www.redhat.com/archives/libguestfs/2010-July/msg00003.html https://www.redhat.com/archives/libguestfs/2010-July/msg00024.html --- daemon/daemon.h | 7 ++++ daemon/proto.c | 88 +++++++++++++++++++++++++++++++++++++++++++++--- src/generator.ml | 21 +++++++++++- src/guestfs-internal.h | 2 + src/guestfs.c | 8 ++++ src/guestfs.h | 5 ++- src/guestfs.pod | 57 +++++++++++++++++++++++++++++++ src/proto.c | 37 ++++++++++++++++++-- 8 files changed, 215 insertions(+), 10 deletions(-)
--- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -1186,6 +1186,63 @@ languages (eg. if your HLL interpreter has already been cleaned up by the time this is called, and if your callback then jumps into some HLL function).
+=head2 guestfs_set_progress_callback + + typedef void (*guestfs_progress_cb) (guestfs_h *g, void *opaque, + int proc_nr, int serial, + uint64_t position, uint64_t total); + void guestfs_set_progress_callback (guestfs_h *g, + guestfs_progress_cb cb, + void *opaque); + +Some long-running operations can generate progress messages. If +this callback is registered, then it will be called each time a +progress message is generated (usually one second after the +operation started, and every second thereafter until it completes, +although the frequency may change in future versions). + +The callback receives two numbers: C<position> and C<total>. +The units of C<total> are not defined, although for some +operations C<total> may relate in some way to the amount of +data to be transferred (eg. in bytes or megabytes), and +C<position> may be the proportion which has been transferred.
s/proportion/(portion|amount|total)/
+The only defined and stable parts of the API are: + +=over 4 + +=item * + +The callback can display to the user some type of progress bar or +indicator which shows the ratio of C<position>:C<total>. + +=item * + +C<total> will be the same number for each progress message within +a single operation.
Is this restriction truly necessary? This api would be useful and well-defined with just the first and third restrictions. Allowing total to change over time is useful for estimated durations.
+=item * + +0 E<lt>= C<position> E<lt>= C<total> + +=item * + +If any progress notification is sent during a call, then a final +progress notification is always sent when C<position> = C<total>. + +This is to simplify caller code, so callers can easily set the +progress indicator to "100%" at the end of the operation, without +requiring special code to detect this case. + +=back + +The callback also receives the procedure number and serial number of +the call. These are only useful for debugging protocol issues, and +the callback can normally ignore them. The callback may want to +print these numbers in error messages or debugging messages. + +Progress callbacks only happen in the BUSY state. + =head1 BLOCK DEVICE NAMING
In the kernel there is now quite a profusion of schemata for naming diff --git a/src/proto.c b/src/proto.c index ad173c6..f9b5a33 100644 --- a/src/proto.c +++ b/src/proto.c @@ -373,17 +373,26 @@ guestfs___send_to_daemon (guestfs_h *g, const void *v_buf, size_t n) * * It also checks for EOF (qemu died) and passes that up through the * child_cleanup function above. + * + * Progress notifications are handled transparently by this function. + * If the callback exists, it is called. The caller of this function + * will not see GUESTFS_PROGRESS_FLAG. */ int guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) { fd_set rset, rset2;
+#define PROGRESS_MESSAGE_SIZE 24
We really should be calculating this value automatically somehow. Is there any structure we can measure the size of?
+#define message_size() \ + (*size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE) + if (g->verbose) fprintf (stderr, "recv_from_daemon: %p g->state = %d, size_rtn = %p, buf_rtn = %p\n", g, g->state, size_rtn, buf_rtn);
+ again: FD_ZERO (&rset);
FD_SET (g->fd[1],&rset); /* Read qemu stdout for log messages& EOF. */ @@ -400,7 +409,7 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) */ ssize_t nr = -4;
- while (nr< (ssize_t) *size_rtn) { + while (nr< (ssize_t) message_size()) {
That loop test is way too complex. This could be rewritten as: for (;;) { ssize_t message_size = *size_rtn != GUESTFS_PROGRESS_FLAG ? *size_rtn : PROGRESS_MESSAGE_SIZE; if (nr < message_size) break; This would also avoid the unsightly #define, be a bit nicer to the compiler, and re-use the value below.
rset2 = rset; int r = select (max_fd+1,&rset2, NULL, NULL, NULL); if (r == -1) { @@ -463,6 +472,8 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } else if (*size_rtn == GUESTFS_CANCEL_FLAG) return 0; + else if (*size_rtn == GUESTFS_PROGRESS_FLAG) + /*FALLTHROUGH*/; /* If this happens, it's pretty bad and we've probably lost * synchronization. */ @@ -473,11 +484,11 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) }
/* Allocate the complete buffer, size now known. */ - *buf_rtn = safe_malloc (g, *size_rtn); + *buf_rtn = safe_malloc (g, message_size()); /*FALLTHROUGH*/ }
- size_t sizetoread = *size_rtn - nr; + size_t sizetoread = message_size() - nr; if (sizetoread> BUFSIZ) sizetoread = BUFSIZ;
r = read (g->sock, (char *) (*buf_rtn) + nr, sizetoread); @@ -524,6 +535,26 @@ guestfs___recv_from_daemon (guestfs_h *g, uint32_t *size_rtn, void **buf_rtn) } #endif
+ if (*size_rtn == GUESTFS_PROGRESS_FLAG) { + if (g->state == BUSY&& g->progress_cb) { + guestfs_progress message; + XDR xdr; + xdrmem_create (&xdr, *buf_rtn, PROGRESS_MESSAGE_SIZE, XDR_DECODE); + xdr_guestfs_progress (&xdr,&message); + xdr_destroy (&xdr); + + g->progress_cb (g, g->progress_cb_data, + message.proc, message.serial, + message.position, message.total); + } + + free (*buf_rtn); + *buf_rtn = NULL; + + /* Process next message. */ + goto again;
This is nasty. You could just return guestfs___recv_from_daemon, called recursively. I think gcc is pretty efficient with the stack in this case.
+ } + return 0; }
-- 1.7.1
-- Matthew Booth, RHCA, RHCSS Red Hat Engineering, Virtualisation Team GPG ID: D33C3490 GPG FPR: 3733 612D 2D05 5458 8A8A 1600 3441 EA19 D33C 3490
On Tue, Aug 31, 2010 at 11:20:42AM +0100, Matthew Booth wrote:
+#define PROGRESS_MESSAGE_SIZE 24
We really should be calculating this value automatically somehow. Is there any structure we can measure the size of?
Not that I'm aware of. However the size of the wire format (which is what we are measuring here) is defined in several RFCs and effectively unchangable, provided the struct itself doesn't change. I will make all the other changes in the revised patch, coming up. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://et.redhat.com/~rjones/libguestfs/ See what it can do: http://et.redhat.com/~rjones/libguestfs/recipes.html
This is an example of how to add progress notifications to one of the daemon commands. Item (1) on the to-do list is to go through the remaining daemon commands and add progress notifications to the easy ones. (Item (94c) is to add it to the hard ones ...) Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming blog: http://rwmj.wordpress.com Fedora now supports 80 OCaml packages (the OPEN alternative to F#) http://cocan.org/getting_started_with_ocaml_on_red_hat_and_fedora
ZX81-stylie graphics. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v
participants (2)
-
Matthew Booth -
Richard W.M. Jones