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(a)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