]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
lib: Introduce event for tracking disk backing file write threshold
authorPeter Krempa <pkrempa@redhat.com>
Tue, 21 Feb 2017 14:03:07 +0000 (15:03 +0100)
committerPeter Krempa <pkrempa@redhat.com>
Mon, 27 Mar 2017 07:29:57 +0000 (09:29 +0200)
When using thin provisioning, management tools need to resize the disk
in certain cases. To avoid having them to poll disk usage introduce an
event which will be fired when a given offset of the storage is written
by the hypervisor. Together with the API which will be added later, it
will allow registering thresholds for given storage backing volumes and
this event will then notify management if the threshold is exceeded.

daemon/remote.c
examples/object-events/event-test.c
include/libvirt/libvirt-domain.h
src/conf/domain_event.c
src/conf/domain_event.h
src/libvirt_private.syms
src/remote/remote_driver.c
src/remote/remote_protocol.x
src/remote_protocol-structs
tools/virsh-domain.c

index 1c9708c681f9d822e02b5c5dd2095ffbb61f7a97..5696b43c6b65cf308581b59a3045ebd6149190be 100644 (file)
@@ -1295,6 +1295,50 @@ remoteRelayDomainEventMetadataChange(virConnectPtr conn,
 }
 
 
+static int
+remoteRelayDomainEventBlockThreshold(virConnectPtr conn,
+                                     virDomainPtr dom,
+                                     const char *dev,
+                                     const char *path,
+                                     unsigned long long threshold,
+                                     unsigned long long excess,
+                                     void *opaque)
+{
+    daemonClientEventCallbackPtr callback = opaque;
+    remote_domain_event_block_threshold_msg data;
+
+    if (callback->callbackID < 0 ||
+        !remoteRelayDomainEventCheckACL(callback->client, conn, dom))
+        return -1;
+
+    VIR_DEBUG("Relaying domain block threshold event %s %d %s %s %llu %llu, callback %d",
+              dom->name, dom->id, dev, NULLSTR(path), threshold, excess, callback->callbackID);
+
+    /* build return data */
+    memset(&data, 0, sizeof(data));
+    data.callbackID = callback->callbackID;
+    if (VIR_STRDUP(data.dev, dev) < 0)
+        goto error;
+    if (path) {
+        if (VIR_ALLOC(data.path) < 0)
+            goto error;
+        if (VIR_STRDUP(*(data.path), path) < 0)
+            goto error;
+    }
+    data.threshold = threshold;
+    data.excess = excess;
+    make_nonnull_domain(&data.dom, dom);
+
+    remoteDispatchObjectEventSend(callback->client, remoteProgram,
+                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD,
+                                  (xdrproc_t)xdr_remote_domain_event_block_threshold_msg, &data);
+
+    return 0;
+ error:
+    VIR_FREE(data.dev);
+    return -1;
+}
+
 
 static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
@@ -1321,6 +1365,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockThreshold),
 };
 
 verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
index 55c004f93f40de628ae76221bb8ef9a13b2acb3d..12690cac09ce358b292b6b0022f8afc4dcade92b 100644 (file)
@@ -15,6 +15,7 @@
 
 #define ARRAY_CARDINALITY(Array) (sizeof(Array) / sizeof(*(Array)))
 #define STREQ(a, b) (strcmp(a, b) == 0)
+#define NULLSTR(s) ((s) ? (s) : "<null>")
 
 #ifndef ATTRIBUTE_UNUSED
 # define ATTRIBUTE_UNUSED __attribute__((__unused__))
@@ -924,6 +925,23 @@ myDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
 }
 
 
+static int
+myDomainEventBlockThresholdCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                    virDomainPtr dom,
+                                    const char *dev,
+                                    const char *path,
+                                    unsigned long long threshold,
+                                    unsigned long long excess,
+                                    void *opaque ATTRIBUTE_UNUSED)
+{
+    printf("%s EVENT: Domain %s(%d) block threshold callback dev '%s'(%s), "
+           "threshold: '%llu', excess: '%llu'",
+           __func__, virDomainGetName(dom), virDomainGetID(dom),
+           dev, NULLSTR(path), threshold, excess);
+    return 0;
+}
+
+
 static int
 myDomainEventMigrationIterationCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
                                         virDomainPtr dom,
@@ -1053,6 +1071,7 @@ struct domainEventData domainEvents[] = {
     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),
+    DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD, myDomainEventBlockThresholdCallback),
 };
 
 struct storagePoolEventData {
index 620606c6284716dabfe13bf6eeec9adf215298cb..355bbd5151f900b8954443485bb9b6901ac4e34a 100644 (file)
@@ -4274,6 +4274,36 @@ typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn,
                                                             void *opaque);
 
 
+/**
+ * virConnectDomainEventBlockThresholdCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @dev: name associated with the affected disk or storage backing chain
+ *       element
+ * @path: for local storage, the path of the backing chain element
+ * @threshold: threshold offset in bytes
+ * @excess: number of bytes written beyond the threshold
+ * @opaque: application specified data
+ *
+ * The callback occurs when the hypervisor detects that the given storage
+ * element was written beyond the point specified by @threshold. The excess
+ * data size written beyond @threshold is reported by @excess (if supported
+ * by the hypervisor, 0 otherwise). The event is useful for thin-provisioned
+ * storage.
+ *
+ * The threshold size can be set via the virDomainSetBlockThreshold API.
+ *
+ * The callback signature to use when registering for an event of type
+ * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD with virConnectDomainEventRegisterAny()
+ */
+typedef void (*virConnectDomainEventBlockThresholdCallback)(virConnectPtr conn,
+                                                            virDomainPtr dom,
+                                                            const char *dev,
+                                                            const char *path,
+                                                            unsigned long long threshold,
+                                                            unsigned long long excess,
+                                                            void *opaque);
+
 /**
  * VIR_DOMAIN_EVENT_CALLBACK:
  *
@@ -4315,6 +4345,7 @@ typedef enum {
     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 */
+    VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD = 24, /* virConnectDomainEventBlockThresholdCallback */
 
 # ifdef VIR_ENUM_SENTINELS
     VIR_DOMAIN_EVENT_ID_LAST
index da503f3ee9f8e2bd781d791a963bbc7600ed8a67..6243b4262f66e6f87bb440cf4e4e4e55e9c56950 100644 (file)
@@ -60,6 +60,7 @@ static virClassPtr virDomainEventMigrationIterationClass;
 static virClassPtr virDomainEventJobCompletedClass;
 static virClassPtr virDomainEventDeviceRemovalFailedClass;
 static virClassPtr virDomainEventMetadataChangeClass;
+static virClassPtr virDomainEventBlockThresholdClass;
 
 static void virDomainEventDispose(void *obj);
 static void virDomainEventLifecycleDispose(void *obj);
@@ -81,6 +82,7 @@ static void virDomainEventMigrationIterationDispose(void *obj);
 static void virDomainEventJobCompletedDispose(void *obj);
 static void virDomainEventDeviceRemovalFailedDispose(void *obj);
 static void virDomainEventMetadataChangeDispose(void *obj);
+static void virDomainEventBlockThresholdDispose(void *obj);
 
 static void
 virDomainEventDispatchDefaultFunc(virConnectPtr conn,
@@ -277,6 +279,17 @@ struct _virDomainEventMetadataCange {
 typedef struct _virDomainEventMetadataCange virDomainEventMetadataChange;
 typedef virDomainEventMetadataChange *virDomainEventMetadataChangePtr;
 
+struct _virDomainEventBlockThreshold {
+    virDomainEvent parent;
+
+    char *dev;
+    char *path;
+
+    unsigned long long threshold;
+    unsigned long long excess;
+};
+typedef struct _virDomainEventBlockThreshold virDomainEventBlockThreshold;
+typedef virDomainEventBlockThreshold *virDomainEventBlockThresholdPtr;
 
 
 static int
@@ -402,6 +415,12 @@ virDomainEventsOnceInit(void)
                       sizeof(virDomainEventMetadataChange),
                       virDomainEventMetadataChangeDispose)))
         return -1;
+    if (!(virDomainEventBlockThresholdClass =
+          virClassNew(virDomainEventClass,
+                      "virDomainEventBlockThreshold",
+                      sizeof(virDomainEventBlockThreshold),
+                      virDomainEventBlockThresholdDispose)))
+        return -1;
     return 0;
 }
 
@@ -600,6 +619,17 @@ virDomainEventMetadataChangeDispose(void *obj)
 }
 
 
+static void
+virDomainEventBlockThresholdDispose(void *obj)
+{
+    virDomainEventBlockThresholdPtr event = obj;
+    VIR_DEBUG("obj=%p", event);
+
+    VIR_FREE(event->dev);
+    VIR_FREE(event->path);
+}
+
+
 static void *
 virDomainEventNew(virClassPtr klass,
                   int eventID,
@@ -1674,6 +1704,60 @@ virDomainEventMetadataChangeNewFromDom(virDomainPtr dom,
 }
 
 
+static virObjectEventPtr
+virDomainEventBlockThresholdNew(int id,
+                                const char *name,
+                                unsigned char *uuid,
+                                const char *dev,
+                                const char *path,
+                                unsigned long long threshold,
+                                unsigned long long excess)
+{
+    virDomainEventBlockThresholdPtr ev;
+
+    if (virDomainEventsInitialize() < 0)
+        return NULL;
+
+    if (!(ev = virDomainEventNew(virDomainEventBlockThresholdClass,
+                                 VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD,
+                                 id, name, uuid)))
+        return NULL;
+
+    if (VIR_STRDUP(ev->dev, dev) < 0 ||
+        VIR_STRDUP(ev->path, path) < 0) {
+        virObjectUnref(ev);
+        return NULL;
+    }
+    ev->threshold = threshold;
+    ev->excess = excess;
+
+    return (virObjectEventPtr)ev;
+}
+
+virObjectEventPtr
+virDomainEventBlockThresholdNewFromObj(virDomainObjPtr obj,
+                                       const char *dev,
+                                       const char *path,
+                                       unsigned long long threshold,
+                                       unsigned long long excess)
+{
+    return virDomainEventBlockThresholdNew(obj->def->id, obj->def->name,
+                                           obj->def->uuid, dev, path,
+                                           threshold, excess);
+}
+
+virObjectEventPtr
+virDomainEventBlockThresholdNewFromDom(virDomainPtr dom,
+                                       const char *dev,
+                                       const char *path,
+                                       unsigned long long threshold,
+                                       unsigned long long excess)
+{
+    return virDomainEventBlockThresholdNew(dom->id, dom->name, dom->uuid,
+                                           dev, path, threshold, excess);
+}
+
+
 static void
 virDomainEventDispatchDefaultFunc(virConnectPtr conn,
                                   virObjectEventPtr event,
@@ -1943,6 +2027,19 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn,
             goto cleanup;
         }
 
+    case VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD:
+        {
+            virDomainEventBlockThresholdPtr blockThresholdEvent;
+
+            blockThresholdEvent = (virDomainEventBlockThresholdPtr)event;
+            ((virConnectDomainEventBlockThresholdCallback)cb)(conn, dom,
+                                                              blockThresholdEvent->dev,
+                                                              blockThresholdEvent->path,
+                                                              blockThresholdEvent->threshold,
+                                                              blockThresholdEvent->excess,
+                                                              cbopaque);
+            goto cleanup;
+        }
     case VIR_DOMAIN_EVENT_ID_LAST:
         break;
     }
index 1933f4724083080c5e7c3308d67a03529c6a6248..3992a29c5804f5460bfe59a0639f97e5d916c54d 100644 (file)
@@ -244,6 +244,21 @@ virDomainEventMetadataChangeNewFromDom(virDomainPtr dom,
                                        int type,
                                        const char *nsuri);
 
+
+virObjectEventPtr
+virDomainEventBlockThresholdNewFromObj(virDomainObjPtr obj,
+                                       const char *dev,
+                                       const char *path,
+                                       unsigned long long threshold,
+                                       unsigned long long excess);
+
+virObjectEventPtr
+virDomainEventBlockThresholdNewFromDom(virDomainPtr dom,
+                                       const char *dev,
+                                       const char *path,
+                                       unsigned long long threshold,
+                                       unsigned long long excess);
+
 int
 virDomainEventStateRegister(virConnectPtr conn,
                             virObjectEventStatePtr state,
index 010942529afb54ec2562a35375948b228971709b..ead2149dd038493151d6e4e050015e4eccea5294 100644 (file)
@@ -536,6 +536,8 @@ virDomainEventBlockJob2NewFromDom;
 virDomainEventBlockJob2NewFromObj;
 virDomainEventBlockJobNewFromDom;
 virDomainEventBlockJobNewFromObj;
+virDomainEventBlockThresholdNewFromDom;
+virDomainEventBlockThresholdNewFromObj;
 virDomainEventControlErrorNewFromDom;
 virDomainEventControlErrorNewFromObj;
 virDomainEventDeviceAddedNewFromDom;
index 0c8bfeed16d4c5ff46abaafded1314ae13e4b65b..efa47beafbe33187d229e577d7a6c9dfd77a18dd 100644 (file)
@@ -395,6 +395,11 @@ remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                    virNetClientPtr client ATTRIBUTE_UNUSED,
                                    void *evdata, void *opaque);
 
+static void
+remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog,
+                                     virNetClientPtr client,
+                                     void *evdata, void *opaque);
+
 static void
 remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
                                          virNetClientPtr client ATTRIBUTE_UNUSED,
@@ -602,6 +607,10 @@ static virNetClientProgramEvent remoteEvents[] = {
       remoteSecretBuildEventValueChanged,
       sizeof(remote_secret_event_value_changed_msg),
       (xdrproc_t)xdr_remote_secret_event_value_changed_msg },
+    { REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD,
+      remoteDomainBuildEventBlockThreshold,
+      sizeof(remote_domain_event_block_threshold_msg),
+      (xdrproc_t)xdr_remote_domain_event_block_threshold_msg },
 };
 
 static void
@@ -5577,6 +5586,30 @@ remoteSecretGetValue(virSecretPtr secret, size_t *value_size,
 }
 
 
+static void
+remoteDomainBuildEventBlockThreshold(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+                                     virNetClientPtr client ATTRIBUTE_UNUSED,
+                                     void *evdata, void *opaque)
+{
+    virConnectPtr conn = opaque;
+    remote_domain_event_block_threshold_msg *msg = evdata;
+    struct private_data *priv = conn->privateData;
+    virDomainPtr dom;
+    virObjectEventPtr event = NULL;
+
+    if (!(dom = get_nonnull_domain(conn, msg->dom)))
+        return;
+
+    event = virDomainEventBlockThresholdNewFromDom(dom, msg->dev,
+                                                   msg->path ? *msg->path : NULL,
+                                                   msg->threshold, msg->excess);
+
+    virObjectUnref(dom);
+
+    remoteEventQueue(priv, event, msg->callbackID);
+}
+
+
 static int
 remoteStreamSend(virStreamPtr st,
                  const char *data,
index abe63af0703f1cbdbf4cad2abbb8e598aac9707a..39dd2b728ea0a1df025656c3c759d7d0115a5d1e 100644 (file)
@@ -3071,6 +3071,15 @@ struct remote_domain_event_block_job_2_msg {
     int status;
 };
 
+struct remote_domain_event_block_threshold_msg {
+    int callbackID;
+    remote_nonnull_domain dom;
+    remote_nonnull_string dev;
+    remote_string path;
+    unsigned hyper threshold;
+    unsigned hyper excess;
+};
+
 struct remote_domain_event_callback_tunable_msg {
     int callbackID;
     remote_nonnull_domain dom;
@@ -6033,5 +6042,12 @@ enum remote_procedure {
      * @acl: domain:save:!VIR_DOMAIN_AFFECT_CONFIG|VIR_DOMAIN_AFFECT_LIVE
      * @acl: domain:save:VIR_DOMAIN_AFFECT_CONFIG
      */
-    REMOTE_PROC_DOMAIN_SET_VCPU = 384
+    REMOTE_PROC_DOMAIN_SET_VCPU = 384,
+
+    /**
+     * @generate: both
+     * @acl: none
+     */
+    REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385
+
 };
index e1e53d21b2657a768317242dc5ae1ef57676ddae..67e43a4acd28a3bfdcb4b2782cb313556f45bbeb 100644 (file)
@@ -2516,6 +2516,14 @@ struct remote_domain_event_block_job_2_msg {
         int                        type;
         int                        status;
 };
+struct remote_domain_event_block_threshold_msg {
+        int                        callbackID;
+        remote_nonnull_domain      dom;
+        remote_nonnull_string      dev;
+        remote_string              path;
+        uint64_t                   threshold;
+        uint64_t                   excess;
+};
 struct remote_domain_event_callback_tunable_msg {
         int                        callbackID;
         remote_nonnull_domain      dom;
@@ -3217,4 +3225,5 @@ enum remote_procedure {
         REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382,
         REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383,
         REMOTE_PROC_DOMAIN_SET_VCPU = 384,
+        REMOTE_PROC_DOMAIN_EVENT_BLOCK_THRESHOLD = 385,
 };
index ebd4b33deda33b2f9bceff44f1977f1c1cf530ea..d1d17c4520da96f4f44131b902d07aa0e8719725 100644 (file)
@@ -12877,6 +12877,25 @@ virshEventMetadataChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED,
 }
 
 
+static void
+virshEventBlockThresholdPrint(virConnectPtr conn ATTRIBUTE_UNUSED,
+                              virDomainPtr dom,
+                              const char *dev,
+                              const char *path,
+                              unsigned long long threshold,
+                              unsigned long long excess,
+                              void *opaque)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    virBufferAsprintf(&buf, _("event 'block-threshold' for domain %s: "
+                              "dev: %s(%s) %llu %llu\n"),
+                      virDomainGetName(dom),
+                      dev, NULLSTR(path), threshold, excess);
+    virshEventPrint(opaque, &buf);
+}
+
+
 static vshEventCallback vshEventCallbacks[] = {
     { "lifecycle",
       VIR_DOMAIN_EVENT_CALLBACK(virshEventLifecyclePrint), },
@@ -12924,6 +12943,8 @@ static vshEventCallback vshEventCallbacks[] = {
       VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), },
     { "metadata-change",
       VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), },
+    { "block-threshold",
+      VIR_DOMAIN_EVENT_CALLBACK(virshEventBlockThresholdPrint), },
 };
 verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks));