[libvirt] [PATCH 3/4] virsh: add event command, for lifecycle events

Eric Blake eblake at redhat.com
Sat Feb 15 00:21:40 UTC 2014


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 <eblake at redhat.com>
---
 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 2c7bf66..3548131 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -10295,6 +10295,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,
+              1 /* VIR_DOMAIN_EVENT_LAST */,
+              "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
  */
@@ -10751,6 +11083,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 f221475..0878778 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1043,6 +1043,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<vi>.

+=item B<event> {[I<domain>] I<event> [I<--loop>] [I<--timeout> I<seconds>] |
+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<domain>.  Using I<--list> as the only argument will provide a list
+of possible I<event> values known by this client, although the connection
+might not allow registering for all these events.
+
+By default, tihs command is one-shot, and returns success once an event
+occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately.
+If I<--timeout> is specified, the command gives up waiting for events
+after I<seconds> have elapsed.   With I<--loop>, the command prints all
+events until a timeout or interrupt key.
+
 =item B<managedsave> I<domain> [I<--bypass-cache>]
 [{I<--running> | I<--paused>}] [I<--verbose>]

-- 
1.8.5.3




More information about the libvir-list mailing list