[Libvir] RFC: PATCH 3/5: verbose job progress in virsh

Daniel P. Berrange berrange at redhat.com
Sat Jan 5 01:09:46 UTC 2008


This patch updates virsh to make use of any APIs with
new *Job variants. This means the 'create', 'start',
'save', 'restore', 'dump', 'net-create' and 'net-start'
methods all now take a '--verbose' option. If this
option is given, it will invoke the *Job variant of
the API and print out increment progress information.

eg, As an example of a job which has a bounded time,

   $ ./virsh --connect test:///default save --verbose test foo
   save [=====       18%          ] Duration: 9 s, ETA: 41 s
   error: Cancelled domain save operation

eg, As an example which is unbounded

   $ ./virsh --connect test:///default save --verbose test foo
   save [         =               ] Duration: 9 s
   error: Cancelled domain save operation

In the latter case, the '=' will bounce back & forth while
the job is running.

Both cases illustrate how the 'Ctrl-C' / SIGINT handler is
hooked up such that 'virJobCancel' is run. Pressing Ctrl-C
twice in quick succession will still immediately exit the
program - useful if cancellation fails for some reason.


 virsh.c |  405 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 356 insertions(+), 49 deletions(-)

Dan.

diff -r ba58ad7f9763 src/virsh.c
--- a/src/virsh.c	Fri Jan 04 18:00:06 2008 -0500
+++ b/src/virsh.c	Fri Jan 04 18:33:32 2008 -0500
@@ -36,6 +36,7 @@
 #include <sys/stat.h>
 #include <inttypes.h>
 #include <test.h>
+#include <signal.h>
 
 #include <libxml/parser.h>
 #include <libxml/tree.h>
@@ -50,6 +51,8 @@
 #include "console.h"
 
 static char *progname;
+
+static int interrupted = 0;
 
 #ifndef TRUE
 #define TRUE 1
@@ -302,6 +305,84 @@ static int namesorter(const void *a, con
 }
 
 
+static void sigint_handler(int sig ATTRIBUTE_UNUSED) {
+    if (interrupted) {
+        _exit(255);
+    }
+    interrupted = 1;
+}
+
+/*
+ * Helper for commands running with an async job
+ */
+static int
+cmdMonitorProgress(vshControl *ctl, vshCmd *cmd, virJobPtr job, virJobInfoPtr info)
+{
+    int tick = 0;
+    int tickStep = 1;
+    /* Save cursor position */
+    fprintf(stdout, "\x1b[s");
+    fflush(stdout);
+
+    do {
+        if (virJobGetInfo(job, info) < 0) {
+            vshError(ctl, FALSE, _("Failed to get job status"));
+            return -1;
+        }
+
+        if (info->state == VIR_JOB_RUNNING) {
+            /* Restore cursor position and clear current line */
+            fprintf(stdout, "\x1b[u\x1b[K");
+
+            if (info->type == VIR_JOB_UNBOUNDED) {
+                int i;
+                char progress[26];
+                for (i = 0 ; i < 25 ; i++) {
+                    if (i == tick)
+                        progress[i] = '=';
+                    else
+                        progress[i] = ' ';
+                }
+                progress[25] = '\0';
+
+                fprintf(stdout, "%s [%s] Duration: %d s",
+                        cmd->def->name, progress, info->runningTime);
+            } else {
+                int i;
+                char progress[26];
+                for (i = 0 ; i < 25 ; i++) {
+                    if (i <= (info->percentComplete/4))
+                        progress[i] = '=';
+                    else
+                        progress[i] = ' ';
+                }
+                if (info->percentComplete > 99)
+                    progress[11] = '0' + (info->percentComplete / 100);
+                if (info->percentComplete > 9)
+                    progress[12] = '0' + ((info->percentComplete % 100) / 10);
+                progress[13] = '0' + (info->percentComplete % 10);
+                progress[14] = '%';
+                progress[25] = '\0';
+
+                fprintf(stdout, "%s [%s] Duration: %d s, ETA: %d s",
+                        cmd->def->name, progress, info->runningTime, info->remainingTime);
+            }
+            fflush(stdout);
+        }
+        usleep(100 * 1000);
+        tick += tickStep;
+        if (tick == 24 || tick == 0)
+            tickStep *= -1;
+
+        if (interrupted) {
+            virJobCancel(job);
+        }
+    } while (info->state == VIR_JOB_RUNNING);
+    interrupted = 0;
+    fprintf(stdout, "\n");
+    return 0;
+}
+
 /* ---------------
  * Commands
  * ---------------
@@ -840,6 +921,7 @@ static vshCmdInfo info_create[] = {
 
 static vshCmdOptDef opts_create[] = {
     {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML domain description")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -906,11 +988,11 @@ static int
 static int
 cmdCreate(vshControl * ctl, vshCmd * cmd)
 {
-    virDomainPtr dom;
     char *from;
     int found;
     int ret = TRUE;
     char *buffer;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -922,17 +1004,48 @@ cmdCreate(vshControl * ctl, vshCmd * cmd
     buffer = readFile (ctl, from);
     if (buffer == NULL) return FALSE;
 
-    dom = virDomainCreateLinux(ctl->conn, buffer, 0);
-    free (buffer);
-
-    if (dom != NULL) {
-        vshPrint(ctl, _("Domain %s created from %s\n"),
-                 virDomainGetName(dom), from);
-	virDomainFree(dom);
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+
+        job = virDomainCreateLinuxJob(ctl->conn, buffer, 0);
+        free(buffer);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to create domain from %s"), from);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virJobDestroy(job);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            virDomainPtr dom = virJobGetDomain(job);
+            vshPrint(ctl, _("Domain %s created from %s\n"),
+                     virDomainGetName(dom), from);
+            virDomainFree(dom);
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled domain create operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to create domain from %s"), from);
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to create domain from %s"), from);
-        ret = FALSE;
-    }
+        virDomainPtr dom = virDomainCreateLinux(ctl->conn, buffer, 0);
+        free(buffer);
+        if (dom != NULL) {
+            vshPrint(ctl, _("Domain %s created from %s\n"),
+                     virDomainGetName(dom), from);
+            virDomainFree(dom);
+        } else {
+            vshError(ctl, FALSE, _("Failed to create domain from %s"), from);
+            ret = FALSE;
+        }
+    }
+
     return ret;
 }
 
@@ -1036,6 +1149,7 @@ static vshCmdInfo info_start[] = {
 
 static vshCmdOptDef opts_start[] = {
     {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive domain")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -1044,6 +1158,7 @@ cmdStart(vshControl * ctl, vshCmd * cmd)
 {
     virDomainPtr dom;
     int ret = TRUE;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -1053,17 +1168,49 @@ cmdStart(vshControl * ctl, vshCmd * cmd)
 
     if (virDomainGetID(dom) != (unsigned int)-1) {
         vshError(ctl, FALSE, _("Domain is already active"));
-	virDomainFree(dom);
-        return FALSE;
-    }
-
-    if (virDomainCreate(dom) == 0) {
-        vshPrint(ctl, _("Domain %s started\n"),
-                 virDomainGetName(dom));
+        virDomainFree(dom);
+        return FALSE;
+    }
+
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+
+        job = virDomainCreateJob(dom, 0);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to start domain %s"),
+                     virDomainGetName(dom));
+            virDomainFree(dom);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virJobDestroy(job);
+            virDomainFree(dom);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            vshPrint(ctl, _("Domain %s started\n"),
+                     virDomainGetName(dom));
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled domain start operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to start domain %s"),
+                     virDomainGetName(dom));
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to start domain %s"),
-                 virDomainGetName(dom));
-        ret = FALSE;
+        if (virDomainCreate(dom) == 0) {
+            vshPrint(ctl, _("Domain %s started\n"),
+                     virDomainGetName(dom));
+        } else {
+            vshError(ctl, FALSE, _("Failed to start domain %s"),
+                     virDomainGetName(dom));
+            ret = FALSE;
+        }
     }
     virDomainFree(dom);
     return ret;
@@ -1082,6 +1229,7 @@ static vshCmdOptDef opts_save[] = {
 static vshCmdOptDef opts_save[] = {
     {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
     {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("where to save the data")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -1092,6 +1240,7 @@ cmdSave(vshControl * ctl, vshCmd * cmd)
     char *name;
     char *to;
     int ret = TRUE;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -1102,11 +1251,39 @@ cmdSave(vshControl * ctl, vshCmd * cmd)
     if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", &name)))
         return FALSE;
 
-    if (virDomainSave(dom, to) == 0) {
-        vshPrint(ctl, _("Domain %s saved to %s\n"), name, to);
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+        job = virDomainSaveJob(dom, to);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to);
+            virDomainFree(dom);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virDomainFree(dom);
+            virJobDestroy(job);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            vshPrint(ctl, _("Domain %s saved to %s\n"), name, to);
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled domain save operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to);
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to);
-        ret = FALSE;
+        if (virDomainSave(dom, to) == 0) {
+            vshPrint(ctl, _("Domain %s saved to %s\n"), name, to);
+        } else {
+            vshError(ctl, FALSE, _("Failed to save domain %s to %s"), name, to);
+            ret = FALSE;
+        }
     }
 
     virDomainFree(dom);
@@ -1277,6 +1454,7 @@ static vshCmdInfo info_restore[] = {
 
 static vshCmdOptDef opts_restore[] = {
     {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("the state to restore")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -1286,6 +1464,7 @@ cmdRestore(vshControl * ctl, vshCmd * cm
     char *from;
     int found;
     int ret = TRUE;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -1294,11 +1473,37 @@ cmdRestore(vshControl * ctl, vshCmd * cm
     if (!found)
         return FALSE;
 
-    if (virDomainRestore(ctl->conn, from) == 0) {
-        vshPrint(ctl, _("Domain restored from %s\n"), from);
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+        job = virDomainRestoreJob(ctl->conn, from);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to restore domain from %s"), from);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virJobDestroy(job);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            vshPrint(ctl, _("Domain restored from %s\n"),from);
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled domain restore operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to restore domain from %s"), from);
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to restore domain from %s"), from);
-        ret = FALSE;
+        if (virDomainRestore(ctl->conn, from) == 0) {
+            vshPrint(ctl, _("Domain restored from %s\n"), from);
+        } else {
+            vshError(ctl, FALSE, _("Failed to restore domain from %s"), from);
+            ret = FALSE;
+        }
     }
     return ret;
 }
@@ -1316,6 +1521,7 @@ static vshCmdOptDef opts_dump[] = {
 static vshCmdOptDef opts_dump[] = {
     {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")},
     {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("where to dump the core")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -1326,6 +1532,7 @@ cmdDump(vshControl * ctl, vshCmd * cmd)
     char *name;
     char *to;
     int ret = TRUE;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -1336,12 +1543,42 @@ cmdDump(vshControl * ctl, vshCmd * cmd)
     if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", &name)))
         return FALSE;
 
-    if (virDomainCoreDump(dom, to, 0) == 0) {
-        vshPrint(ctl, _("Domain %s dumpd to %s\n"), name, to);
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+        job = virDomainCoreDumpJob(dom, to, 0);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to core dump domain %s to %s"),
+                     name, to);
+            virDomainFree(dom);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virDomainFree(dom);
+            virJobDestroy(job);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            vshPrint(ctl, _("Domain %s dumped to %s\n"), name, to);
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled domain dump operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to core dump domain %s to %s"),
+                     name, to);
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to core dump domain %s to %s"),
-                 name, to);
-        ret = FALSE;
+        if (virDomainCoreDump(dom, to, 0) == 0) {
+            vshPrint(ctl, _("Domain %s dumped to %s\n"), name, to);
+        } else {
+            vshError(ctl, FALSE, _("Failed to core dump domain %s to %s"),
+                     name, to);
+            ret = FALSE;
+        }
     }
 
     virDomainFree(dom);
@@ -2338,6 +2575,7 @@ static vshCmdInfo info_network_create[] 
 
 static vshCmdOptDef opts_network_create[] = {
     {"file", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("file containing an XML network description")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -2349,6 +2587,7 @@ cmdNetworkCreate(vshControl * ctl, vshCm
     int found;
     int ret = TRUE;
     char *buffer;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -2360,15 +2599,46 @@ cmdNetworkCreate(vshControl * ctl, vshCm
     buffer = readFile (ctl, from);
     if (buffer == NULL) return FALSE;
 
-    network = virNetworkCreateXML(ctl->conn, buffer);
-    free (buffer);
-
-    if (network != NULL) {
-        vshPrint(ctl, _("Network %s created from %s\n"),
-                 virNetworkGetName(network), from);
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+
+        job = virNetworkCreateXMLJob(ctl->conn, buffer);
+        free(buffer);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to create network from %s"), from);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virJobDestroy(job);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            virNetworkPtr dom = virJobGetNetwork(job);
+            vshPrint(ctl, _("Network %s created from %s\n"),
+                     virNetworkGetName(dom), from);
+            virNetworkFree(dom);
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled network create operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to create network from %s"), from);
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to create network from %s"), from);
-        ret = FALSE;
+        network = virNetworkCreateXML(ctl->conn, buffer);
+        free (buffer);
+
+        if (network != NULL) {
+            vshPrint(ctl, _("Network %s created from %s\n"),
+                     virNetworkGetName(network), from);
+        } else {
+            vshError(ctl, FALSE, _("Failed to create network from %s"), from);
+            ret = FALSE;
+        }
     }
     return ret;
 }
@@ -2674,6 +2944,7 @@ static vshCmdInfo info_network_start[] =
 
 static vshCmdOptDef opts_network_start[] = {
     {"name", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("name of the inactive network")},
+    {"verbose", VSH_OT_BOOL, 0, gettext_noop("show progress information")},
     {NULL, 0, 0, NULL}
 };
 
@@ -2682,6 +2953,7 @@ cmdNetworkStart(vshControl * ctl, vshCmd
 {
     virNetworkPtr network;
     int ret = TRUE;
+    int verbose = vshCommandOptBool(cmd, "verbose");
 
     if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
         return FALSE;
@@ -2689,14 +2961,47 @@ cmdNetworkStart(vshControl * ctl, vshCmd
     if (!(network = vshCommandOptNetworkBy(ctl, cmd, "name", NULL, VSH_BYNAME)))
          return FALSE;
 
-    if (virNetworkCreate(network) == 0) {
-        vshPrint(ctl, _("Network %s started\n"),
-                 virNetworkGetName(network));
+    if (verbose) {
+        virJobPtr job;
+        virJobInfo info;
+
+        job = virNetworkCreateJob(network);
+        if (job == NULL) {
+            vshError(ctl, FALSE, _("Failed to start network %s"),
+                     virNetworkGetName(network));
+            virNetworkFree(network);
+            return FALSE;
+        }
+
+        if (cmdMonitorProgress(ctl,cmd, job, &info) < 0) {
+            virJobDestroy(job);
+            virNetworkFree(network);
+            return FALSE;
+        }
+
+        if (info.state == VIR_JOB_COMPLETE) {
+            vshPrint(ctl, _("Network %s started\n"),
+                     virNetworkGetName(network));
+        } else if (info.state == VIR_JOB_CANCELLED) {
+            vshError(ctl, FALSE, _("Cancelled network start operation"));
+            ret = FALSE;
+        } else {
+            vshError(ctl, FALSE, _("Failed to start network %s"),
+                     virNetworkGetName(network));
+            ret = FALSE;
+        }
+        virJobDestroy(job);
     } else {
-        vshError(ctl, FALSE, _("Failed to start network %s"),
-                 virNetworkGetName(network));
-        ret = FALSE;
-    }
+        if (virNetworkCreate(network) == 0) {
+            vshPrint(ctl, _("Network %s started\n"),
+                     virNetworkGetName(network));
+        } else {
+            vshError(ctl, FALSE, _("Failed to start network %s"),
+                     virNetworkGetName(network));
+            ret = FALSE;
+        }
+    }
+    virNetworkFree(network);
     return ret;
 }
 
@@ -5017,6 +5322,8 @@ main(int argc, char **argv)
         return -1;
     }
 
+    signal(SIGINT, sigint_handler);
+
     if (!(progname = strrchr(argv[0], '/')))
         progname = argv[0];
     else

-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 




More information about the libvir-list mailing list