From 99fa96c39075a56034055535a313b0590e8b09ec Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 14 Feb 2014 16:59:35 -0700 Subject: [PATCH] virsh: add event command, for lifecycle events Add 'virsh event --list' and 'virsh event [dom] --event=name [--loop] [--timeout]'. Borrows somewhat from event-test.c, but defaults to a one-shot notification, and takes advantage of the event loop integration to allow Ctrl-C to interrupt the wait for an event. For now, this just does lifecycle events. * tools/virsh.pod (event): Document new command. * tools/virsh-domain.c (vshDomainEventToString) (vshDomainEventDetailToString, vshDomEventData) (vshEventLifecyclePrint, cmdEvent): New struct and functions. Signed-off-by: Eric Blake --- tools/virsh-domain.c | 338 +++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 15 ++ 2 files changed, 353 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 03bbaabd0c..00ace11caa 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -10294,6 +10294,338 @@ cmdEdit(vshControl *ctl, const vshCmd *cmd) return ret; } + +/* + * "event" command + */ +static const char * +vshDomainEventToString(int event) +{ + const char *ret = _("unknown"); + switch ((virDomainEventType) event) { + case VIR_DOMAIN_EVENT_DEFINED: + ret = _("Defined"); + break; + case VIR_DOMAIN_EVENT_UNDEFINED: + ret = _("Undefined"); + break; + case VIR_DOMAIN_EVENT_STARTED: + ret = _("Started"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED: + ret = _("Suspended"); + break; + case VIR_DOMAIN_EVENT_RESUMED: + ret = _("Resumed"); + break; + case VIR_DOMAIN_EVENT_STOPPED: + ret = _("Stopped"); + break; + case VIR_DOMAIN_EVENT_SHUTDOWN: + ret = _("Shutdown"); + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED: + ret = _("PMSuspended"); + break; + case VIR_DOMAIN_EVENT_CRASHED: + ret = _("Crashed"); + break; + case VIR_DOMAIN_EVENT_LAST: + break; + } + return ret; +} + +static const char * +vshDomainEventDetailToString(int event, int detail) +{ + const char *ret = _("unknown"); + switch ((virDomainEventType) event) { + case VIR_DOMAIN_EVENT_DEFINED: + switch ((virDomainEventDefinedDetailType) detail) { + case VIR_DOMAIN_EVENT_DEFINED_ADDED: + ret = _("Added"); + break; + case VIR_DOMAIN_EVENT_DEFINED_UPDATED: + ret = _("Updated"); + break; + case VIR_DOMAIN_EVENT_DEFINED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_UNDEFINED: + switch ((virDomainEventUndefinedDetailType) detail) { + case VIR_DOMAIN_EVENT_UNDEFINED_REMOVED: + ret = _("Removed"); + break; + case VIR_DOMAIN_EVENT_UNDEFINED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_STARTED: + switch ((virDomainEventStartedDetailType) detail) { + case VIR_DOMAIN_EVENT_STARTED_BOOTED: + ret = _("Booted"); + break; + case VIR_DOMAIN_EVENT_STARTED_MIGRATED: + ret = _("Migrated"); + break; + case VIR_DOMAIN_EVENT_STARTED_RESTORED: + ret = _("Restored"); + break; + case VIR_DOMAIN_EVENT_STARTED_FROM_SNAPSHOT: + ret = _("Snapshot"); + break; + case VIR_DOMAIN_EVENT_STARTED_WAKEUP: + ret = _("Event wakeup"); + break; + case VIR_DOMAIN_EVENT_STARTED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_SUSPENDED: + switch ((virDomainEventSuspendedDetailType) detail) { + case VIR_DOMAIN_EVENT_SUSPENDED_PAUSED: + ret = _("Paused"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED: + ret = _("Migrated"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_IOERROR: + ret = _("I/O Error"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_WATCHDOG: + ret = _("Watchdog"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_RESTORED: + ret = _("Restored"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_FROM_SNAPSHOT: + ret = _("Snapshot"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_API_ERROR: + ret = _("API error"); + break; + case VIR_DOMAIN_EVENT_SUSPENDED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_RESUMED: + switch ((virDomainEventResumedDetailType) detail) { + case VIR_DOMAIN_EVENT_RESUMED_UNPAUSED: + ret = _("Unpaused"); + break; + case VIR_DOMAIN_EVENT_RESUMED_MIGRATED: + ret = _("Migrated"); + break; + case VIR_DOMAIN_EVENT_RESUMED_FROM_SNAPSHOT: + ret = _("Snapshot"); + break; + case VIR_DOMAIN_EVENT_RESUMED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_STOPPED: + switch ((virDomainEventStoppedDetailType) detail) { + case VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN: + ret = _("Shutdown"); + break; + case VIR_DOMAIN_EVENT_STOPPED_DESTROYED: + ret = _("Destroyed"); + break; + case VIR_DOMAIN_EVENT_STOPPED_CRASHED: + ret = _("Crashed"); + break; + case VIR_DOMAIN_EVENT_STOPPED_MIGRATED: + ret = _("Migrated"); + break; + case VIR_DOMAIN_EVENT_STOPPED_SAVED: + ret = _("Saved"); + break; + case VIR_DOMAIN_EVENT_STOPPED_FAILED: + ret = _("Failed"); + break; + case VIR_DOMAIN_EVENT_STOPPED_FROM_SNAPSHOT: + ret = _("Snapshot"); + break; + case VIR_DOMAIN_EVENT_STOPPED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_SHUTDOWN: + switch ((virDomainEventShutdownDetailType) detail) { + case VIR_DOMAIN_EVENT_SHUTDOWN_FINISHED: + ret = _("Finished"); + break; + case VIR_DOMAIN_EVENT_SHUTDOWN_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED: + switch ((virDomainEventPMSuspendedDetailType) detail) { + case VIR_DOMAIN_EVENT_PMSUSPENDED_MEMORY: + ret = _("Memory"); + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED_DISK: + ret = _("Disk"); + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_CRASHED: + switch ((virDomainEventCrashedDetailType) detail) { + case VIR_DOMAIN_EVENT_CRASHED_PANICKED: + ret = _("Panicked"); + break; + case VIR_DOMAIN_EVENT_CRASHED_LAST: + break; + } + break; + case VIR_DOMAIN_EVENT_LAST: + break; + } + return ret; +} + +struct vshDomEventData { + vshControl *ctl; + bool loop; + int count; +}; +typedef struct vshDomEventData vshDomEventData; + +/* FIXME: Support all callbacks, not just lifecycle */ +VIR_ENUM_DECL(vshDomainEvent) +VIR_ENUM_IMPL(vshDomainEvent, + /* VIR_DOMAIN_EVENT_ID_LAST, */ 1, + "lifecycle") + +static void +vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int event, + int detail, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'lifecycle' for domain %s: %s %s\n"), + virDomainGetName(dom), vshDomainEventToString(event), + vshDomainEventDetailToString(event, detail)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static const vshCmdInfo info_event[] = { + {.name = "event", + .data = N_("Domain Events") + }, + {.name = "desc", + .data = N_("List event types, or wait for domain events to occur") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_event[] = { + {.name = "domain", + .type = VSH_OT_DATA, + .help = N_("filter by domain name, id, or uuid") + }, + {.name = "event", + .type = VSH_OT_DATA, + .help = N_("which event type to wait for") + }, + {.name = "loop", + .type = VSH_OT_BOOL, + .help = N_("loop until timeout or interrupt, rather than one-shot") + }, + {.name = "timeout", + .type = VSH_OT_INT, + .help = N_("timeout seconds") + }, + {.name = "list", + .type = VSH_OT_BOOL, + .help = N_("list valid event types") + }, + {.name = NULL} +}; + +static bool +cmdEvent(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + int eventId = -1; + int timeout = 0; + vshDomEventData data; + const char *eventName = NULL; + int event; + + if (vshCommandOptBool(cmd, "list")) { + size_t i; + + for (i = 0; i < 1 /* VIR_DOMAIN_EVENT_ID_LAST */; i++) + vshPrint(ctl, "%s\n", vshDomainEventTypeToString(i)); + return true; + } + + if (vshCommandOptString(cmd, "event", &eventName) < 0) + return false; + if (!eventName) { + vshError(ctl, "%s", _("either --list or event type is required")); + return false; + } + if ((event = vshDomainEventTypeFromString(eventName) < 0)) { + vshError(ctl, _("unknown event type %s"), eventName); + return false; + } + + data.ctl = ctl; + data.loop = vshCommandOptBool(cmd, "loop"); + data.count = 0; + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + return false; + + if (vshCommandOptBool(cmd, "domain")) + dom = vshCommandOptDomain(ctl, cmd, NULL); + if (vshEventStart(ctl, timeout) < 0) + goto cleanup; + + if ((eventId = virConnectDomainEventRegisterAny(ctl->conn, dom, event, + VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), + &data, NULL)) < 0) + goto cleanup; + switch (vshEventWait(ctl)) { + case VSH_EVENT_INTERRUPT: + vshPrint(ctl, "%s", _("event loop interrupted\n")); + break; + case VSH_EVENT_TIMEOUT: + vshPrint(ctl, "%s", _("event loop timed out\n")); + break; + case VSH_EVENT_DONE: + break; + default: + goto cleanup; + } + vshPrint(ctl, _("events received: %d\n"), data.count); + if (data.count) + ret = true; + +cleanup: + vshEventCleanup(ctl); + if (eventId >= 0 && + virConnectDomainEventDeregisterAny(ctl->conn, eventId) < 0) + ret = false; + if (dom) + virDomainFree(dom); + return ret; +} + + /* * "change-media" command */ @@ -10750,6 +11082,12 @@ const vshCmdDef domManagementCmds[] = { .info = info_edit, .flags = 0 }, + {.name = "event", + .handler = cmdEvent, + .opts = opts_event, + .info = info_event, + .flags = 0 + }, {.name = "inject-nmi", .handler = cmdInjectNMI, .opts = opts_inject_nmi, diff --git a/tools/virsh.pod b/tools/virsh.pod index 301043f0a0..bb5340f02d 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1047,6 +1047,21 @@ except that it does some error checking. The editor used can be supplied by the C<$VISUAL> or C<$EDITOR> environment variables, and defaults to C. +=item B {[I] I [I<--loop>] [I<--timeout> I] | +I<--list>} + +Wait for a class of domain events to occur, and print appropriate details +of events as they happen. The events can optionally be filtered by +I. Using I<--list> as the only argument will provide a list +of possible I values known by this client, although the connection +might not allow registering for all these events. + +By default, this command is one-shot, and returns success once an event +occurs; you can send SIGINT (usually via C) to quit immediately. +If I<--timeout> is specified, the command gives up waiting for events +after I have elapsed. With I<--loop>, the command prints all +events until a timeout or interrupt key. + =item B I [I<--bypass-cache>] [{I<--running> | I<--paused>}] [I<--verbose>] -- GitLab