[libvirt] [PATCHv2] qemu: Emit domain events for all virtio-serial channels

Presently domain events are emitted for the "agent lifecycle" which is a specialization on virtio-serial events specific to the channel named "org.qemu.guest_agent.0". This patch adds a generic event for "channel lifecycles", which emit state change events for all attached channels. --- daemon/remote.c | 42 +++++++++++++++++ examples/object-events/event-test.c | 57 ++++++++++++++++++++++++ include/libvirt/libvirt-domain.h | 44 ++++++++++++++++++ src/conf/domain_event.c | 89 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 12 +++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 6 +++ src/remote/remote_driver.c | 32 +++++++++++++ src/remote/remote_protocol.x | 17 ++++++- src/remote_protocol-structs | 7 +++ tools/virsh-domain.c | 35 +++++++++++++++ 12 files changed, 347 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index e414f92..25b32f2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1086,6 +1086,47 @@ remoteRelayDomainEventAgentLifecycle(virConnectPtr conn, static int +remoteRelayDomainEventChannelLifecycle(virConnectPtr conn, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_channel_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain channel lifecycle event %s %d, callback %d, " + " name: %s, state %d, reason %d", + dom->name, dom->id, callback->callbackID, channelName, state, reason); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + if (VIR_STRDUP(data.channelName, channelName) < 0) + goto error; + data.state = state; + data.reason = reason; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_callback_channel_lifecycle_msg, + &data); + + return 0; + error: + VIR_FREE(data.channelName); + return -1; +} + + +static int remoteRelayDomainEventDeviceAdded(virConnectPtr conn, virDomainPtr dom, const char *devAlias, @@ -1248,6 +1289,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventChannelLifecycle) }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 730cb8b..da023eb 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -337,6 +337,46 @@ guestAgentLifecycleEventReasonToString(int event) return "unknown"; } + +static const char * +guestChannelLifecycleEventStateToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED: + return "Disconnected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED: + return "Connected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST: + break; + } + + return "unknown"; +} + + +static const char * +guestChannelLifecycleEventReasonToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleReason) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN: + return "Unknown"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED: + return "Domain started"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL: + return "Channel event"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST: + break; + } + + return "unknown"; +} + + static const char * storagePoolEventToString(int event) { @@ -797,6 +837,22 @@ myDomainEventAgentLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +myDomainEventChannelLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) guest channel(%s) state changed: %s reason: %s\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), channelName, + guestChannelLifecycleEventStateToString(state), + guestChannelLifecycleEventReasonToString(reason)); + + return 0; +} + static int myDomainEventDeviceAddedCallback(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -971,6 +1027,7 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION, myDomainEventMigrationIterationCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_JOB_COMPLETED, myDomainEventJobCompletedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE, myDomainEventChannelLifecycleCallback) }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..9a5c664 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,49 @@ typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, int reason, void *opaque); +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED = 1, /* channel connected */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED = 2, /* channel disconnected */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST +# endif +} virConnectDomainEventChannelLifecycleState; + +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN = 0, /* unknown state change reason */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED, /* state changed due to domain start */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL, /* channel state changed */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST +# endif +} virConnectDomainEventChannelLifecycleReason; + +/** + * virConnectDomainEventChannelLifecycleCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @channelName: the name of the channel on which the event occurred + * @state: new state of the guest channel, one of virConnectDomainEventChannelLifecycleState + * @reason: reason for state change; one of virConnectDomainEventChannelLifecycleReason + * @opaque: application specified data + * + * This callback occurs when libvirt detects a change in the state of a guest + * serial channel. The hypervisor must support serial notification, and this is + * currently limited to modern versions of qemu. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventChannelLifecycleCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque); /** * VIR_DOMAIN_EVENT_CALLBACK: @@ -4006,6 +4049,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION = 20, /* virConnectDomainEventMigrationIterationCallback */ VIR_DOMAIN_EVENT_ID_JOB_COMPLETED = 21, /* virConnectDomainEventJobCompletedCallback */ VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */ + VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE = 23, /* virConnectDomainEventChannelLifecycleCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 63ae9e1..f971a0d 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -55,6 +55,7 @@ static virClassPtr virDomainEventPMClass; static virClassPtr virDomainQemuMonitorEventClass; static virClassPtr virDomainEventTunableClass; static virClassPtr virDomainEventAgentLifecycleClass; +static virClassPtr virDomainEventChannelLifecycleClass; static virClassPtr virDomainEventDeviceAddedClass; static virClassPtr virDomainEventMigrationIterationClass; static virClassPtr virDomainEventJobCompletedClass; @@ -75,6 +76,7 @@ static void virDomainEventPMDispose(void *obj); static void virDomainQemuMonitorEventDispose(void *obj); static void virDomainEventTunableDispose(void *obj); static void virDomainEventAgentLifecycleDispose(void *obj); +static void virDomainEventChannelLifecycleDispose(void *obj); static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); @@ -241,6 +243,16 @@ struct _virDomainEventAgentLifecycle { typedef struct _virDomainEventAgentLifecycle virDomainEventAgentLifecycle; typedef virDomainEventAgentLifecycle *virDomainEventAgentLifecyclePtr; +struct _virDomainEventChannelLifecycle { + virDomainEvent parent; + + char *channelName; + int state; + int reason; +}; +typedef struct _virDomainEventChannelLifecycle virDomainEventChannelLifecycle; +typedef virDomainEventChannelLifecycle *virDomainEventChannelLifecyclePtr; + struct _virDomainEventMigrationIteration { virDomainEvent parent; @@ -367,6 +379,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventAgentLifecycle), virDomainEventAgentLifecycleDispose))) return -1; + if (!(virDomainEventChannelLifecycleClass = + virClassNew(virDomainEventClass, + "virDomainEventChannelLifecycle", + sizeof(virDomainEventChannelLifecycle), + virDomainEventChannelLifecycleDispose))) + return -1; if (!(virDomainEventMigrationIterationClass = virClassNew(virDomainEventClass, "virDomainEventMigrationIteration", @@ -557,6 +575,15 @@ virDomainEventAgentLifecycleDispose(void *obj) }; static void +virDomainEventChannelLifecycleDispose(void *obj) +{ + virDomainEventChannelLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->channelName); +}; + +static void virDomainEventMigrationIterationDispose(void *obj) { virDomainEventMigrationIterationPtr event = obj; @@ -1460,6 +1487,56 @@ virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, } static virObjectEventPtr +virDomainEventChannelLifecycleNew(int id, + const char *name, + const unsigned char *uuid, + const char *channelName, + int state, + int reason) +{ + virDomainEventChannelLifecyclePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventChannelLifecycleClass, + VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE, + id, name, uuid))) + return NULL; + + if (VIR_STRDUP(ev->channelName, channelName) < 0) { + virObjectUnref(ev); + return NULL; + } + + ev->state = state; + ev->reason = reason; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromObj(virDomainObjPtr obj, + const char *channelName, + int state, + int reason) +{ + return virDomainEventChannelLifecycleNew(obj->def->id, obj->def->name, + obj->def->uuid, channelName, state, reason); +} + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromDom(virDomainPtr dom, + const char *channelName, + int state, + int reason) +{ + return virDomainEventChannelLifecycleNew(dom->id, dom->name, dom->uuid, + channelName, state, reason); +} + + +static virObjectEventPtr virDomainEventMigrationIterationNew(int id, const char *name, const unsigned char *uuid, @@ -1812,6 +1889,18 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE: + { + virDomainEventChannelLifecyclePtr channelLifecycleEvent; + channelLifecycleEvent = (virDomainEventChannelLifecyclePtr)event; + ((virConnectDomainEventChannelLifecycleCallback)cb)(conn, dom, + channelLifecycleEvent->channelName, + channelLifecycleEvent->state, + channelLifecycleEvent->reason, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_DEVICE_ADDED: { virDomainEventDeviceAddedPtr deviceAddedEvent; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 54fa879..3a689b3 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -217,6 +217,18 @@ virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, int reason); virObjectEventPtr +virDomainEventChannelLifecycleNewFromObj(virDomainObjPtr obj, + const char *channelName, + int state, + int reason); + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromDom(virDomainPtr dom, + const char *channelName, + int state, + int reason); + +virObjectEventPtr virDomainEventMigrationIterationNewFromObj(virDomainObjPtr obj, int iteration); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 74dd527..e259687 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -535,6 +535,8 @@ virDomainEventBlockJob2NewFromDom; virDomainEventBlockJob2NewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventBlockJobNewFromObj; +virDomainEventChannelLifecycleNewFromDom; +virDomainEventChannelLifecycleNewFromObj; virDomainEventControlErrorNewFromDom; virDomainEventControlErrorNewFromObj; virDomainEventDeviceAddedNewFromDom; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 38c8414..b464412 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4407,6 +4407,7 @@ processSerialChangedEvent(virQEMUDriverPtr driver, virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainChrDeviceState newstate; virObjectEventPtr event = NULL; + virObjectEventPtr channelEvent = NULL; virDomainDeviceDef dev; qemuDomainObjPrivatePtr priv = vm->privateData; int rc; @@ -4482,6 +4483,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, qemuDomainEventQueue(driver, event); } + channelEvent = virDomainEventChannelLifecycleNewFromObj(vm, dev.data.chr->target.name, newstate, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL); + qemuDomainEventQueue(driver, channelEvent); + endjob: qemuDomainObjEndJob(driver, vm); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1b67aee..31b5028 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1931,6 +1931,7 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, int agentReason = VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL; qemuMonitorChardevInfoPtr entry; virObjectEventPtr event = NULL; + virObjectEventPtr channelEvent = NULL; char id[32]; if (booted) @@ -1958,6 +1959,11 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, agentReason))) qemuDomainEventQueue(driver, event); + + channelEvent = + virDomainEventChannelLifecycleNewFromObj(vm, chr->target.name, entry->state, agentReason); + qemuDomainEventQueue(driver, channelEvent); + chr->state = entry->state; } } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index a3cd7cd..eb783cb 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -345,6 +345,11 @@ remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog, void *evdata, void *opaque); static void +remoteDomainBuildEventCallbackChannelLifecycle(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); @@ -574,6 +579,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteNodeDeviceBuildEventUpdate, sizeof(remote_node_device_event_update_msg), (xdrproc_t)xdr_remote_node_device_event_update_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE, + remoteDomainBuildEventCallbackChannelLifecycle, + sizeof(remote_domain_event_callback_channel_lifecycle_msg), + (xdrproc_t)xdr_remote_domain_event_callback_channel_lifecycle_msg }, }; static void @@ -5152,6 +5161,29 @@ remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog ATTRIBU static void +remoteDomainBuildEventCallbackChannelLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_channel_lifecycle_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventChannelLifecycleNewFromDom(dom, msg->channelName, msg->state, + msg->reason); + + virObjectUnref(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + + +static void remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index e8382dc..e5227c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3242,6 +3242,15 @@ struct remote_domain_event_callback_agent_lifecycle_msg { int reason; }; +struct remote_domain_event_callback_channel_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + char *channelName; + int state; + int reason; +}; + struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record retStats<REMOTE_DOMAIN_LIST_MAX>; }; @@ -5934,5 +5943,11 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377 + REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE = 378 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index b71accc..7921016 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2689,6 +2689,13 @@ struct remote_domain_event_callback_agent_lifecycle_msg { int state; int reason; }; +struct remote_domain_event_callback_channel_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + remote_string channelName; + int state; + int reason; +}; struct remote_connect_get_all_domain_stats_ret { struct { u_int retStats_len; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 184f64d..e085358 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12637,6 +12637,20 @@ VIR_ENUM_IMPL(virshEventAgentLifecycleReason, N_("domain started"), N_("channel event")) +VIR_ENUM_DECL(virshEventChannelLifecycleState) +VIR_ENUM_IMPL(virshEventChannelLifecycleState, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST, + N_("unknown"), + N_("connected"), + N_("disconnected")) + +VIR_ENUM_DECL(virshEventChannelLifecycleReason) +VIR_ENUM_IMPL(virshEventChannelLifecycleReason, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST, + N_("unknown"), + N_("domain started"), + N_("channel event")) + #define UNKNOWNSTR(str) (str ? str : N_("unsupported value")) static void virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -12656,6 +12670,25 @@ virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, } static void +virshEventChannelLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'channel-lifecycle' for domain %s: name: '%s', " + "state: '%s' reason: '%s'\n"), + virDomainGetName(dom), + channelName, + UNKNOWNSTR(virshEventChannelLifecycleStateTypeToString(state)), + UNKNOWNSTR(virshEventChannelLifecycleReasonTypeToString(reason))); + virshEventPrint(opaque, &buf); +} + +static void virshEventMigrationIterationPrint(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int iteration, @@ -12755,6 +12788,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, { "device-removal-failed", VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, + { "channel-lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(virshEventChannelLifecyclePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 2.7.4 (Apple Git-66)

On Mon, Nov 14, 2016 at 08:19:15AM -0500, Matt Broadstone wrote:
Presently domain events are emitted for the "agent lifecycle" which is a specialization on virtio-serial events specific to the channel named "org.qemu.guest_agent.0". This patch adds a generic event for "channel lifecycles", which emit state change events for all attached channels. --- daemon/remote.c | 42 +++++++++++++++++ examples/object-events/event-test.c | 57 ++++++++++++++++++++++++ include/libvirt/libvirt-domain.h | 44 ++++++++++++++++++ src/conf/domain_event.c | 89 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 12 +++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 6 +++ src/remote/remote_driver.c | 32 +++++++++++++ src/remote/remote_protocol.x | 17 ++++++- src/remote_protocol-structs | 7 +++ tools/virsh-domain.c | 35 +++++++++++++++ 12 files changed, 347 insertions(+), 1 deletion(-)
diff --git a/daemon/remote.c b/daemon/remote.c index e414f92..25b32f2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1086,6 +1086,47 @@ remoteRelayDomainEventAgentLifecycle(virConnectPtr conn,
static int +remoteRelayDomainEventChannelLifecycle(virConnectPtr conn, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_channel_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain channel lifecycle event %s %d, callback %d, " + " name: %s, state %d, reason %d", + dom->name, dom->id, callback->callbackID, channelName, state, reason); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + if (VIR_STRDUP(data.channelName, channelName) < 0) + goto error;
No need for the error, just return -1, it will not allocate anything on error.
+ data.state = state; + data.reason = reason; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_callback_channel_lifecycle_msg, + &data); + + return 0; + error: + VIR_FREE(data.channelName);
So this is not needed either.
diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 730cb8b..da023eb 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -337,6 +337,46 @@ guestAgentLifecycleEventReasonToString(int event) return "unknown"; }
+ +static const char * +guestChannelLifecycleEventStateToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED: + return "Disconnected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED: + return "Connected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST: + break; + } + + return "unknown"; +} + + +static const char * +guestChannelLifecycleEventReasonToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleReason) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN: + return "Unknown"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED: + return "Domain started"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL: + return "Channel event"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST: + break; + } + + return "unknown"; +} + +
Maybe these could be share with the Agent one (with added comment and maybe sanity check (AGENT_..._LAST == CHANNEL_..._LAST)? Not required.
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..9a5c664 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,49 @@ typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, int reason, void *opaque);
+typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED = 1, /* channel connected */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED = 2, /* channel disconnected */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST +# endif +} virConnectDomainEventChannelLifecycleState; + +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN = 0, /* unknown state change reason */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED, /* state changed due to domain start */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL, /* channel state changed */
Good that it's trying to keep the same values (even though it's just for the simplicity of the code few hunks below), but why not do that in previous enum as well? We could then document that agent lifecycle should not be used (because why would you) and we'd just make it back-compat (basically AGENT_ enum values would reference CHANNEL_ enum values). But that's thinking way ahead =)
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 63ae9e1..f971a0d 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -1460,6 +1487,56 @@ virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, }
static virObjectEventPtr +virDomainEventChannelLifecycleNew(int id, + const char *name, + const unsigned char *uuid, + const char *channelName, + int state, + int reason) +{ + virDomainEventChannelLifecyclePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventChannelLifecycleClass, + VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE, + id, name, uuid))) + return NULL; + + if (VIR_STRDUP(ev->channelName, channelName) < 0) { + virObjectUnref(ev); + return NULL; + } + + ev->state = state; + ev->reason = reason; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromObj(virDomainObjPtr obj, + const char *channelName, + int state, + int reason) +{ + return virDomainEventChannelLifecycleNew(obj->def->id, obj->def->name, + obj->def->uuid, channelName, state, reason);
Long line ^^
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 38c8414..b464412 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4482,6 +4483,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, qemuDomainEventQueue(driver, event); }
+ channelEvent = virDomainEventChannelLifecycleNewFromObj(vm, dev.data.chr->target.name, newstate, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL); + qemuDomainEventQueue(driver, channelEvent); +
Create and queue it before the previous agent event since that one might fail and skip this.
endjob: qemuDomainObjEndJob(driver, vm);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1b67aee..31b5028 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1931,6 +1931,7 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, int agentReason = VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL; qemuMonitorChardevInfoPtr entry; virObjectEventPtr event = NULL; + virObjectEventPtr channelEvent = NULL; char id[32];
if (booted)
So you're reusing the constants here (the reason for marking them equal), but can we prevent it from biting us in the future? I don't wxpect many more events to be added, I'm just trying to be safe =)
@@ -1958,6 +1959,11 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, agentReason))) qemuDomainEventQueue(driver, event);
+ + channelEvent = + virDomainEventChannelLifecycleNewFromObj(vm, chr->target.name, entry->state, agentReason);
Long line, split it where the arguments are, not after the equals sign.
+ qemuDomainEventQueue(driver, channelEvent); +
Also this should be emitted only if state != STATE_DEFAULT, you can utilize the if above.
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index e8382dc..e5227c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3242,6 +3242,15 @@ struct remote_domain_event_callback_agent_lifecycle_msg { int reason; };
+struct remote_domain_event_callback_channel_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + char *channelName;
remote_string, not char *
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 184f64d..e085358 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12656,6 +12670,25 @@ virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, }
static void +virshEventChannelLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'channel-lifecycle' for domain %s: name: '%s', "
s/name/channel name/ ?
+ "state: '%s' reason: '%s'\n"), + virDomainGetName(dom), + channelName,
I totally missed the channelName here, please put the format string on new line as well, thanks. Otherwise looks good and I think it should go in with those fixes. Sorry for the wait on the review. Martin
participants (2)
-
Martin Kletzander
-
Matt Broadstone