When changing the metadata via virDomainSetMetadata, we now
emit an event to notify the app of changes. This is useful
when co-ordinating different applications read/write of
custom metadata.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
daemon/remote.c | 45 +++++++++++++++++++
examples/object-events/event-test.c | 35 +++++++++++++++
include/libvirt/libvirt-domain.h | 20 +++++++++
src/bhyve/bhyve_driver.c | 7 +++
src/conf/domain_event.c | 86 +++++++++++++++++++++++++++++++++++++
src/conf/domain_event.h | 10 +++++
src/libvirt_private.syms | 2 +
src/lxc/lxc_driver.c | 6 +++
src/qemu/qemu_driver.c | 6 +++
src/remote/remote_driver.c | 33 +++++++++++++-
src/remote/remote_protocol.x | 15 ++++++-
src/remote_protocol-structs | 7 +++
src/test/test_driver.c | 6 +++
tools/virsh-domain.c | 25 +++++++++++
14 files changed, 301 insertions(+), 2 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index 23c9de4..3d837d8 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 730cb8b..f2316d7 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 6362947..e303140 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 17f8524..e42ec47 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 63ae9e1..da503f3 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 54fa879..1933f47 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 2d23e46..3b8d227 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 a7bc9f0..04a4b8c 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 b359e77..2c9ef95 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -18069,6 +18069,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 46da06f..c161a56 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -355,6 +355,11 @@ remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr
prog,
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,
void *evdata, void *opaque);
@@ -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),
@@ -5121,7 +5130,7 @@
remoteDomainBuildEventCallbackDeviceRemovalFailed(virNetClientProgramPtr prog AT
void *evdata, void *opaque)
{
virConnectPtr conn = opaque;
- remote_domain_event_callback_device_added_msg *msg = evdata;
+ remote_domain_event_callback_device_removal_failed_msg *msg = evdata;
struct private_data *priv = conn->privateData;
virDomainPtr dom;
virObjectEventPtr event = NULL;
@@ -5249,6 +5258,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,
void *evdata, void *opaque)
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index b846ef2..f268e1c 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 41bc3bd..2fc9e46 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 de92a01..6820298 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 3a6fa5c..f8f5d9c 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));
--
2.9.3