diff --git a/po/POTFILES.in b/po/POTFILES.in index 1fd3afdd6f..0d9adb0758 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -354,6 +354,7 @@ @SRCDIR@tools/virsh-checkpoint.c @SRCDIR@tools/virsh-completer-host.c @SRCDIR@tools/virsh-console.c +@SRCDIR@tools/virsh-domain-event.c @SRCDIR@tools/virsh-domain-monitor.c @SRCDIR@tools/virsh-domain.c @SRCDIR@tools/virsh-edit.c diff --git a/tools/meson.build b/tools/meson.build index f4b4a16c29..ac714e6425 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -174,6 +174,7 @@ executable( 'virsh-completer-volume.c', 'virsh-console.c', 'virsh-domain.c', + 'virsh-domain-event.c', 'virsh-domain-monitor.c', 'virsh-host.c', 'virsh-interface.c', diff --git a/tools/virsh-completer-domain.c b/tools/virsh-completer-domain.c index 321c47ef65..250dd8b21a 100644 --- a/tools/virsh-completer-domain.c +++ b/tools/virsh-completer-domain.c @@ -357,25 +357,6 @@ virshDomainBlockjobBaseTopCompleter(vshControl *ctl, return ret; } -char ** -virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED, - const vshCmd *cmd G_GNUC_UNUSED, - unsigned int flags) -{ - size_t i = 0; - g_auto(GStrv) tmp = NULL; - - virCheckFlags(0, NULL); - - tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1); - - for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) - tmp[i] = g_strdup(virshDomainEventCallbacks[i].name); - - return g_steal_pointer(&tmp); -} - - char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-completer-domain.h b/tools/virsh-completer-domain.h index 044c675842..27cf963912 100644 --- a/tools/virsh-completer-domain.h +++ b/tools/virsh-completer-domain.h @@ -41,11 +41,6 @@ virshDomainDiskTargetCompleter(vshControl *ctl, const vshCmd *cmd, unsigned int flags); -char ** -virshDomainEventNameCompleter(vshControl *ctl, - const vshCmd *cmd, - unsigned int flags); - char ** virshDomainInterfaceStateCompleter(vshControl *ctl, const vshCmd *cmd, diff --git a/tools/virsh-domain-event.c b/tools/virsh-domain-event.c new file mode 100644 index 0000000000..51571dffad --- /dev/null +++ b/tools/virsh-domain-event.c @@ -0,0 +1,1007 @@ +/* + * virsh-domain-event.c: Domain event listening commands + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include +#include "virsh-domain-event.h" +#include "virsh-util.h" + +#include "internal.h" +#include "viralloc.h" +#include "virenum.h" +#include "virutil.h" +#include "virtime.h" +#include "virtypedparam.h" + +/* + * "event" command + */ + +VIR_ENUM_DECL(virshDomainEvent); +VIR_ENUM_IMPL(virshDomainEvent, + VIR_DOMAIN_EVENT_LAST, + N_("Defined"), + N_("Undefined"), + N_("Started"), + N_("Suspended"), + N_("Resumed"), + N_("Stopped"), + N_("Shutdown"), + N_("PMSuspended"), + N_("Crashed")); + +static const char * +virshDomainEventToString(int event) +{ + const char *str = virshDomainEventTypeToString(event); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainEventDefined); +VIR_ENUM_IMPL(virshDomainEventDefined, + VIR_DOMAIN_EVENT_DEFINED_LAST, + N_("Added"), + N_("Updated"), + N_("Renamed"), + N_("Snapshot")); + +VIR_ENUM_DECL(virshDomainEventUndefined); +VIR_ENUM_IMPL(virshDomainEventUndefined, + VIR_DOMAIN_EVENT_UNDEFINED_LAST, + N_("Removed"), + N_("Renamed")); + +VIR_ENUM_DECL(virshDomainEventStarted); +VIR_ENUM_IMPL(virshDomainEventStarted, + VIR_DOMAIN_EVENT_STARTED_LAST, + N_("Booted"), + N_("Migrated"), + N_("Restored"), + N_("Snapshot"), + N_("Event wakeup")); + +VIR_ENUM_DECL(virshDomainEventSuspended); +VIR_ENUM_IMPL(virshDomainEventSuspended, + VIR_DOMAIN_EVENT_SUSPENDED_LAST, + N_("Paused"), + N_("Migrated"), + N_("I/O Error"), + N_("Watchdog"), + N_("Restored"), + N_("Snapshot"), + N_("API error"), + N_("Post-copy"), + N_("Post-copy Error")); + +VIR_ENUM_DECL(virshDomainEventResumed); +VIR_ENUM_IMPL(virshDomainEventResumed, + VIR_DOMAIN_EVENT_RESUMED_LAST, + N_("Unpaused"), + N_("Migrated"), + N_("Snapshot"), + N_("Post-copy")); + +VIR_ENUM_DECL(virshDomainEventStopped); +VIR_ENUM_IMPL(virshDomainEventStopped, + VIR_DOMAIN_EVENT_STOPPED_LAST, + N_("Shutdown"), + N_("Destroyed"), + N_("Crashed"), + N_("Migrated"), + N_("Saved"), + N_("Failed"), + N_("Snapshot")); + +VIR_ENUM_DECL(virshDomainEventShutdown); +VIR_ENUM_IMPL(virshDomainEventShutdown, + VIR_DOMAIN_EVENT_SHUTDOWN_LAST, + N_("Finished"), + N_("Finished after guest request"), + N_("Finished after host request")); + +VIR_ENUM_DECL(virshDomainEventPMSuspended); +VIR_ENUM_IMPL(virshDomainEventPMSuspended, + VIR_DOMAIN_EVENT_PMSUSPENDED_LAST, + N_("Memory"), + N_("Disk")); + +VIR_ENUM_DECL(virshDomainEventCrashed); +VIR_ENUM_IMPL(virshDomainEventCrashed, + VIR_DOMAIN_EVENT_CRASHED_LAST, + N_("Panicked"), + N_("Crashloaded")); + +static const char * +virshDomainEventDetailToString(int event, int detail) +{ + const char *str = NULL; + switch ((virDomainEventType) event) { + case VIR_DOMAIN_EVENT_DEFINED: + str = virshDomainEventDefinedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_UNDEFINED: + str = virshDomainEventUndefinedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_STARTED: + str = virshDomainEventStartedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_SUSPENDED: + str = virshDomainEventSuspendedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_RESUMED: + str = virshDomainEventResumedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_STOPPED: + str = virshDomainEventStoppedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_SHUTDOWN: + str = virshDomainEventShutdownTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_PMSUSPENDED: + str = virshDomainEventPMSuspendedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_CRASHED: + str = virshDomainEventCrashedTypeToString(detail); + break; + case VIR_DOMAIN_EVENT_LAST: + break; + } + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainEventWatchdog); +VIR_ENUM_IMPL(virshDomainEventWatchdog, + VIR_DOMAIN_EVENT_WATCHDOG_LAST, + N_("none"), + N_("pause"), + N_("reset"), + N_("poweroff"), + N_("shutdown"), + N_("debug"), + N_("inject-nmi")); + +static const char * +virshDomainEventWatchdogToString(int action) +{ + const char *str = virshDomainEventWatchdogTypeToString(action); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainEventIOError); +VIR_ENUM_IMPL(virshDomainEventIOError, + VIR_DOMAIN_EVENT_IO_ERROR_LAST, + N_("none"), + N_("pause"), + N_("report")); + +static const char * +virshDomainEventIOErrorToString(int action) +{ + const char *str = virshDomainEventIOErrorTypeToString(action); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshGraphicsPhase); +VIR_ENUM_IMPL(virshGraphicsPhase, + VIR_DOMAIN_EVENT_GRAPHICS_LAST, + N_("connect"), + N_("initialize"), + N_("disconnect")); + +static const char * +virshGraphicsPhaseToString(int phase) +{ + const char *str = virshGraphicsPhaseTypeToString(phase); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshGraphicsAddress); +VIR_ENUM_IMPL(virshGraphicsAddress, + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST, + N_("IPv4"), + N_("IPv6"), + N_("unix")); + +static const char * +virshGraphicsAddressToString(int family) +{ + const char *str = virshGraphicsAddressTypeToString(family); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainBlockJobStatus); +VIR_ENUM_IMPL(virshDomainBlockJobStatus, + VIR_DOMAIN_BLOCK_JOB_LAST, + N_("completed"), + N_("failed"), + N_("canceled"), + N_("ready")); + +static const char * +virshDomainBlockJobStatusToString(int status) +{ + const char *str = virshDomainBlockJobStatusTypeToString(status); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainEventDiskChange); +VIR_ENUM_IMPL(virshDomainEventDiskChange, + VIR_DOMAIN_EVENT_DISK_CHANGE_LAST, + N_("changed"), + N_("dropped")); + +static const char * +virshDomainEventDiskChangeToString(int reason) +{ + const char *str = virshDomainEventDiskChangeTypeToString(reason); + return str ? _(str) : _("unknown"); +} + +VIR_ENUM_DECL(virshDomainEventTrayChange); +VIR_ENUM_IMPL(virshDomainEventTrayChange, + VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST, + N_("opened"), + N_("closed")); + +static const char * +virshDomainEventTrayChangeToString(int reason) +{ + const char *str = virshDomainEventTrayChangeTypeToString(reason); + return str ? _(str) : _("unknown"); +} + + +struct virshDomainEventCallback { + const char *name; + virConnectDomainEventGenericCallback cb; +}; +typedef struct virshDomainEventCallback virshDomainEventCallback; + + +struct virshDomEventData { + vshControl *ctl; + bool loop; + int *count; + bool timestamp; + virshDomainEventCallback *cb; + int id; +}; +typedef struct virshDomEventData virshDomEventData; + +/** + * virshEventPrint: + * + * @data: opaque data passed to all event callbacks + * @buf: string buffer describing the event + * + * Print the event description found in @buf and update virshDomEventData. + * + * This function resets @buf and frees all memory consumed by its content. + */ +static void +virshEventPrint(virshDomEventData *data, + virBuffer *buf) +{ + g_autofree char *msg = NULL; + + if (!(msg = virBufferContentAndReset(buf))) + return; + + if (!data->loop && *data->count) + return; + + if (data->timestamp) { + char timestamp[VIR_TIME_STRING_BUFLEN]; + + if (virTimeStringNowRaw(timestamp) < 0) + timestamp[0] = '\0'; + + vshPrint(data->ctl, "%s: %s", timestamp, msg); + } else { + vshPrint(data->ctl, "%s", msg); + } + + (*data->count)++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static void +virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"), + ((virshDomEventData *) opaque)->cb->name, + virDomainGetName(dom)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int event, + int detail, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"), + virDomainGetName(dom), + virshDomainEventToString(event), + virshDomainEventDetailToString(event, detail)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + long long utcoffset, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"), + virDomainGetName(dom), + utcoffset); + virshEventPrint(opaque, &buf); +} + +static void +virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int action, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"), + virDomainGetName(dom), + virshDomainEventWatchdogToString(action)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"), + virDomainGetName(dom), + srcPath, + devAlias, + virshDomainEventIOErrorToString(action)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int phase, + const virDomainEventGraphicsAddress *local, + const virDomainEventGraphicsAddress *remote, + const char *authScheme, + const virDomainEventGraphicsSubject *subject, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + size_t i; + + virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': " + "%s local[%s %s %s] remote[%s %s %s] %s\n"), + virDomainGetName(dom), + virshGraphicsPhaseToString(phase), + virshGraphicsAddressToString(local->family), + local->node, + local->service, + virshGraphicsAddressToString(remote->family), + remote->node, + remote->service, + authScheme); + for (i = 0; i < subject->nidentity; i++) { + virBufferAsprintf(&buf, "\t%s=%s\n", + subject->identities[i].type, + subject->identities[i].name); + } + virshEventPrint(opaque, &buf); +} + +static void +virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + const char *reason, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': " + "%s (%s) %s due to %s\n"), + virDomainGetName(dom), + srcPath, + devAlias, + virshDomainEventIOErrorToString(action), + reason); + virshEventPrint(opaque, &buf); +} + +static void +virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *disk, + int type, + int status, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"), + ((virshDomEventData *) opaque)->cb->name, + virDomainGetName(dom), + virshDomainBlockJobToString(type), + disk, + virshDomainBlockJobStatusToString(status)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *oldSrc, + const char *newSrc, + const char *alias, + int reason, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: " + "%s -> %s: %s\n"), + virDomainGetName(dom), + alias, + NULLSTR(oldSrc), + NULLSTR(newSrc), + virshDomainEventDiskChangeToString(reason)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *alias, + int reason, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"), + virDomainGetName(dom), + alias, + virshDomainEventTrayChangeToString(reason)); + virshEventPrint(opaque, &buf); +} + +static void +virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int reason G_GNUC_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. */ + virshEventGenericPrint(conn, dom, opaque); +} + +static void +virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + unsigned long long actual, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"), + virDomainGetName(dom), + actual); + virshEventPrint(opaque, &buf); +} + +static void +virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *alias, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"), + virDomainGetName(dom), + alias); + virshEventPrint(opaque, &buf); +} + +static void +virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *alias, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"), + virDomainGetName(dom), + alias); + virshEventPrint(opaque, &buf); +} + +static void +virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + virTypedParameterPtr params, + int nparams, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + size_t i; + char *value; + + virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"), + virDomainGetName(dom)); + for (i = 0; i < nparams; i++) { + value = virTypedParameterToString(¶ms[i]); + if (value) { + virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value); + VIR_FREE(value); + } + } + virshEventPrint(opaque, &buf); +} + +VIR_ENUM_DECL(virshEventAgentLifecycleState); +VIR_ENUM_IMPL(virshEventAgentLifecycleState, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST, + N_("unknown"), + N_("connected"), + N_("disconnected")); + +VIR_ENUM_DECL(virshEventAgentLifecycleReason); +VIR_ENUM_IMPL(virshEventAgentLifecycleReason, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST, + N_("unknown"), + N_("domain started"), + N_("channel event")); + +#define UNKNOWNSTR(str) (str ? str : N_("unsupported value")) +static void +virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int state, + int reason, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: " + "'%s' reason: '%s'\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)), + UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason))); + virshEventPrint(opaque, &buf); +} + +static void +virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int iteration, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': " + "iteration: '%d'\n"), + virDomainGetName(dom), + iteration); + + virshEventPrint(opaque, &buf); +} + +static void +virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + virTypedParameterPtr params, + int nparams, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + size_t i; + + virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"), + virDomainGetName(dom)); + for (i = 0; i < nparams; i++) { + g_autofree char *value = virTypedParameterToString(¶ms[i]); + if (value) + virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value); + } + virshEventPrint(opaque, &buf); +} + + +static void +virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *alias, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"), + virDomainGetName(dom), + alias); + virshEventPrint(opaque, &buf); +} + +VIR_ENUM_DECL(virshEventMetadataChangeType); +VIR_ENUM_IMPL(virshEventMetadataChangeType, + VIR_DOMAIN_METADATA_LAST, + N_("description"), + N_("title"), + N_("element")); + +static void +virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)), + NULLSTR(nsuri)); + virshEventPrint(opaque, &buf); +} + + +static void +virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *dev, + const char *path, + unsigned long long threshold, + unsigned long long excess, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': " + "dev: %s(%s) %llu %llu\n"), + virDomainGetName(dom), + dev, NULLSTR(path), threshold, excess); + virshEventPrint(opaque, &buf); +} + + +VIR_ENUM_DECL(virshEventMemoryFailureRecipientType); +VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType, + VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST, + N_("hypervisor"), + N_("guest")); + +VIR_ENUM_DECL(virshEventMemoryFailureActionType); +VIR_ENUM_IMPL(virshEventMemoryFailureActionType, + VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST, + N_("ignore"), + N_("inject"), + N_("fatal"), + N_("reset")); + +static void +virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + int recipient, + int action, + unsigned int flags, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n" + "recipient: %s\naction: %s\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)), + UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action))); + virBufferAsprintf(&buf, _("flags:\n" + "\taction required: %d\n\trecursive: %d\n"), + !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED), + !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE)); + + virshEventPrint(opaque, &buf); +} + + +static void +virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED, + virDomainPtr dom, + const char *alias, + unsigned long long size, + void *opaque) +{ + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, + _("event 'memory-device-size-change' for domain '%s':\n" + "alias: %s\nsize: %llu\n"), + virDomainGetName(dom), alias, size); + + virshEventPrint(opaque, &buf); +} + + +virshDomainEventCallback virshDomainEventCallbacks[] = { + { "lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, + { "reboot", virshEventGenericPrint, }, + { "rtc-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), }, + { "watchdog", + VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), }, + { "io-error", + VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), }, + { "graphics", + VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), }, + { "io-error-reason", + VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), }, + { "control-error", virshEventGenericPrint, }, + { "block-job", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), }, + { "disk-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), }, + { "tray-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), }, + { "pm-wakeup", + VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, + { "pm-suspend", + VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, + { "balloon-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), }, + { "pm-suspend-disk", + VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, + { "device-removed", + VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), }, + { "block-job-2", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), }, + { "tunable", + VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), }, + { "agent-lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), }, + { "device-added", + VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), }, + { "migration-iteration", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), }, + { "job-completed", + VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, + { "device-removal-failed", + VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, + { "metadata-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, + { "block-threshold", + VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), }, + { "memory-failure", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), }, + { "memory-device-size-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), }, +}; +G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks)); + + +static char ** +virshDomainEventNameCompleter(vshControl *ctl G_GNUC_UNUSED, + const vshCmd *cmd G_GNUC_UNUSED, + unsigned int flags) +{ + size_t i = 0; + g_auto(GStrv) tmp = NULL; + + virCheckFlags(0, NULL); + + tmp = g_new0(char *, VIR_DOMAIN_EVENT_ID_LAST + 1); + + for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) + tmp[i] = g_strdup(virshDomainEventCallbacks[i].name); + + return g_steal_pointer(&tmp); +} + + +static const vshCmdInfo info_event[] = { + {.name = "help", + .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[] = { + VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"), + 0, 0), + {.name = "event", + .type = VSH_OT_STRING, + .completer = virshDomainEventNameCompleter, + .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") + }, + {.name = "timeout", + .type = VSH_OT_INT, + .help = N_("timeout seconds") + }, + {.name = "list", + .type = VSH_OT_BOOL, + .help = N_("list valid event types") + }, + {.name = "timestamp", + .type = VSH_OT_BOOL, + .help = N_("show timestamp for each printed event") + }, + {.name = NULL} +}; + +static bool +cmdEvent(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + bool ret = false; + int timeout = 0; + virshDomEventData *data = NULL; + size_t i; + const char *eventName = NULL; + int event = -1; + bool all = vshCommandOptBool(cmd, "all"); + bool loop = vshCommandOptBool(cmd, "loop"); + bool timestamp = vshCommandOptBool(cmd, "timestamp"); + int count = 0; + virshControl *priv = ctl->privData; + + VSH_EXCLUSIVE_OPTIONS("all", "event"); + VSH_EXCLUSIVE_OPTIONS("list", "all"); + VSH_EXCLUSIVE_OPTIONS("list", "event"); + + if (vshCommandOptBool(cmd, "list")) { + for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) + vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name); + return true; + } + + if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0) + return false; + if (eventName) { + for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) + if (STREQ(eventName, virshDomainEventCallbacks[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 is required")); + return false; + } + + if (all) { + data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST); + for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) { + data[i].ctl = ctl; + data[i].loop = loop; + data[i].count = &count; + data[i].timestamp = timestamp; + data[i].cb = &virshDomainEventCallbacks[i]; + data[i].id = -1; + } + } else { + data = g_new0(virshDomEventData, 1); + data[0].ctl = ctl; + data[0].loop = vshCommandOptBool(cmd, "loop"); + data[0].count = &count; + data[0].timestamp = timestamp; + data[0].cb = &virshDomainEventCallbacks[event]; + data[0].id = -1; + } + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + goto cleanup; + + if (vshCommandOptBool(cmd, "domain")) + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshEventStart(ctl, timeout) < 0) + goto cleanup; + + for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { + if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom, + all ? i : event, + data[i].cb->cb, + &data[i], + NULL)) < 0) { + /* When registering for all events: if the first + * registration succeeds, silently ignore failures on all + * later registrations on the assumption that the server + * is older and didn't know quite as many events. */ + if (i) + vshResetLibvirtError(); + else + 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"), count); + if (count) + ret = true; + + cleanup: + vshEventCleanup(ctl); + if (data) { + for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { + if (data[i].id >= 0 && + virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0) + ret = false; + } + VIR_FREE(data); + } + return ret; +} + + +const vshCmdDef domEventCmds[] = { + {.name = "event", + .handler = cmdEvent, + .opts = opts_event, + .info = info_event, + .flags = 0 + }, + {.name = NULL} +}; diff --git a/tools/virsh-domain-event.h b/tools/virsh-domain-event.h new file mode 100644 index 0000000000..dd96ef21ac --- /dev/null +++ b/tools/virsh-domain-event.h @@ -0,0 +1,23 @@ +/* + * virsh-domain-event.h: Commands for domain events + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "virsh.h" + +extern const vshCmdDef domEventCmds[]; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 9c304dbf78..dc6e3b5020 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12887,946 +12887,6 @@ cmdEdit(vshControl *ctl, const vshCmd *cmd) } -/* - * "event" command - */ -VIR_ENUM_DECL(virshDomainEvent); -VIR_ENUM_IMPL(virshDomainEvent, - VIR_DOMAIN_EVENT_LAST, - N_("Defined"), - N_("Undefined"), - N_("Started"), - N_("Suspended"), - N_("Resumed"), - N_("Stopped"), - N_("Shutdown"), - N_("PMSuspended"), - N_("Crashed")); - -static const char * -virshDomainEventToString(int event) -{ - const char *str = virshDomainEventTypeToString(event); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainEventDefined); -VIR_ENUM_IMPL(virshDomainEventDefined, - VIR_DOMAIN_EVENT_DEFINED_LAST, - N_("Added"), - N_("Updated"), - N_("Renamed"), - N_("Snapshot")); - -VIR_ENUM_DECL(virshDomainEventUndefined); -VIR_ENUM_IMPL(virshDomainEventUndefined, - VIR_DOMAIN_EVENT_UNDEFINED_LAST, - N_("Removed"), - N_("Renamed")); - -VIR_ENUM_DECL(virshDomainEventStarted); -VIR_ENUM_IMPL(virshDomainEventStarted, - VIR_DOMAIN_EVENT_STARTED_LAST, - N_("Booted"), - N_("Migrated"), - N_("Restored"), - N_("Snapshot"), - N_("Event wakeup")); - -VIR_ENUM_DECL(virshDomainEventSuspended); -VIR_ENUM_IMPL(virshDomainEventSuspended, - VIR_DOMAIN_EVENT_SUSPENDED_LAST, - N_("Paused"), - N_("Migrated"), - N_("I/O Error"), - N_("Watchdog"), - N_("Restored"), - N_("Snapshot"), - N_("API error"), - N_("Post-copy"), - N_("Post-copy Error")); - -VIR_ENUM_DECL(virshDomainEventResumed); -VIR_ENUM_IMPL(virshDomainEventResumed, - VIR_DOMAIN_EVENT_RESUMED_LAST, - N_("Unpaused"), - N_("Migrated"), - N_("Snapshot"), - N_("Post-copy")); - -VIR_ENUM_DECL(virshDomainEventStopped); -VIR_ENUM_IMPL(virshDomainEventStopped, - VIR_DOMAIN_EVENT_STOPPED_LAST, - N_("Shutdown"), - N_("Destroyed"), - N_("Crashed"), - N_("Migrated"), - N_("Saved"), - N_("Failed"), - N_("Snapshot")); - -VIR_ENUM_DECL(virshDomainEventShutdown); -VIR_ENUM_IMPL(virshDomainEventShutdown, - VIR_DOMAIN_EVENT_SHUTDOWN_LAST, - N_("Finished"), - N_("Finished after guest request"), - N_("Finished after host request")); - -VIR_ENUM_DECL(virshDomainEventPMSuspended); -VIR_ENUM_IMPL(virshDomainEventPMSuspended, - VIR_DOMAIN_EVENT_PMSUSPENDED_LAST, - N_("Memory"), - N_("Disk")); - -VIR_ENUM_DECL(virshDomainEventCrashed); -VIR_ENUM_IMPL(virshDomainEventCrashed, - VIR_DOMAIN_EVENT_CRASHED_LAST, - N_("Panicked"), - N_("Crashloaded")); - -static const char * -virshDomainEventDetailToString(int event, int detail) -{ - const char *str = NULL; - switch ((virDomainEventType) event) { - case VIR_DOMAIN_EVENT_DEFINED: - str = virshDomainEventDefinedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_UNDEFINED: - str = virshDomainEventUndefinedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_STARTED: - str = virshDomainEventStartedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_SUSPENDED: - str = virshDomainEventSuspendedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_RESUMED: - str = virshDomainEventResumedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_STOPPED: - str = virshDomainEventStoppedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_SHUTDOWN: - str = virshDomainEventShutdownTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_PMSUSPENDED: - str = virshDomainEventPMSuspendedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_CRASHED: - str = virshDomainEventCrashedTypeToString(detail); - break; - case VIR_DOMAIN_EVENT_LAST: - break; - } - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainEventWatchdog); -VIR_ENUM_IMPL(virshDomainEventWatchdog, - VIR_DOMAIN_EVENT_WATCHDOG_LAST, - N_("none"), - N_("pause"), - N_("reset"), - N_("poweroff"), - N_("shutdown"), - N_("debug"), - N_("inject-nmi")); - -static const char * -virshDomainEventWatchdogToString(int action) -{ - const char *str = virshDomainEventWatchdogTypeToString(action); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainEventIOError); -VIR_ENUM_IMPL(virshDomainEventIOError, - VIR_DOMAIN_EVENT_IO_ERROR_LAST, - N_("none"), - N_("pause"), - N_("report")); - -static const char * -virshDomainEventIOErrorToString(int action) -{ - const char *str = virshDomainEventIOErrorTypeToString(action); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshGraphicsPhase); -VIR_ENUM_IMPL(virshGraphicsPhase, - VIR_DOMAIN_EVENT_GRAPHICS_LAST, - N_("connect"), - N_("initialize"), - N_("disconnect")); - -static const char * -virshGraphicsPhaseToString(int phase) -{ - const char *str = virshGraphicsPhaseTypeToString(phase); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshGraphicsAddress); -VIR_ENUM_IMPL(virshGraphicsAddress, - VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST, - N_("IPv4"), - N_("IPv6"), - N_("unix")); - -static const char * -virshGraphicsAddressToString(int family) -{ - const char *str = virshGraphicsAddressTypeToString(family); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainBlockJobStatus); -VIR_ENUM_IMPL(virshDomainBlockJobStatus, - VIR_DOMAIN_BLOCK_JOB_LAST, - N_("completed"), - N_("failed"), - N_("canceled"), - N_("ready")); - -static const char * -virshDomainBlockJobStatusToString(int status) -{ - const char *str = virshDomainBlockJobStatusTypeToString(status); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainEventDiskChange); -VIR_ENUM_IMPL(virshDomainEventDiskChange, - VIR_DOMAIN_EVENT_DISK_CHANGE_LAST, - N_("changed"), - N_("dropped")); - -static const char * -virshDomainEventDiskChangeToString(int reason) -{ - const char *str = virshDomainEventDiskChangeTypeToString(reason); - return str ? _(str) : _("unknown"); -} - -VIR_ENUM_DECL(virshDomainEventTrayChange); -VIR_ENUM_IMPL(virshDomainEventTrayChange, - VIR_DOMAIN_EVENT_TRAY_CHANGE_LAST, - N_("opened"), - N_("closed")); - -static const char * -virshDomainEventTrayChangeToString(int reason) -{ - const char *str = virshDomainEventTrayChangeTypeToString(reason); - return str ? _(str) : _("unknown"); -} - -struct virshDomEventData { - vshControl *ctl; - bool loop; - int *count; - bool timestamp; - virshDomainEventCallback *cb; - int id; -}; -typedef struct virshDomEventData virshDomEventData; - -/** - * virshEventPrint: - * - * @data: opaque data passed to all event callbacks - * @buf: string buffer describing the event - * - * Print the event description found in @buf and update virshDomEventData. - * - * This function resets @buf and frees all memory consumed by its content. - */ -static void -virshEventPrint(virshDomEventData *data, - virBuffer *buf) -{ - g_autofree char *msg = NULL; - - if (!(msg = virBufferContentAndReset(buf))) - return; - - if (!data->loop && *data->count) - return; - - if (data->timestamp) { - char timestamp[VIR_TIME_STRING_BUFLEN]; - - if (virTimeStringNowRaw(timestamp) < 0) - timestamp[0] = '\0'; - - vshPrint(data->ctl, "%s: %s", timestamp, msg); - } else { - vshPrint(data->ctl, "%s", msg); - } - - (*data->count)++; - if (!data->loop) - vshEventDone(data->ctl); -} - -static void -virshEventGenericPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event '%s' for domain '%s'\n"), - ((virshDomEventData *) opaque)->cb->name, - virDomainGetName(dom)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int event, - int detail, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'lifecycle' for domain '%s': %s %s\n"), - virDomainGetName(dom), - virshDomainEventToString(event), - virshDomainEventDetailToString(event, detail)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventRTCChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - long long utcoffset, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'rtc-change' for domain '%s': %lld\n"), - virDomainGetName(dom), - utcoffset); - virshEventPrint(opaque, &buf); -} - -static void -virshEventWatchdogPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int action, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'watchdog' for domain '%s': %s\n"), - virDomainGetName(dom), - virshDomainEventWatchdogToString(action)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventIOErrorPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *srcPath, - const char *devAlias, - int action, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'io-error' for domain '%s': %s (%s) %s\n"), - virDomainGetName(dom), - srcPath, - devAlias, - virshDomainEventIOErrorToString(action)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventGraphicsPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int phase, - const virDomainEventGraphicsAddress *local, - const virDomainEventGraphicsAddress *remote, - const char *authScheme, - const virDomainEventGraphicsSubject *subject, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - size_t i; - - virBufferAsprintf(&buf, _("event 'graphics' for domain '%s': " - "%s local[%s %s %s] remote[%s %s %s] %s\n"), - virDomainGetName(dom), - virshGraphicsPhaseToString(phase), - virshGraphicsAddressToString(local->family), - local->node, - local->service, - virshGraphicsAddressToString(remote->family), - remote->node, - remote->service, - authScheme); - for (i = 0; i < subject->nidentity; i++) { - virBufferAsprintf(&buf, "\t%s=%s\n", - subject->identities[i].type, - subject->identities[i].name); - } - virshEventPrint(opaque, &buf); -} - -static void -virshEventIOErrorReasonPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *srcPath, - const char *devAlias, - int action, - const char *reason, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'io-error-reason' for domain '%s': " - "%s (%s) %s due to %s\n"), - virDomainGetName(dom), - srcPath, - devAlias, - virshDomainEventIOErrorToString(action), - reason); - virshEventPrint(opaque, &buf); -} - -static void -virshEventBlockJobPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *disk, - int type, - int status, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event '%s' for domain '%s': %s for %s %s\n"), - ((virshDomEventData *) opaque)->cb->name, - virDomainGetName(dom), - virshDomainBlockJobToString(type), - disk, - virshDomainBlockJobStatusToString(status)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventDiskChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *oldSrc, - const char *newSrc, - const char *alias, - int reason, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'disk-change' for domain '%s' disk %s: " - "%s -> %s: %s\n"), - virDomainGetName(dom), - alias, - NULLSTR(oldSrc), - NULLSTR(newSrc), - virshDomainEventDiskChangeToString(reason)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventTrayChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *alias, - int reason, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'tray-change' for domain '%s' disk %s: %s\n"), - virDomainGetName(dom), - alias, - virshDomainEventTrayChangeToString(reason)); - virshEventPrint(opaque, &buf); -} - -static void -virshEventPMChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int reason G_GNUC_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. */ - virshEventGenericPrint(conn, dom, opaque); -} - -static void -virshEventBalloonChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - unsigned long long actual, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'balloon-change' for domain '%s': %lluKiB\n"), - virDomainGetName(dom), - actual); - virshEventPrint(opaque, &buf); -} - -static void -virshEventDeviceRemovedPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *alias, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'device-removed' for domain '%s': %s\n"), - virDomainGetName(dom), - alias); - virshEventPrint(opaque, &buf); -} - -static void -virshEventDeviceAddedPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *alias, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'device-added' for domain '%s': %s\n"), - virDomainGetName(dom), - alias); - virshEventPrint(opaque, &buf); -} - -static void -virshEventTunablePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - virTypedParameterPtr params, - int nparams, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - size_t i; - char *value; - - virBufferAsprintf(&buf, _("event 'tunable' for domain '%s':\n"), - virDomainGetName(dom)); - for (i = 0; i < nparams; i++) { - value = virTypedParameterToString(¶ms[i]); - if (value) { - virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value); - VIR_FREE(value); - } - } - virshEventPrint(opaque, &buf); -} - -VIR_ENUM_DECL(virshEventAgentLifecycleState); -VIR_ENUM_IMPL(virshEventAgentLifecycleState, - VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST, - N_("unknown"), - N_("connected"), - N_("disconnected")); - -VIR_ENUM_DECL(virshEventAgentLifecycleReason); -VIR_ENUM_IMPL(virshEventAgentLifecycleReason, - VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST, - N_("unknown"), - N_("domain started"), - N_("channel event")); - -#define UNKNOWNSTR(str) (str ? str : N_("unsupported value")) -static void -virshEventAgentLifecyclePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int state, - int reason, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'agent-lifecycle' for domain '%s': state: " - "'%s' reason: '%s'\n"), - virDomainGetName(dom), - UNKNOWNSTR(virshEventAgentLifecycleStateTypeToString(state)), - UNKNOWNSTR(virshEventAgentLifecycleReasonTypeToString(reason))); - virshEventPrint(opaque, &buf); -} - -static void -virshEventMigrationIterationPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int iteration, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'migration-iteration' for domain '%s': " - "iteration: '%d'\n"), - virDomainGetName(dom), - iteration); - - virshEventPrint(opaque, &buf); -} - -static void -virshEventJobCompletedPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - virTypedParameterPtr params, - int nparams, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - size_t i; - - virBufferAsprintf(&buf, _("event 'job-completed' for domain '%s':\n"), - virDomainGetName(dom)); - for (i = 0; i < nparams; i++) { - g_autofree char *value = virTypedParameterToString(¶ms[i]); - if (value) - virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value); - } - virshEventPrint(opaque, &buf); -} - - -static void -virshEventDeviceRemovalFailedPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *alias, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'device-removal-failed' for domain '%s': %s\n"), - virDomainGetName(dom), - alias); - virshEventPrint(opaque, &buf); -} - -VIR_ENUM_DECL(virshEventMetadataChangeType); -VIR_ENUM_IMPL(virshEventMetadataChangeType, - VIR_DOMAIN_METADATA_LAST, - N_("description"), - N_("title"), - N_("element")); - -static void -virshEventMetadataChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int type, - const char *nsuri, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'metadata-change' for domain '%s': type %s, uri %s\n"), - virDomainGetName(dom), - UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)), - NULLSTR(nsuri)); - virshEventPrint(opaque, &buf); -} - - -static void -virshEventBlockThresholdPrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *dev, - const char *path, - unsigned long long threshold, - unsigned long long excess, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'block-threshold' for domain '%s': " - "dev: %s(%s) %llu %llu\n"), - virDomainGetName(dom), - dev, NULLSTR(path), threshold, excess); - virshEventPrint(opaque, &buf); -} - - -VIR_ENUM_DECL(virshEventMemoryFailureRecipientType); -VIR_ENUM_IMPL(virshEventMemoryFailureRecipientType, - VIR_DOMAIN_EVENT_MEMORY_FAILURE_RECIPIENT_LAST, - N_("hypervisor"), - N_("guest")); - -VIR_ENUM_DECL(virshEventMemoryFailureActionType); -VIR_ENUM_IMPL(virshEventMemoryFailureActionType, - VIR_DOMAIN_EVENT_MEMORY_FAILURE_ACTION_LAST, - N_("ignore"), - N_("inject"), - N_("fatal"), - N_("reset")); - -static void -virshEventMemoryFailurePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - int recipient, - int action, - unsigned int flags, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, _("event 'memory-failure' for domain '%s':\n" - "recipient: %s\naction: %s\n"), - virDomainGetName(dom), - UNKNOWNSTR(virshEventMemoryFailureRecipientTypeTypeToString(recipient)), - UNKNOWNSTR(virshEventMemoryFailureActionTypeTypeToString(action))); - virBufferAsprintf(&buf, _("flags:\n" - "\taction required: %d\n\trecursive: %d\n"), - !!(flags & VIR_DOMAIN_MEMORY_FAILURE_ACTION_REQUIRED), - !!(flags & VIR_DOMAIN_MEMORY_FAILURE_RECURSIVE)); - - virshEventPrint(opaque, &buf); -} - - -static void -virshEventMemoryDeviceSizeChangePrint(virConnectPtr conn G_GNUC_UNUSED, - virDomainPtr dom, - const char *alias, - unsigned long long size, - void *opaque) -{ - g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; - - virBufferAsprintf(&buf, - _("event 'memory-device-size-change' for domain '%s':\n" - "alias: %s\nsize: %llu\n"), - virDomainGetName(dom), alias, size); - - virshEventPrint(opaque, &buf); -} - - -virshDomainEventCallback virshDomainEventCallbacks[] = { - { "lifecycle", - VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, - { "reboot", virshEventGenericPrint, }, - { "rtc-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventRTCChangePrint), }, - { "watchdog", - VIR_DOMAIN_EVENT_CALLBACK(virshEventWatchdogPrint), }, - { "io-error", - VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorPrint), }, - { "graphics", - VIR_DOMAIN_EVENT_CALLBACK(virshEventGraphicsPrint), }, - { "io-error-reason", - VIR_DOMAIN_EVENT_CALLBACK(virshEventIOErrorReasonPrint), }, - { "control-error", virshEventGenericPrint, }, - { "block-job", - VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), }, - { "disk-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventDiskChangePrint), }, - { "tray-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventTrayChangePrint), }, - { "pm-wakeup", - VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, - { "pm-suspend", - VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, - { "balloon-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventBalloonChangePrint), }, - { "pm-suspend-disk", - VIR_DOMAIN_EVENT_CALLBACK(virshEventPMChangePrint), }, - { "device-removed", - VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovedPrint), }, - { "block-job-2", - VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockJobPrint), }, - { "tunable", - VIR_DOMAIN_EVENT_CALLBACK(virshEventTunablePrint), }, - { "agent-lifecycle", - VIR_DOMAIN_EVENT_CALLBACK(virshEventAgentLifecyclePrint), }, - { "device-added", - VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), }, - { "migration-iteration", - VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), }, - { "job-completed", - VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, - { "device-removal-failed", - VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, - { "metadata-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, - { "block-threshold", - VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), }, - { "memory-failure", - VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryFailurePrint), }, - { "memory-device-size-change", - VIR_DOMAIN_EVENT_CALLBACK(virshEventMemoryDeviceSizeChangePrint), }, -}; -G_STATIC_ASSERT(VIR_DOMAIN_EVENT_ID_LAST == G_N_ELEMENTS(virshDomainEventCallbacks)); - -static const vshCmdInfo info_event[] = { - {.name = "help", - .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[] = { - VIRSH_COMMON_OPT_DOMAIN_OT_STRING(N_("filter by domain name, id or uuid"), - 0, 0), - {.name = "event", - .type = VSH_OT_STRING, - .completer = virshDomainEventNameCompleter, - .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") - }, - {.name = "timeout", - .type = VSH_OT_INT, - .help = N_("timeout seconds") - }, - {.name = "list", - .type = VSH_OT_BOOL, - .help = N_("list valid event types") - }, - {.name = "timestamp", - .type = VSH_OT_BOOL, - .help = N_("show timestamp for each printed event") - }, - {.name = NULL} -}; - -static bool -cmdEvent(vshControl *ctl, const vshCmd *cmd) -{ - g_autoptr(virshDomain) dom = NULL; - bool ret = false; - int timeout = 0; - virshDomEventData *data = NULL; - size_t i; - const char *eventName = NULL; - int event = -1; - bool all = vshCommandOptBool(cmd, "all"); - bool loop = vshCommandOptBool(cmd, "loop"); - bool timestamp = vshCommandOptBool(cmd, "timestamp"); - int count = 0; - virshControl *priv = ctl->privData; - - VSH_EXCLUSIVE_OPTIONS("all", "event"); - VSH_EXCLUSIVE_OPTIONS("list", "all"); - VSH_EXCLUSIVE_OPTIONS("list", "event"); - - if (vshCommandOptBool(cmd, "list")) { - for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) - vshPrint(ctl, "%s\n", virshDomainEventCallbacks[event].name); - return true; - } - - if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0) - return false; - if (eventName) { - for (event = 0; event < VIR_DOMAIN_EVENT_ID_LAST; event++) - if (STREQ(eventName, virshDomainEventCallbacks[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 is required")); - return false; - } - - if (all) { - data = g_new0(virshDomEventData, VIR_DOMAIN_EVENT_ID_LAST); - for (i = 0; i < VIR_DOMAIN_EVENT_ID_LAST; i++) { - data[i].ctl = ctl; - data[i].loop = loop; - data[i].count = &count; - data[i].timestamp = timestamp; - data[i].cb = &virshDomainEventCallbacks[i]; - data[i].id = -1; - } - } else { - data = g_new0(virshDomEventData, 1); - data[0].ctl = ctl; - data[0].loop = vshCommandOptBool(cmd, "loop"); - data[0].count = &count; - data[0].timestamp = timestamp; - data[0].cb = &virshDomainEventCallbacks[event]; - data[0].id = -1; - } - if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) - goto cleanup; - - if (vshCommandOptBool(cmd, "domain")) - if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) - goto cleanup; - - if (vshEventStart(ctl, timeout) < 0) - goto cleanup; - - for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { - if ((data[i].id = virConnectDomainEventRegisterAny(priv->conn, dom, - all ? i : event, - data[i].cb->cb, - &data[i], - NULL)) < 0) { - /* When registering for all events: if the first - * registration succeeds, silently ignore failures on all - * later registrations on the assumption that the server - * is older and didn't know quite as many events. */ - if (i) - vshResetLibvirtError(); - else - 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"), count); - if (count) - ret = true; - - cleanup: - vshEventCleanup(ctl); - if (data) { - for (i = 0; i < (all ? VIR_DOMAIN_EVENT_ID_LAST : 1); i++) { - if (data[i].id >= 0 && - virConnectDomainEventDeregisterAny(priv->conn, data[i].id) < 0) - ret = false; - } - VIR_FREE(data); - } - return ret; -} - - /* * "change-media" command */ @@ -14851,12 +13911,6 @@ const vshCmdDef domManagementCmds[] = { .info = info_edit, .flags = 0 }, - {.name = "event", - .handler = cmdEvent, - .opts = opts_event, - .info = info_event, - .flags = 0 - }, {.name = "get-user-sshkeys", .handler = cmdGetUserSSHKeys, .opts = opts_get_user_sshkeys, diff --git a/tools/virsh.c b/tools/virsh.c index 64e0700fcd..f7adb90be8 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -45,6 +45,7 @@ #include "virsh-checkpoint.h" #include "virsh-console.h" #include "virsh-domain.h" +#include "virsh-domain-event.h" #include "virsh-domain-monitor.h" #include "virsh-host.h" #include "virsh-interface.h" @@ -814,6 +815,7 @@ static const vshCmdDef virshCmds[] = { static const vshCmdGrp cmdGroups[] = { {VIRSH_CMD_GRP_DOM_MANAGEMENT, "domain", domManagementCmds}, {VIRSH_CMD_GRP_DOM_MONITORING, "monitor", domMonitoringCmds}, + {VIRSH_CMD_GRP_DOM_EVENTS, "events", domEventCmds}, {VIRSH_CMD_GRP_HOST_AND_HV, "host", hostAndHypervisorCmds}, {VIRSH_CMD_GRP_CHECKPOINT, "checkpoint", checkpointCmds}, {VIRSH_CMD_GRP_IFACE, "interface", ifaceCmds}, diff --git a/tools/virsh.h b/tools/virsh.h index cacd54db57..d7a60b135d 100644 --- a/tools/virsh.h +++ b/tools/virsh.h @@ -41,6 +41,7 @@ #define VIRSH_CMD_GRP_CHECKPOINT "Checkpoint" #define VIRSH_CMD_GRP_DOM_MANAGEMENT "Domain Management" #define VIRSH_CMD_GRP_DOM_MONITORING "Domain Monitoring" +#define VIRSH_CMD_GRP_DOM_EVENTS "Domain Events" #define VIRSH_CMD_GRP_STORAGE_POOL "Storage Pool" #define VIRSH_CMD_GRP_STORAGE_VOL "Storage Volume" #define VIRSH_CMD_GRP_NETWORK "Networking"