diff --git a/daemon/remote.c b/daemon/remote.c index 1d7082ea84..18f493d97f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1008,6 +1008,41 @@ remoteRelayDomainEventTunable(virConnectPtr conn, } +static int +remoteRelayDomainEventAgentLifecycle(virConnectPtr conn, + virDomainPtr dom, + int state, + int reason, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_agent_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain agent lifecycle event %s %d, callback %d, " + " state %d, reason %d", + dom->name, dom->id, callback->callbackID, state, reason); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + data.state = state; + data.reason = reason; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg, + &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -1027,6 +1062,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemoved), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob2), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventTunable), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventAgentLifecycle), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 1fac2a3938..298d7f41c5 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3332,6 +3332,46 @@ typedef void (*virConnectDomainEventTunableCallback)(virConnectPtr conn, void *opaque); +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_CONNECTED = 1, /* agent connected */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_DISCONNECTED = 2, /* agent disconnected */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST +# endif +} virConnectDomainEventAgentLifecycleState; + +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_UNKNOWN = 0, /* unknown state change reason */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED = 1, /* state changed due to domain start */ + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL = 2, /* channel state changed */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_LAST +# endif +} virConnectDomainEventAgentLifecycleReason; + +/** + * virConnectDomainEventAgentLifecycleCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @state: new state of the guest agent, one of virConnectDomainEventAgentLifecycleState + * @reason: reason for state change; one of virConnectDomainEventAgentLifecycleReason + * @opaque: application specified data + * + * This callback occurs when libvirt detects a change in the state of a guest + * agent. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, + virDomainPtr dom, + int state, + int reason, + void *opaque); + + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -3367,6 +3407,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED = 15, /* virConnectDomainEventDeviceRemovedCallback */ VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 = 16, /* virConnectDomainEventBlockJobCallback */ VIR_DOMAIN_EVENT_ID_TUNABLE = 17, /* virConnectDomainEventTunableCallback */ + VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE = 18,/* virConnectDomainEventAgentLifecycleCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 3504b34abd..d1042bfc92 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -54,6 +54,7 @@ static virClassPtr virDomainEventDeviceRemovedClass; static virClassPtr virDomainEventPMClass; static virClassPtr virDomainQemuMonitorEventClass; static virClassPtr virDomainEventTunableClass; +static virClassPtr virDomainEventAgentLifecycleClass; static void virDomainEventDispose(void *obj); @@ -70,6 +71,7 @@ static void virDomainEventDeviceRemovedDispose(void *obj); static void virDomainEventPMDispose(void *obj); static void virDomainQemuMonitorEventDispose(void *obj); static void virDomainEventTunableDispose(void *obj); +static void virDomainEventAgentLifecycleDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -215,6 +217,15 @@ struct _virDomainEventTunable { typedef struct _virDomainEventTunable virDomainEventTunable; typedef virDomainEventTunable *virDomainEventTunablePtr; +struct _virDomainEventAgentLifecycle { + virDomainEvent parent; + + int state; + int reason; +}; +typedef struct _virDomainEventAgentLifecycle virDomainEventAgentLifecycle; +typedef virDomainEventAgentLifecycle *virDomainEventAgentLifecyclePtr; + static int virDomainEventsOnceInit(void) @@ -303,6 +314,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventTunable), virDomainEventTunableDispose))) return -1; + if (!(virDomainEventAgentLifecycleClass = + virClassNew(virDomainEventClass, + "virDomainEventAgentLifecycle", + sizeof(virDomainEventAgentLifecycle), + virDomainEventAgentLifecycleDispose))) + return -1; return 0; } @@ -447,6 +464,13 @@ virDomainEventTunableDispose(void *obj) virTypedParamsFree(event->params, event->nparams); } +static void +virDomainEventAgentLifecycleDispose(void *obj) +{ + virDomainEventAgentLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); +}; + static void * virDomainEventNew(virClassPtr klass, @@ -1202,6 +1226,49 @@ virDomainEventDeviceRemovedNewFromDom(virDomainPtr dom, devAlias); } + +static virObjectEventPtr +virDomainEventAgentLifecycleNew(int id, + const char *name, + const unsigned char *uuid, + int state, + int reason) +{ + virDomainEventAgentLifecyclePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventAgentLifecycleClass, + VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, + id, name, uuid))) + return NULL; + + ev->state = state; + ev->reason = reason; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromObj(virDomainObjPtr obj, + int state, + int reason) +{ + return virDomainEventAgentLifecycleNew(obj->def->id, obj->def->name, + obj->def->uuid, state, reason); +} + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, + int state, + int reason) +{ + return virDomainEventAgentLifecycleNew(dom->id, dom->name, dom->uuid, + state, reason); +} + + /* This function consumes the params so caller don't have to care about * freeing it even if error occurs. The reason is to not have to do deep * copy of params. @@ -1459,6 +1526,17 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE: + { + virDomainEventAgentLifecyclePtr agentLifecycleEvent; + agentLifecycleEvent = (virDomainEventAgentLifecyclePtr)event; + ((virConnectDomainEventAgentLifecycleCallback)cb)(conn, dom, + agentLifecycleEvent->state, + agentLifecycleEvent->reason, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_LAST: break; } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index dc0109cf67..534ff9eb4a 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -193,6 +193,15 @@ virDomainEventTunableNewFromDom(virDomainPtr dom, virTypedParameterPtr params, int nparams); +virObjectEventPtr +virDomainEventAgentLifecycleNewFromObj(virDomainObjPtr obj, + int state, + int reason); + +virObjectEventPtr +virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, + int state, + int reason); int virDomainEventStateRegister(virConnectPtr conn, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index daf4dd74fa..f58be3ece1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -437,6 +437,8 @@ virDomainXMLOptionNew; # conf/domain_event.h +virDomainEventAgentLifecycleNewFromDom; +virDomainEventAgentLifecycleNewFromObj; virDomainEventBalloonChangeNewFromDom; virDomainEventBalloonChangeNewFromObj; virDomainEventBlockJob2NewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 04e536062c..88f8743bb1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -334,6 +334,11 @@ remoteDomainBuildEventCallbackTunable(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -489,6 +494,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackTunable, sizeof(remote_domain_event_callback_tunable_msg), (xdrproc_t)xdr_remote_domain_event_callback_tunable_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE, + remoteDomainBuildEventCallbackAgentLifecycle, + sizeof(remote_domain_event_callback_agent_lifecycle_msg), + (xdrproc_t)xdr_remote_domain_event_callback_agent_lifecycle_msg }, }; @@ -5482,6 +5491,28 @@ remoteDomainBuildEventCallbackTunable(virNetClientProgramPtr prog ATTRIBUTE_UNUS } +static void +remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_agent_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 = virDomainEventAgentLifecycleNewFromDom(dom, msg->state, + msg->reason); + + virDomainFree(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ebf453071a..85c95f5daa 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3108,6 +3108,14 @@ struct remote_connect_get_all_domain_stats_args { unsigned int flags; }; +struct remote_domain_event_callback_agent_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + int state; + int reason; +}; + struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record retStats; }; @@ -5506,5 +5514,11 @@ enum remote_procedure { * @generate: none * @acl: connect:write */ - REMOTE_PROC_NODE_ALLOC_PAGES = 347 + REMOTE_PROC_NODE_ALLOC_PAGES = 347, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE = 348 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 362baf99a7..9769b2a073 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2573,6 +2573,12 @@ struct remote_connect_get_all_domain_stats_args { u_int stats; u_int flags; }; +struct remote_domain_event_callback_agent_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + int state; + int reason; +}; struct remote_connect_get_all_domain_stats_ret { struct { u_int retStats_len; @@ -2927,4 +2933,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_BLOCK_COPY = 345, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_TUNABLE = 346, REMOTE_PROC_NODE_ALLOC_PAGES = 347, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_AGENT_LIFECYCLE = 348, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 0572275ffc..4f03956e30 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11672,6 +11672,43 @@ vshEventTunablePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); } +VIR_ENUM_DECL(vshEventAgentLifecycleState) +VIR_ENUM_IMPL(vshEventAgentLifecycleState, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_LAST, + N_("unknown"), + N_("connected"), + N_("disconnected")) + +VIR_ENUM_DECL(vshEventAgentLifecycleReason) +VIR_ENUM_IMPL(vshEventAgentLifecycleReason, + 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 +vshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int state, + int reason, + void *opaque) +{ + vshDomEventData *data = opaque; + + if (!data->loop && *data->count) + return; + vshPrint(data->ctl, + _("event 'agent-lifecycle' for domain %s: state: '%s' reason: '%s'\n"), + virDomainGetName(dom), + UNKNOWNSTR(vshEventAgentLifecycleStateTypeToString(state)), + UNKNOWNSTR(vshEventAgentLifecycleReasonTypeToString(reason))); + + (*data->count)++; + if (!data->loop) + vshEventDone(data->ctl); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(vshEventLifecyclePrint), }, @@ -11707,6 +11744,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(vshEventBlockJobPrint), }, { "tunable", VIR_DOMAIN_EVENT_CALLBACK(vshEventTunablePrint), }, + { "agent-lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(vshEventAgentLifecyclePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks));