diff --git a/daemon/remote.c b/daemon/remote.c index 17783fa652..f5ca2acc98 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1099,6 +1099,43 @@ remoteRelayDomainEventMigrationIteration(virConnectPtr conn, } +static int +remoteRelayDomainEventJobCompleted(virConnectPtr conn, + virDomainPtr dom, + virTypedParameterPtr params, + int nparams, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_job_completed_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain migration completed event %s %d, " + "callback %d, params %p %d", + dom->name, dom->id, callback->callbackID, params, nparams); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + if (virTypedParamsSerialize(params, nparams, + (virTypedParameterRemotePtr *) &data.params.params_val, + &data.params.params_len, + VIR_TYPED_PARAM_STRING_OKAY) < 0) + return -1; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED, + (xdrproc_t)xdr_remote_domain_event_callback_job_completed_msg, + &data); + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), @@ -1121,6 +1158,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventAgentLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceAdded), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 79c25df9e5..8ea3df6138 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3320,6 +3320,29 @@ typedef void (*virConnectDomainEventMigrationIterationCallback)(virConnectPtr co int iteration, void *opaque); +/** + * virConnectDomainEventJobCompletedCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @params: job statistics stored as an array of virTypedParameter + * @nparams: size of the params array + * @opaque: application specific data + * + * This callback occurs when a job (such as migration) running on the domain + * is completed. The params array will contain statistics of the just completed + * job as virDomainGetJobStats would return. The callback must not free @params + * (the array will be freed once the callback finishes). + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_JOB_COMPLETED with + * virConnectDomainEventRegisterAny(). + */ +typedef void (*virConnectDomainEventJobCompletedCallback)(virConnectPtr conn, + virDomainPtr dom, + virTypedParameterPtr params, + int nparams, + void *opaque); + /** * VIR_DOMAIN_TUNABLE_CPU_VCPUPIN: * @@ -3620,6 +3643,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE = 18,/* virConnectDomainEventAgentLifecycleCallback */ VIR_DOMAIN_EVENT_ID_DEVICE_ADDED = 19, /* virConnectDomainEventDeviceAddedCallback */ VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION = 20, /* virConnectDomainEventMigrationIterationCallback */ + VIR_DOMAIN_EVENT_ID_JOB_COMPLETED = 21, /* virConnectDomainEventJobCompletedCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 5cb3ccd5b4..a9107e5f5e 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -57,7 +57,7 @@ static virClassPtr virDomainEventTunableClass; static virClassPtr virDomainEventAgentLifecycleClass; static virClassPtr virDomainEventDeviceAddedClass; static virClassPtr virDomainEventMigrationIterationClass; - +static virClassPtr virDomainEventJobCompletedClass; static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); @@ -76,6 +76,7 @@ static void virDomainEventTunableDispose(void *obj); static void virDomainEventAgentLifecycleDispose(void *obj); static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); +static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -246,6 +247,14 @@ struct _virDomainEventMigrationIteration { typedef struct _virDomainEventMigrationIteration virDomainEventMigrationIteration; typedef virDomainEventMigrationIteration *virDomainEventMigrationIterationPtr; +struct _virDomainEventJobCompleted { + virDomainEvent parent; + + virTypedParameterPtr params; + int nparams; +}; +typedef struct _virDomainEventJobCompleted virDomainEventJobCompleted; +typedef virDomainEventJobCompleted *virDomainEventJobCompletedPtr; static int virDomainEventsOnceInit(void) @@ -352,6 +361,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventMigrationIteration), virDomainEventMigrationIterationDispose))) return -1; + if (!(virDomainEventJobCompletedClass = + virClassNew(virDomainEventClass, + "virDomainEventJobCompleted", + sizeof(virDomainEventJobCompleted), + virDomainEventJobCompletedDispose))) + return -1; return 0; } @@ -519,6 +534,15 @@ virDomainEventMigrationIterationDispose(void *obj) VIR_DEBUG("obj=%p", event); }; +static void +virDomainEventJobCompletedDispose(void *obj) +{ + virDomainEventJobCompletedPtr event = obj; + VIR_DEBUG("obj=%p", event); + + virTypedParamsFree(event->params, event->nparams); +} + static void * virDomainEventNew(virClassPtr klass, @@ -1394,6 +1418,53 @@ virDomainEventMigrationIterationNewFromDom(virDomainPtr dom, iteration); } +/* This function consumes @params, the caller must not free it. + */ +static virObjectEventPtr +virDomainEventJobCompletedNew(int id, + const char *name, + const unsigned char *uuid, + virTypedParameterPtr params, + int nparams) +{ + virDomainEventJobCompletedPtr ev; + + if (virDomainEventsInitialize() < 0) + goto error; + + if (!(ev = virDomainEventNew(virDomainEventJobCompletedClass, + VIR_DOMAIN_EVENT_ID_JOB_COMPLETED, + id, name, uuid))) + goto error; + + ev->params = params; + ev->nparams = nparams; + + return (virObjectEventPtr) ev; + + error: + virTypedParamsFree(params, nparams); + return NULL; +} + +virObjectEventPtr +virDomainEventJobCompletedNewFromObj(virDomainObjPtr obj, + virTypedParameterPtr params, + int nparams) +{ + return virDomainEventJobCompletedNew(obj->def->id, obj->def->name, + obj->def->uuid, params, nparams); +} + +virObjectEventPtr +virDomainEventJobCompletedNewFromDom(virDomainPtr dom, + virTypedParameterPtr params, + int nparams) +{ + return virDomainEventJobCompletedNew(dom->id, dom->name, dom->uuid, + params, nparams); +} + /* 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 @@ -1685,6 +1756,18 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_JOB_COMPLETED: + { + virDomainEventJobCompletedPtr ev; + + ev = (virDomainEventJobCompletedPtr) event; + ((virConnectDomainEventJobCompletedCallback) cb)(conn, dom, + ev->params, + ev->nparams, + 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 b7cddb5a50..3eb13c8187 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -217,6 +217,16 @@ virObjectEventPtr virDomainEventMigrationIterationNewFromDom(virDomainPtr dom, int iteration); +virObjectEventPtr +virDomainEventJobCompletedNewFromObj(virDomainObjPtr obj, + virTypedParameterPtr params, + int nparams); + +virObjectEventPtr +virDomainEventJobCompletedNewFromDom(virDomainPtr dom, + virTypedParameterPtr params, + int nparams); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 9491845758..ca32dc124b 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -8873,7 +8873,9 @@ virDomainGetJobInfo(virDomainPtr domain, virDomainJobInfoPtr info) * when libvirtd is restarted. Note that time information returned for * completed migrations may be completely irrelevant unless both source and * destination hosts have synchronized time (i.e., NTP daemon is running on - * both of them). + * both of them). The statistics of a completed job can also be obtained by + * listening to a VIR_DOMAIN_EVENT_ID_JOB_COMPLETED event (on the source host + * in case of a migration job). * * Returns 0 in case of success and -1 in case of failure. */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3a1b9e1fe1..c8a2f0a9b1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -508,6 +508,8 @@ virDomainEventIOErrorNewFromDom; virDomainEventIOErrorNewFromObj; virDomainEventIOErrorReasonNewFromDom; virDomainEventIOErrorReasonNewFromObj; +virDomainEventJobCompletedNewFromDom; +virDomainEventJobCompletedNewFromObj; virDomainEventLifecycleNew; virDomainEventLifecycleNewFromDef; virDomainEventLifecycleNewFromDom; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d84d2c1cda..2daa50786d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -343,6 +343,11 @@ remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -519,6 +524,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteConnectNotifyEventConnectionClosed, sizeof(remote_connect_event_connection_closed_msg), (xdrproc_t)xdr_remote_connect_event_connection_closed_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED, + remoteDomainBuildEventCallbackJobCompleted, + sizeof(remote_domain_event_callback_job_completed_msg), + (xdrproc_t)xdr_remote_domain_event_callback_job_completed_msg }, }; static void @@ -5383,6 +5392,39 @@ remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog ATT } +static void +remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, + void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_job_completed_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + virTypedParameterPtr params = NULL; + int nparams = 0; + + if (virTypedParamsDeserialize((virTypedParameterRemotePtr) msg->params.params_val, + msg->params.params_len, + REMOTE_DOMAIN_JOB_STATS_MAX, + ¶ms, &nparams) < 0) + return; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) { + virTypedParamsFree(params, nparams); + return; + } + + event = virDomainEventJobCompletedNewFromDom(dom, params, nparams); + + virObjectUnref(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 c6dd51e57c..952686c5a6 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3232,6 +3232,12 @@ struct remote_domain_event_callback_migration_iteration_msg { int iteration; }; +struct remote_domain_event_callback_job_completed_msg { + int callbackID; + remote_nonnull_domain dom; + remote_typed_param params; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -5728,5 +5734,11 @@ enum remote_procedure { * @generate: none * @acl: none */ - REMOTE_PROC_CONNECT_EVENT_CONNECTION_CLOSED = 362 + REMOTE_PROC_CONNECT_EVENT_CONNECTION_CLOSED = 362, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED = 363 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 6c1cf5d16e..070338c400 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2700,6 +2700,14 @@ struct remote_domain_event_callback_migration_iteration_msg { remote_nonnull_domain dom; int iteration; }; +struct remote_domain_event_callback_job_completed_msg { + int callbackID; + remote_nonnull_domain dom; + struct { + u_int params_len; + remote_typed_param * params_val; + } params; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3063,4 +3071,5 @@ enum remote_procedure { REMOTE_PROC_CONNECT_CLOSE_CALLBACK_REGISTER = 360, REMOTE_PROC_CONNECT_CLOSE_CALLBACK_UNREGISTER = 361, REMOTE_PROC_CONNECT_EVENT_CONNECTION_CLOSED = 362, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_JOB_COMPLETED = 363, }; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 979f115e84..f66faca14f 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -11975,6 +11975,29 @@ virshEventMigrationIterationPrint(virConnectPtr conn ATTRIBUTE_UNUSED, virshEventPrint(opaque, &buf); } +static void +virshEventJobCompletedPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + virTypedParameterPtr params, + int nparams, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + size_t i; + char *value; + + virBufferAsprintf(&buf, _("event 'job-completed' for domain %s:\n"), + virDomainGetName(dom)); + for (i = 0; i < nparams; i++) { + value = virTypedParameterToString(¶ms[i]); + if (value) { + virBufferAsprintf(&buf, "\t%s: %s\n", params[i].field, value); + VIR_FREE(value); + } + } + virshEventPrint(opaque, &buf); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), }, @@ -12016,6 +12039,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceAddedPrint), }, { "migration-iteration", VIR_DOMAIN_EVENT_CALLBACK(virshEventMigrationIterationPrint), }, + { "job-completed", + VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks));