[libvirt] [PATCH] 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 | 41 +++++++++++++++++ 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, 344 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..d720132 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,46 @@ 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 = 1, /* state changed due to domain start */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = 2, /* 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 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 +4046,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..c778c42 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); @@ -538,6 +543,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackAgentLifecycle, sizeof(remote_domain_event_callback_agent_lifecycle_msg), (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_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 }, { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_ADDED, remoteDomainBuildEventCallbackDeviceAdded, sizeof(remote_domain_event_callback_device_added_msg), @@ -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 07, 2016 at 15:48:35 -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 | 41 +++++++++++++++++ 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, 344 insertions(+), 1 deletion(-)
[...]
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..d720132 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,46 @@ 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 = 1, /* state changed due to domain start */
This reason is not used at all and I don't think it even makes sense to be reported for channel events. With guest agent it might make sense and also other possible reasons as guest agent error. With channels you mostly care about the connect and disconnect events. It might make sense to report client connect or disconnect in the future for certain backend types, but I don't think that qemu supports such report currently.
+ VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = 2, /* 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.
You should note that the hypervisor needs to support channel state notifications, otherwise it won't work. (Qemu supporting this has been around for ages now, but we still support versions older than that. Also other hypervisors don't support this currently)
+ * + * 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 +4046,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/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);
I don't think we should emit this event for the guest agent channel. It has a separate one and the channel is reserved for libvirt anyways, so client applications should use the guest agent event for this.
+ 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
[...]
@@ -1958,6 +1959,11 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, agentReason))) qemuDomainEventQueue(driver, event);
+ + channelEvent = + virDomainEventChannelLifecycleNewFromObj(vm, chr->target.name, entry->state, agentReason); + qemuDomainEventQueue(driver, channelEvent);
Same comment as above.
+ chr->state = entry->state; } } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index a3cd7cd..c778c42 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c
[...]
@@ -538,6 +543,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackAgentLifecycle, sizeof(remote_domain_event_callback_agent_lifecycle_msg), (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_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 }, { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_ADDED, remoteDomainBuildEventCallbackDeviceAdded, sizeof(remote_domain_event_callback_device_added_msg),
These are ordered in the way the events were added, and you are not putting yours at the end. Other than the few details looks good. Peter

On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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 | 41 +++++++++++++++++ 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, 344 insertions(+), 1 deletion(-)
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);
I don't think we should emit this event for the guest agent channel. It has a separate one and the channel is reserved for libvirt anyways, so client applications should use the guest agent event for this.
I don't really agree with that. I don't see any compelling reason to special case the guest agent channel - it just creates extra work for client apps who want to see events for all channels. There is no harm in having the guest agent trigger both events, the old special case one, and the new general purpose one. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On Fri, Nov 11, 2016 at 10:18:00 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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. ---
[...]
I don't think we should emit this event for the guest agent channel. It has a separate one and the channel is reserved for libvirt anyways, so client applications should use the guest agent event for this.
I don't really agree with that. I don't see any compelling reason to special case the guest agent channel - it just creates extra work for client apps who want to see events for all channels. There is no harm in having the guest agent trigger both events, the old special case one, and the new general purpose one.
My reasoning is that the clients are not supposed to connect to the channel where the guest agent communicates and thus should not receive any events using this event type. The guest agent event is designed so that it can report errors of the guest agent (e.g. reporting invalid reply, timeout etc.) whereas this does not apply to regular channels where libvirt does not care at all what the state of the guest application is or which protocol they talk. The way libvirt uses the guest agent channel makes it special from point of view of other apps, since libvirt always hogs it and apps are supposed to use libvirt api. This does not apply to generic channels. (E.g, spice channels are special in the very same way). Peter

On Fri, Nov 11, 2016 at 11:27:07AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:18:00 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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. ---
[...]
I don't think we should emit this event for the guest agent channel. It has a separate one and the channel is reserved for libvirt anyways, so client applications should use the guest agent event for this.
I don't really agree with that. I don't see any compelling reason to special case the guest agent channel - it just creates extra work for client apps who want to see events for all channels. There is no harm in having the guest agent trigger both events, the old special case one, and the new general purpose one.
My reasoning is that the clients are not supposed to connect to the channel where the guest agent communicates and thus should not receive any events using this event type.
The guest agent event is designed so that it can report errors of the guest agent (e.g. reporting invalid reply, timeout etc.) whereas this does not apply to regular channels where libvirt does not care at all what the state of the guest application is or which protocol they talk.
The way libvirt uses the guest agent channel makes it special from point of view of other apps, since libvirt always hogs it and apps are supposed to use libvirt api. This does not apply to generic channels. (E.g, spice channels are special in the very same way).
These events are not solely useful for knowing when to connect to the channel. An application which relies on the QEMU guest agent because of the libvirt API calls it makes, may wish to know when the guest agent is running, so it can avoid making libvirt APIs that use it. eg, the app can disable the ability to quiesce filesystems until it sees the event that the agent is running. So I don't really see the guest agent as so special that we must not allow these events to be seen by apps - it just creates extra work for apps to force them to pointlessly register two event callbacks. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On Fri, Nov 11, 2016 at 10:33:11 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:27:07AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:18:00 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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. ---
[...]
These events are not solely useful for knowing when to connect to the channel. An application which relies on the QEMU guest agent because of the libvirt API calls it makes, may wish to know when the guest agent is running, so it can avoid making libvirt APIs that use it.
eg, the app can disable the ability to quiesce filesystems until it sees the event that the agent is running.
So I don't really see the guest agent as so special that we must not allow these events to be seen by apps - it just creates extra work for apps to force them to pointlessly register two event callbacks.
My point was that the guest agent may still be connected but disfunctional from libvirt's point of view. The Agent lifecycle callback should report this, while the channel callback should not. Such apps will need two callbacks anyways.

On Fri, Nov 11, 2016 at 11:41:42AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:33:11 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:27:07AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:18:00 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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. ---
[...]
These events are not solely useful for knowing when to connect to the channel. An application which relies on the QEMU guest agent because of the libvirt API calls it makes, may wish to know when the guest agent is running, so it can avoid making libvirt APIs that use it.
eg, the app can disable the ability to quiesce filesystems until it sees the event that the agent is running.
So I don't really see the guest agent as so special that we must not allow these events to be seen by apps - it just creates extra work for apps to force them to pointlessly register two event callbacks.
My point was that the guest agent may still be connected but disfunctional from libvirt's point of view. The Agent lifecycle callback should report this, while the channel callback should not.
Such apps will need two callbacks anyways.
It depends on what they wish to report. The QEMU guest agent is not the only one that is special from an app POV too - the SPICE guest agent is another one that apps cannot directly connect to, but which would still report events via the channel lifecycle event. There may be others later too, so I'm really not seeing a reason to special case the QEMU guest agent in any way. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On Fri, Nov 11, 2016 at 5:58 AM, Daniel P. Berrange <berrange@redhat.com> wrote:
On Fri, Nov 11, 2016 at 11:41:42AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:33:11 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:27:07AM +0100, Peter Krempa wrote:
On Fri, Nov 11, 2016 at 10:18:00 +0000, Daniel Berrange wrote:
On Fri, Nov 11, 2016 at 11:13:19AM +0100, Peter Krempa wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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. > ---
[...]
These events are not solely useful for knowing when to connect to the channel. An application which relies on the QEMU guest agent because of the libvirt API calls it makes, may wish to know when the guest agent is running, so it can avoid making libvirt APIs that use it.
eg, the app can disable the ability to quiesce filesystems until it sees the event that the agent is running.
So I don't really see the guest agent as so special that we must not allow these events to be seen by apps - it just creates extra work for apps to force them to pointlessly register two event callbacks.
My point was that the guest agent may still be connected but disfunctional from libvirt's point of view. The Agent lifecycle callback should report this, while the channel callback should not.
Such apps will need two callbacks anyways.
It depends on what they wish to report. The QEMU guest agent is not the only one that is special from an app POV too - the SPICE guest agent is another one that apps cannot directly connect to, but which would still report events via the channel lifecycle event. There may be others later too, so I'm really not seeing a reason to special case the QEMU guest agent in any way.
Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|
I'm inclined to agree with Daniel here. Having a generic callback for all paths here doesn't actually adversely affect anything, and is arguably more "correct" in terms of reporting. Rather think of it this way: this current behavior accurately models what I see when monitoring the domstatus file. Matt

On Fri, Nov 11, 2016 at 5:13 AM, Peter Krempa <pkrempa@redhat.com> wrote:
On Mon, Nov 07, 2016 at 15:48:35 -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 | 41 +++++++++++++++++ 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, 344 insertions(+), 1 deletion(-)
[...]
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..d720132 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,46 @@ 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 = 1, /* state changed due to domain start */
This reason is not used at all and I don't think it even makes sense to be reported for channel events. With guest agent it might make sense and also other possible reasons as guest agent error.
This reason is, in fact, being used in qemu_process.c:1925 (qemuProcessRefreshChannelVirtioState). The state is actually aligned with the virConnectDomainEventChannelLifecycleReason enum in order to reuse the existing `agentReason` for emitting this event. I can explicitly add a `channelReason` if that makes the code clearer/more readable.
With channels you mostly care about the connect and disconnect events. It might make sense to report client connect or disconnect in the future for certain backend types, but I don't think that qemu supports such report currently.
I agree that with chanels you generally care about `connected` and `disconnected`, but the real problem here is that there is no framing over the virtio-serial channel. As a result, it's important for developers of guest agents to know when a channel connection is being cycled or started for the first time. Remember that the reason for this patch in the first place is that I am creating an agent myself.
+ VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = 2, /* 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.
You should note that the hypervisor needs to support channel state notifications, otherwise it won't work. (Qemu supporting this has been around for ages now, but we still support versions older than that. Also other hypervisors don't support this currently)
Agreed.
+ * + * 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 +4046,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/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);
I don't think we should emit this event for the guest agent channel. It has a separate one and the channel is reserved for libvirt anyways, so client applications should use the guest agent event for this.
This has been answered in subsequent emails.
+ 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
[...]
@@ -1958,6 +1959,11 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, agentReason))) qemuDomainEventQueue(driver, event);
+ + channelEvent = + virDomainEventChannelLifecycleNewFromObj(vm, chr->target.name, entry->state, agentReason); + qemuDomainEventQueue(driver, channelEvent);
Same comment as above.
+ chr->state = entry->state; } } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index a3cd7cd..c778c42 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c
[...]
@@ -538,6 +543,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackAgentLifecycle, sizeof(remote_domain_event_callback_agent_lifecycle_msg), (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_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 }, { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_ADDED, remoteDomainBuildEventCallbackDeviceAdded, sizeof(remote_domain_event_callback_device_added_msg),
These are ordered in the way the events were added, and you are not putting yours at the end.
Oops, thanks for catching that. I thought I had caught all the ordering issues!
Other than the few details looks good.
Peter

On Fri, Nov 11, 2016 at 07:51:25 -0500, Matt Broadstone wrote:
On Fri, Nov 11, 2016 at 5:13 AM, Peter Krempa <pkrempa@redhat.com> wrote:
On Mon, Nov 07, 2016 at 15:48:35 -0500, Matt Broadstone wrote:
[...]
diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..d720132 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,46 @@ 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 = 1, /* state changed due to domain start */
This reason is not used at all and I don't think it even makes sense to be reported for channel events. With guest agent it might make sense and also other possible reasons as guest agent error.
This reason is, in fact, being used in qemu_process.c:1925 (qemuProcessRefreshChannelVirtioState). The state is actually aligned with the virConnectDomainEventChannelLifecycleReason enum in order to reuse the existing `agentReason` for emitting this event. I can explicitly add a `channelReason` if that makes the code clearer/more readable.
It uses the AGENT_LIFECYCLE value not this declared here. Thus that exact value is not used. If you want to rely on the fact that they stay in sync, please note it in a comment. I've used 'git grep' and thus did not see it.
With channels you mostly care about the connect and disconnect events. It might make sense to report client connect or disconnect in the future for certain backend types, but I don't think that qemu supports such report currently.
I agree that with chanels you generally care about `connected` and `disconnected`, but the real problem here is that there is no framing over the virtio-serial channel. As a result, it's important for developers of guest agents to know when a channel connection is being cycled or started for the first time. Remember that the reason for this patch in the first place is that I am creating an agent myself.
Okay, fair enough. Note that, for channels, this basically will always state that the VM has booted and the channel is disconnected as there is no other option. With the regular guest agent with qemu that did not support the channel state notification we always connected to the agent. Peter
participants (3)
-
Daniel P. Berrange
-
Matt Broadstone
-
Peter Krempa