[libvirt] [PATCH] virsh: support remaining domain events

Earlier, I added 'virsh event' for lifecycle events, to get the concept approved; this patch finishes the support for all other events, although the user still has to register for one event type at a time. A future patch may add an --all parameter to make it possible to register for all events through a single call. * tools/virsh-domain.c (vshDomainEventWatchdogToString) (vshDomainEventIOErrorToString, vshGraphicsPhaseToString) (vshGraphicsAddressToString, vshDomainBlockJobStatusToString) (vshDomainEventDiskChangeToString) (vshDomainEventTrayChangeToString, vshEventGenericPrint) (vshEventRTCChangePrint, vshEventWatchdogPrint) (vshEventIOErrorPrint, vshEventGraphicsPrint) (vshEventIOErrorReasonPrint, vshEventBlockJobPrint) (vshEventDiskChangePrint, vshEventTrayChangePrint) (vshEventPMChangePrint, vshEventBalloonChangePrint) (vshEventDeviceRemovedPrint): New helper routines. (cmdEvent): Support full array of event callbacks. Signed-off-by: Eric Blake <eblake@redhat.com> --- tools/virsh-domain.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 381 insertions(+), 11 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 8e42300..3cffc9c 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -10434,18 +10434,135 @@ vshDomainEventDetailToString(int event, int detail) return str ? _(str) : _("unknown"); } +VIR_ENUM_DECL(vshDomainEventWatchdog) +VIR_ENUM_IMPL(vshDomainEventWatchdog, + VIR_DOMAIN_EVENT_WATCHDOG_LAST, + N_("none"), + N_("pause"), + N_("reset"), + N_("poweroff"), + N_("shutdown"), + N_("debug")) + +static const char * +vshDomainEventWatchdogToString(int action) +{ + const char *str = vshDomainEventWatchdogTypeToString(action); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshDomainEventIOError) +VIR_ENUM_IMPL(vshDomainEventIOError, + VIR_DOMAIN_EVENT_IO_ERROR_LAST, + N_("none"), + N_("pause"), + N_("report")) + +static const char * +vshDomainEventIOErrorToString(int action) +{ + const char *str = vshDomainEventIOErrorTypeToString(action); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshGraphicsPhase) +VIR_ENUM_IMPL(vshGraphicsPhase, + VIR_DOMAIN_EVENT_GRAPHICS_LAST, + N_("connect"), + N_("initialize"), + N_("disconnect")) + +static const char * +vshGraphicsPhaseToString(int phase) +{ + const char *str = vshGraphicsPhaseTypeToString(phase); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshGraphicsAddress) +VIR_ENUM_IMPL(vshGraphicsAddress, + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST, + N_("IPv4"), + N_("IPv6"), + N_("unix")) + +static const char * +vshGraphicsAddressToString(int family) +{ + const char *str = vshGraphicsAddressTypeToString(family); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshDomainBlockJobStatus) +VIR_ENUM_IMPL(vshDomainBlockJobStatus, + VIR_DOMAIN_BLOCK_JOB_LAST, + N_("completed"), + N_("failed"), + N_("canceled"), + N_("ready")) + +static const char * +vshDomainBlockJobStatusToString(int status) +{ + const char *str = vshDomainBlockJobStatusTypeToString(status); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshDomainEventDiskChange) +VIR_ENUM_IMPL(vshDomainEventDiskChange, + VIR_DOMAIN_EVENT_DISK_CHANGE_LAST, + N_("changed"), + N_("dropped")) + +static const char * +vshDomainEventDiskChangeToString(int reason) +{ + const char *str = vshDomainEventDiskChangeTypeToString(reason); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(vshDomainEventTrayChange) +VIR_ENUM_IMPL(vshDomainEventTrayChange, + VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST, + N_("opened"), + N_("closed")) + +static const char * +vshDomainEventTrayChangeToString(int reason) +{ + const char *str = vshDomainEventTrayChangeTypeToString(reason); + return str ? _(str) : _("unknown"); +} + +struct vshEventCallback { + const char *name; + virConnectDomainEventGenericCallback cb; +}; +typedef struct vshEventCallback vshEventCallback; + struct vshDomEventData { vshControl *ctl; bool loop; int count; + vshEventCallback *cb; }; typedef struct vshDomEventData vshDomEventData; -/* FIXME: Support all callbacks, not just lifecycle */ -VIR_ENUM_DECL(vshDomainEventId) -VIR_ENUM_IMPL(vshDomainEventId, - /* VIR_DOMAIN_EVENT_ID_LAST, */ 1, - "lifecycle") +static void +vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event '%s' for domain %s\n"), + data->cb->name, virDomainGetName(dom)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} static void vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -10466,6 +10583,257 @@ vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); } +static void +vshEventRTCChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + long long utcoffset, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'rtc-change' for domain %s: %lld\n"), + virDomainGetName(dom), utcoffset); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventWatchdogPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int action, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'watchdog' for domain %s: %s\n"), + virDomainGetName(dom), vshDomainEventWatchdogToString(action)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventIOErrorPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'io-error' for domain %s: %s (%s) %s\n"), + virDomainGetName(dom), srcPath, devAlias, + vshDomainEventIOErrorToString(action)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventGraphicsPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int phase, + const virDomainEventGraphicsAddress *local, + const virDomainEventGraphicsAddress *remote, + const char *authScheme, + const virDomainEventGraphicsSubject *subject, + void *opaque) +{ + vshDomEventData *data = opaque; + size_t i; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'graphics' for domain %s: " + "%s local[%s %s %s] remote[%s %s %s] %s"), + virDomainGetName(dom), vshGraphicsPhaseToString(phase), + vshGraphicsAddressToString(local->family), + local->node, local->service, + vshGraphicsAddressToString(remote->family), + remote->node, remote->service, + authScheme); + for (i = 0; i < subject->nidentity; i++) + vshPrint(data->ctl, " %s=%s", subject->identities[i].type, + subject->identities[i].name); + vshPrint(data->ctl, "\n"); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventIOErrorReasonPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + const char *reason, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'io-error-reason' for domain %s: " + "%s (%s) %s due to %s\n"), + virDomainGetName(dom), srcPath, devAlias, + vshDomainEventIOErrorToString(action), reason); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventBlockJobPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *disk, + int type, + int status, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, _("event 'block-job' for domain %s: %s for %s %s\n"), + virDomainGetName(dom), vshDomainBlockJobToString(type), + disk, vshDomainBlockJobStatusToString(status)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventDiskChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *oldSrc, + const char *newSrc, + const char *alias, + int reason, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, + _("event 'disk-change' for domain %s disk %s: %s -> %s: %s\n"), + virDomainGetName(dom), alias, NULLSTR(oldSrc), NULLSTR(newSrc), + vshDomainEventDiskChangeToString(reason)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventTrayChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *alias, + int reason, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, + _("event 'disk-change' for domain %s disk %s: %s\n"), + virDomainGetName(dom), alias, + vshDomainEventTrayChangeToString(reason)); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventPMChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int reason ATTRIBUTE_UNUSED, + void *opaque) +{ + /* As long as libvirt.h doesn't define any reasons, we might as + * well treat all PM state changes as generic events. */ + vshEventGenericPrint(conn, dom, opaque); +} + +static void +vshEventBalloonChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, + _("event 'balloon-change' for domain %s: %lluKiB\n"), + virDomainGetName(dom), actual); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +vshEventDeviceRemovedPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *alias, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && data->count) + return; + vshPrint(data->ctl, + _("event 'device-removed' for domain %s: %s\n"), + virDomainGetName(dom), alias); + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static vshEventCallback vshEventCallbacks[] = { + { "lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), }, + { "reboot", vshEventGenericPrint, }, + { "rtc-change", + VIR_DOMAIN_EVENT_CALLBACK(vshEventRTCChangePrint), }, + { "watchdog", + VIR_DOMAIN_EVENT_CALLBACK(vshEventWatchdogPrint), }, + { "io-error", + VIR_DOMAIN_EVENT_CALLBACK(vshEventIOErrorPrint), }, + { "graphics", + VIR_DOMAIN_EVENT_CALLBACK(vshEventGraphicsPrint), }, + { "io-error-reason", + VIR_DOMAIN_EVENT_CALLBACK(vshEventIOErrorReasonPrint), }, + { "control-error", vshEventGenericPrint, }, + { "block-job", + VIR_DOMAIN_EVENT_CALLBACK(vshEventBlockJobPrint), }, + { "disk-change", + VIR_DOMAIN_EVENT_CALLBACK(vshEventDiskChangePrint), }, + { "tray-change", + VIR_DOMAIN_EVENT_CALLBACK(vshEventTrayChangePrint), }, + { "pm-wakeup", + VIR_DOMAIN_EVENT_CALLBACK(vshEventPMChangePrint), }, + { "pm-suspend", + VIR_DOMAIN_EVENT_CALLBACK(vshEventPMChangePrint), }, + { "balloon-change", + VIR_DOMAIN_EVENT_CALLBACK(vshEventBalloonChangePrint), }, + { "pm-suspend-disk", + VIR_DOMAIN_EVENT_CALLBACK(vshEventPMChangePrint), }, + { "device-removed", + VIR_DOMAIN_EVENT_CALLBACK(vshEventDeviceRemovedPrint), }, +}; +verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); + static const vshCmdInfo info_event[] = { {.name = "event", .data = N_("Domain Events") @@ -10512,10 +10880,8 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) int event; if (vshCommandOptBool(cmd, "list")) { - size_t i; - - for (i = 0; i < 1 /* VIR_DOMAIN_EVENT_ID_LAST */; i++) - vshPrint(ctl, "%s\n", vshDomainEventIdTypeToString(i)); + for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) + vshPrint(ctl, "%s\n", vshEventCallbacks[event].name); return true; } @@ -10525,7 +10891,10 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) vshError(ctl, "%s", _("either --list or event type is required")); return false; } - if ((event = vshDomainEventIdTypeFromString(eventName) < 0)) { + for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) + if (STREQ(eventName, vshEventCallbacks[event].name)) + break; + if (event == VIR_DOMAIN_EVENT_ID_LAST) { vshError(ctl, _("unknown event type %s"), eventName); return false; } @@ -10533,6 +10902,7 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) data.ctl = ctl; data.loop = vshCommandOptBool(cmd, "loop"); data.count = 0; + data.cb = &vshEventCallbacks[event]; if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) return false; @@ -10542,7 +10912,7 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) goto cleanup; if ((eventId = virConnectDomainEventRegisterAny(ctl->conn, dom, event, - VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), + data.cb->cb, &data, NULL)) < 0) goto cleanup; switch (vshEventWait(ctl)) { -- 1.8.5.3

Similar to our event-test demo program, it's nice to be able to have a mode where we can sniff all events at once, rather than having to spawn multiple virsh in parallel with one for each event type. (Can I just say our RegisterAny design is lousy? The fact that the majority of our callback pointers have a function signature with the opaque data in a different position, and that we have to cast the function signature before registering it, makes it hard to write a generic callback function; we have to write one for every type of event id. Life would have been easier if we had designed the callback as a fixed signature with a void* and size parameter, and then allowed the caller to downcast the void* to a particular struct for data specific to their callback id, where we could have then had a single function with a switch statement for each event id, and register that one function for all types of events. It would also be nicer if the callback functions knew which callbackID was being used when invoking that callback, so that I could use a single data structure among all callbacks rather than having to create an array of data. But I really don't want to go add yet another event API design.) * tools/virsh-domain.c (cmdEvent): Add --all parameter; convert all callbacks to support shared counter. * tools/virsh.pod (event): Document it. Signed-off-by: Eric Blake <eblake@redhat.com> --- tools/virsh-domain.c | 136 ++++++++++++++++++++++++++++++++------------------- tools/virsh.pod | 8 +-- 2 files changed, 90 insertions(+), 54 deletions(-) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 3cffc9c..7adab00 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -10543,8 +10543,9 @@ typedef struct vshEventCallback vshEventCallback; struct vshDomEventData { vshControl *ctl; bool loop; - int count; + int *count; vshEventCallback *cb; + int id; }; typedef struct vshDomEventData vshDomEventData; @@ -10555,11 +10556,11 @@ vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event '%s' for domain %s\n"), data->cb->name, virDomainGetName(dom)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10573,12 +10574,12 @@ vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + 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++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10591,11 +10592,11 @@ vshEventRTCChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'rtc-change' for domain %s: %lld\n"), virDomainGetName(dom), utcoffset); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10608,11 +10609,11 @@ vshEventWatchdogPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'watchdog' for domain %s: %s\n"), virDomainGetName(dom), vshDomainEventWatchdogToString(action)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10627,12 +10628,12 @@ vshEventIOErrorPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'io-error' for domain %s: %s (%s) %s\n"), virDomainGetName(dom), srcPath, devAlias, vshDomainEventIOErrorToString(action)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10650,7 +10651,7 @@ vshEventGraphicsPrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshDomEventData *data = opaque; size_t i; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'graphics' for domain %s: " "%s local[%s %s %s] remote[%s %s %s] %s"), @@ -10664,7 +10665,7 @@ vshEventGraphicsPrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshPrint(data->ctl, " %s=%s", subject->identities[i].type, subject->identities[i].name); vshPrint(data->ctl, "\n"); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10680,13 +10681,13 @@ vshEventIOErrorReasonPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'io-error-reason' for domain %s: " "%s (%s) %s due to %s\n"), virDomainGetName(dom), srcPath, devAlias, vshDomainEventIOErrorToString(action), reason); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10701,12 +10702,12 @@ vshEventBlockJobPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'block-job' for domain %s: %s for %s %s\n"), virDomainGetName(dom), vshDomainBlockJobToString(type), disk, vshDomainBlockJobStatusToString(status)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10722,13 +10723,13 @@ vshEventDiskChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'disk-change' for domain %s disk %s: %s -> %s: %s\n"), virDomainGetName(dom), alias, NULLSTR(oldSrc), NULLSTR(newSrc), vshDomainEventDiskChangeToString(reason)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10742,13 +10743,13 @@ vshEventTrayChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'disk-change' for domain %s disk %s: %s\n"), virDomainGetName(dom), alias, vshDomainEventTrayChangeToString(reason)); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10772,12 +10773,12 @@ vshEventBalloonChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'balloon-change' for domain %s: %lluKiB\n"), virDomainGetName(dom), actual); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10790,12 +10791,12 @@ vshEventDeviceRemovedPrint(virConnectPtr conn ATTRIBUTE_UNUSED, { vshDomEventData *data = opaque; - if (!data->loop && data->count) + if (!data->loop && *data->count) return; vshPrint(data->ctl, _("event 'device-removed' for domain %s: %s\n"), virDomainGetName(dom), alias); - data->count++; + (*data->count)++; if (!data->loop) vshEventDone(data->ctl); } @@ -10853,6 +10854,10 @@ static const vshCmdOptDef opts_event[] = { .type = VSH_OT_DATA, .help = N_("which event type to wait for") }, + {.name = "all", + .type = VSH_OT_BOOL, + .help = N_("wait for all events instead of just one type") + }, {.name = "loop", .type = VSH_OT_BOOL, .help = N_("loop until timeout or interrupt, rather than one-shot") @@ -10873,11 +10878,14 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) { virDomainPtr dom = NULL; bool ret = false; - int eventId = -1; int timeout = 0; - vshDomEventData data; + vshDomEventData *data = NULL; + size_t i; const char *eventName = NULL; - int event; + int event = -1; + bool all = vshCommandOptBool(cmd, "all"); + bool loop = vshCommandOptBool(cmd, "loop"); + int count = 0; if (vshCommandOptBool(cmd, "list")) { for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) @@ -10887,34 +10895,55 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) if (vshCommandOptString(cmd, "event", &eventName) < 0) return false; - if (!eventName) { - vshError(ctl, "%s", _("either --list or event type is required")); - return false; - } - for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) - if (STREQ(eventName, vshEventCallbacks[event].name)) - break; - if (event == VIR_DOMAIN_EVENT_ID_LAST) { - vshError(ctl, _("unknown event type %s"), eventName); + if (eventName) { + for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) + if (STREQ(eventName, vshEventCallbacks[event].name)) + break; + if (event == VIR_DOMAIN_EVENT_ID_LAST) { + vshError(ctl, _("unknown event type %s"), eventName); + return false; + } + } else if (!all) { + vshError(ctl, "%s", + _("one of --list, --all, or event type is required")); return false; } - data.ctl = ctl; - data.loop = vshCommandOptBool(cmd, "loop"); - data.count = 0; - data.cb = &vshEventCallbacks[event]; + if (all) { + if (VIR_ALLOC_N(data, VIR_DOMAIN_EVENT_ID_LAST) < 0) + goto cleanup; + for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) { + data[i].ctl = ctl; + data[i].loop = loop; + data[i].count = &count; + data[i].cb = &vshEventCallbacks[i]; + data[i].id = -1; + } + } else { + if (VIR_ALLOC_N(data, 1) < 0) + goto cleanup; + data[0].ctl = ctl; + data[0].loop = vshCommandOptBool(cmd, "loop"); + data[0].count = &count; + data[0].cb = &vshEventCallbacks[event]; + data[0].id = -1; + } if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) - return false; + goto cleanup; if (vshCommandOptBool(cmd, "domain")) dom = vshCommandOptDomain(ctl, cmd, NULL); if (vshEventStart(ctl, timeout) < 0) goto cleanup; - if ((eventId = virConnectDomainEventRegisterAny(ctl->conn, dom, event, - data.cb->cb, - &data, NULL)) < 0) - goto cleanup; + for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { + if ((data[i].id = virConnectDomainEventRegisterAny(ctl->conn, dom, + all ? i : event, + data[i].cb->cb, + &data[i], + NULL)) < 0) + goto cleanup; + } switch (vshEventWait(ctl)) { case VSH_EVENT_INTERRUPT: vshPrint(ctl, "%s", _("event loop interrupted\n")); @@ -10927,15 +10956,20 @@ cmdEvent(vshControl *ctl, const vshCmd *cmd) default: goto cleanup; } - vshPrint(ctl, _("events received: %d\n"), data.count); - if (data.count) + vshPrint(ctl, _("events received: %d\n"), count); + if (count) ret = true; cleanup: vshEventCleanup(ctl); - if (eventId >= 0 && - virConnectDomainEventDeregisterAny(ctl->conn, eventId) < 0) - ret = false; + if (data) { + for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { + if (data[i].id >= 0 && + virConnectDomainEventDeregisterAny(ctl->conn, data[i].id) < 0) + ret = false; + } + VIR_FREE(data); + } if (dom) virDomainFree(dom); return ret; diff --git a/tools/virsh.pod b/tools/virsh.pod index 568e06e..ab9bdd9 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -1047,14 +1047,16 @@ 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>} +=item B<event> {[I<domain>] { I<event> | I<--all> } [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. +might not allow registering for all these events. It is also possible +to use I<--all> instead of I<event> to register for all possible event +types at once. By default, this command is one-shot, and returns success once an event occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately. -- 1.8.5.3

On Mon, Feb 24, 2014 at 05:08:29PM -0700, Eric Blake wrote:
Similar to our event-test demo program, it's nice to be able to have a mode where we can sniff all events at once, rather than having to spawn multiple virsh in parallel with one for each event type.
(Can I just say our RegisterAny design is lousy? The fact that the majority of our callback pointers have a function signature with the opaque data in a different position, and that we have to cast the function signature before registering it, makes it hard to write a generic callback function; we have to write one for every type of event id. Life would have been easier if we had designed the callback as a fixed signature with a void* and size parameter, and then allowed the caller to downcast the void* to a particular struct for data specific to their callback id, where we could have then had a single function with a switch statement for each event id, and register that one function for all types of events. It would also be nicer if the callback functions knew which callbackID was being used when invoking that callback, so that I could use a single data structure among all callbacks rather than having to create an array of data. But I really don't want to go add yet another event API design.)
* tools/virsh-domain.c (cmdEvent): Add --all parameter; convert all callbacks to support shared counter. * tools/virsh.pod (event): Document it.
Signed-off-by: Eric Blake <eblake@redhat.com> --- tools/virsh-domain.c | 136 ++++++++++++++++++++++++++++++++------------------- tools/virsh.pod | 8 +-- 2 files changed, 90 insertions(+), 54 deletions(-)
ACK Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 02/28/2014 10:41 AM, Daniel P. Berrange wrote:
On Mon, Feb 24, 2014 at 05:08:29PM -0700, Eric Blake wrote:
Similar to our event-test demo program, it's nice to be able to have a mode where we can sniff all events at once, rather than having to spawn multiple virsh in parallel with one for each event type.
* tools/virsh-domain.c (cmdEvent): Add --all parameter; convert all callbacks to support shared counter. * tools/virsh.pod (event): Document it.
Signed-off-by: Eric Blake <eblake@redhat.com> --- tools/virsh-domain.c | 136 ++++++++++++++++++++++++++++++++------------------- tools/virsh.pod | 8 +-- 2 files changed, 90 insertions(+), 54 deletions(-)
ACK
Thanks; pushed. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/24/2014 02:49 PM, Eric Blake wrote:
Earlier, I added 'virsh event' for lifecycle events, to get the concept approved; this patch finishes the support for all other events, although the user still has to register for one event type at a time. A future patch may add an --all parameter to make it possible to register for all events through a single call.
Ping - this would be nice to have in 1.2.2, since 'virsh event' is new to this release.
* tools/virsh-domain.c (vshDomainEventWatchdogToString) (vshDomainEventIOErrorToString, vshGraphicsPhaseToString) (vshGraphicsAddressToString, vshDomainBlockJobStatusToString) (vshDomainEventDiskChangeToString) (vshDomainEventTrayChangeToString, vshEventGenericPrint) (vshEventRTCChangePrint, vshEventWatchdogPrint) (vshEventIOErrorPrint, vshEventGraphicsPrint) (vshEventIOErrorReasonPrint, vshEventBlockJobPrint) (vshEventDiskChangePrint, vshEventTrayChangePrint) (vshEventPMChangePrint, vshEventBalloonChangePrint) (vshEventDeviceRemovedPrint): New helper routines. (cmdEvent): Support full array of event callbacks.
Signed-off-by: Eric Blake <eblake@redhat.com> --- tools/virsh-domain.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 381 insertions(+), 11 deletions(-)
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake