diff --git a/daemon/remote.c b/daemon/remote.c index 23c9de429a..3d837d8590 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1223,6 +1223,50 @@ remoteRelayDomainEventDeviceRemovalFailed(virConnectPtr conn, } +static int +remoteRelayDomainEventMetadataChange(virConnectPtr conn, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_metadata_change_msg data; + char **nsurip; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain metadata change %s %d %d %s, callback %d", + dom->name, dom->id, type, NULLSTR(nsuri), callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + + data.type = type; + if (nsuri) { + if (VIR_ALLOC(nsurip) < 0) + return -1; + if (VIR_STRDUP(*nsurip, nsuri) < 0) { + VIR_FREE(nsurip); + return -1; + } + data.nsuri = nsurip; + } + + make_nonnull_domain(&data.dom, dom); + data.callbackID = callback->callbackID; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE, + (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg, + &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), @@ -1248,6 +1292,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange), }; 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 730cb8ba22..f2316d79f5 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -917,6 +917,40 @@ myDomainEventDeviceRemovalFailedCallback(virConnectPtr conn ATTRIBUTE_UNUSED, } +static const char * +metadataTypeToStr(int status) +{ + switch ((virDomainMetadataType) status) { + case VIR_DOMAIN_METADATA_DESCRIPTION: + return "description"; + + case VIR_DOMAIN_METADATA_TITLE: + return "title"; + + case VIR_DOMAIN_METADATA_ELEMENT: + return "element"; + + case VIR_DOMAIN_METADATA_LAST: + break; + } + + return "unknown"; +} + +static int +myDomainEventMetadataChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque ATTRIBUTE_UNUSED) +{ + const char *typestr = metadataTypeToStr(type); + printf("%s EVENT: Domain %s(%d) metadata type: %s (%s)\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), typestr, nsuri ? nsuri : "n/a"); + return 0; +} + + static void myFreeFunc(void *opaque) @@ -971,6 +1005,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_METADATA_CHANGE, myDomainEventMetadataChangeCallback), }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 6362947087..e303140a23 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3770,6 +3770,25 @@ typedef void (*virConnectDomainEventDeviceRemovalFailedCallback)(virConnectPtr c const char *devAlias, void *opaque); +/** + * virConnectDomainEventMetadataChangeCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @type: a value from virDomainMetadataTypea + * @nsuri: XML namespace URI + * @opaque: application specified data + * + * This callback is triggered when the domain XML metadata is changed + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_METADATA_CHANGE with virConnectDomainEventRegisterAny(). + */ +typedef void (*virConnectDomainEventMetadataChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque); + /** * virConnectDomainEventMigrationIterationCallback: @@ -4195,6 +4214,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_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 17f85245d1..e42ec479eb 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -1124,6 +1124,13 @@ bhyveDomainSetMetadata(virDomainPtr dom, privconn->xmlopt, BHYVE_STATE_DIR, BHYVE_CONFIG_DIR, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + virObjectEventStateQueue(privconn->domainEventState, ev); + } + + cleanup: virObjectUnref(caps); virObjectUnlock(vm); diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 63ae9e1a02..da503f3ee9 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -59,6 +59,7 @@ static virClassPtr virDomainEventDeviceAddedClass; static virClassPtr virDomainEventMigrationIterationClass; static virClassPtr virDomainEventJobCompletedClass; static virClassPtr virDomainEventDeviceRemovalFailedClass; +static virClassPtr virDomainEventMetadataChangeClass; static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); @@ -79,6 +80,7 @@ static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDeviceRemovalFailedDispose(void *obj); +static void virDomainEventMetadataChangeDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -266,6 +268,15 @@ struct _virDomainEventDeviceRemovalFailed { typedef struct _virDomainEventDeviceRemovalFailed virDomainEventDeviceRemovalFailed; typedef virDomainEventDeviceRemovalFailed *virDomainEventDeviceRemovalFailedPtr; +struct _virDomainEventMetadataCange { + virDomainEvent parent; + + int type; + char *nsuri; +}; +typedef struct _virDomainEventMetadataCange virDomainEventMetadataChange; +typedef virDomainEventMetadataChange *virDomainEventMetadataChangePtr; + static int @@ -385,6 +396,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventDeviceRemovalFailed), virDomainEventDeviceRemovalFailedDispose))) return -1; + if (!(virDomainEventMetadataChangeClass = + virClassNew(virDomainEventClass, + "virDomainEventMetadataChange", + sizeof(virDomainEventMetadataChange), + virDomainEventMetadataChangeDispose))) + return -1; return 0; } @@ -573,6 +590,16 @@ virDomainEventJobCompletedDispose(void *obj) } +static void +virDomainEventMetadataChangeDispose(void *obj) +{ + virDomainEventMetadataChangePtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->nsuri); +} + + static void * virDomainEventNew(virClassPtr klass, int eventID, @@ -1600,6 +1627,53 @@ virDomainEventTunableNewFromDom(virDomainPtr dom, } +static virObjectEventPtr +virDomainEventMetadataChangeNew(int id, + const char *name, + unsigned char *uuid, + int type, + const char *nsuri) +{ + virDomainEventMetadataChangePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventMetadataChangeClass, + VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, + id, name, uuid))) + return NULL; + + ev->type = type; + if (nsuri && VIR_STRDUP(ev->nsuri, nsuri) < 0) + goto error; + + return (virObjectEventPtr)ev; + + error: + virObjectUnref(ev); + return NULL; +} + +virObjectEventPtr +virDomainEventMetadataChangeNewFromObj(virDomainObjPtr obj, + int type, + const char *nsuri) +{ + return virDomainEventMetadataChangeNew(obj->def->id, obj->def->name, + obj->def->uuid, type, nsuri); +} + +virObjectEventPtr +virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, + int type, + const char *nsuri) +{ + return virDomainEventMetadataChangeNew(dom->id, dom->name, dom->uuid, + type, nsuri); +} + + static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, @@ -1857,6 +1931,18 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_METADATA_CHANGE: + { + virDomainEventMetadataChangePtr metadataChangeEvent; + + metadataChangeEvent = (virDomainEventMetadataChangePtr)event; + ((virConnectDomainEventMetadataChangeCallback)cb)(conn, dom, + metadataChangeEvent->type, + metadataChangeEvent->nsuri, + 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 54fa87991a..1933f47240 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -234,6 +234,16 @@ virDomainEventJobCompletedNewFromDom(virDomainPtr dom, virTypedParameterPtr params, int nparams); +virObjectEventPtr +virDomainEventMetadataChangeNewFromObj(virDomainObjPtr obj, + int type, + const char *nsuri); + +virObjectEventPtr +virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, + int type, + const char *nsuri); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f07a219a82..2624559065 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -558,6 +558,8 @@ virDomainEventLifecycleNew; virDomainEventLifecycleNewFromDef; virDomainEventLifecycleNewFromDom; virDomainEventLifecycleNewFromObj; +virDomainEventMetadataChangeNewFromDom; +virDomainEventMetadataChangeNewFromObj; virDomainEventMigrationIterationNewFromDom; virDomainEventMigrationIterationNewFromObj; virDomainEventPMSuspendDiskNewFromDom; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index a7bc9f06b4..04a4b8c2a6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -5379,6 +5379,12 @@ lxcDomainSetMetadata(virDomainPtr dom, driver->xmlopt, cfg->stateDir, cfg->configDir, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + virObjectEventStateQueue(driver->domainEventState, ev); + } + virLXCDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 675a4d0e7e..89fd6b22a9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -18072,6 +18072,12 @@ qemuDomainSetMetadata(virDomainPtr dom, driver->xmlopt, cfg->stateDir, cfg->configDir, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + qemuDomainEventQueue(driver, ev); + } + qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 46da06f0a4..716111939c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -354,6 +354,11 @@ remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventCallbackMetadataChange(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -558,6 +563,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackDeviceRemovalFailed, sizeof(remote_domain_event_callback_device_removal_failed_msg), (xdrproc_t)xdr_remote_domain_event_callback_device_removal_failed_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE, + remoteDomainBuildEventCallbackMetadataChange, + sizeof(remote_domain_event_callback_metadata_change_msg), + (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg }, { REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, remoteStoragePoolBuildEventLifecycle, sizeof(remote_storage_pool_event_lifecycle_msg), @@ -5248,6 +5257,28 @@ remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog ATTRIBUTE } +static void +remoteDomainBuildEventCallbackMetadataChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_metadata_change_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventMetadataChangeNewFromDom(dom, msg->type, msg->nsuri ? *msg->nsuri : NULL); + + 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 b846ef2138..f268e1c0d5 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3353,6 +3353,13 @@ struct remote_domain_set_guest_vcpus_args { }; +struct remote_domain_event_callback_metadata_change_msg { + int callbackID; + remote_nonnull_domain dom; + int type; + remote_string nsuri; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -5952,5 +5959,11 @@ enum remote_procedure { * @priority: high * @acl: storage_vol:read */ - REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378 + REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 41bc3bd247..2fc9e460f6 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2800,6 +2800,12 @@ struct remote_domain_set_guest_vcpus_args { int state; u_int flags; }; +struct remote_domain_event_callback_metadata_change_msg { + int callbackID; + remote_nonnull_domain dom; + int type; + remote_string nsuri +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3179,4 +3185,5 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376, REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379, }; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index de92a013a7..68202983a1 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -2721,6 +2721,12 @@ static int testDomainSetMetadata(virDomainPtr dom, privconn->caps, privconn->xmlopt, NULL, NULL, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(privdom, type, uri); + testObjectEventQueue(privconn, ev); + } + virDomainObjEndAPI(&privdom); return ret; } diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 3a6fa5c700..f8f5d9c662 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12750,6 +12750,29 @@ virshEventDeviceRemovalFailedPrint(virConnectPtr conn ATTRIBUTE_UNUSED, virshEventPrint(opaque, &buf); } +VIR_ENUM_DECL(virshEventMetadataChangeType) +VIR_ENUM_IMPL(virshEventMetadataChangeType, + VIR_DOMAIN_METADATA_LAST, + N_("description"), + N_("title"), + N_("element")) + +static void +virshEventMetadataChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'metdata-change' for domain %s: %s %s\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)), + NULLSTR(nsuri)); + virshEventPrint(opaque, &buf); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", @@ -12796,6 +12819,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, { "device-removal-failed", VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, + { "metadata-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks));