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 -=| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list