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 -=|