]> xenbits.xensource.com Git - libvirt.git/commitdiff
Asynchronous event for BlockPull completion
authorAdam Litke <agl@us.ibm.com>
Tue, 14 Jun 2011 14:36:53 +0000 (09:36 -0500)
committerEric Blake <eblake@redhat.com>
Wed, 15 Jun 2011 04:48:42 +0000 (22:48 -0600)
When an operation started by virDomainBlockPullAll completes (either with
success or with failure), raise an event to indicate the final status.  This
allows an API user to avoid polling on virDomainBlockPullInfo if they would
prefer to use the event mechanism.

* daemon/remote.c: Dispatch events to client
* include/libvirt/libvirt.h.in: Define event ID and callback signature
* src/conf/domain_event.c, src/conf/domain_event.h,
  src/libvirt_private.syms: Extend API to handle the new event
* src/qemu/qemu_driver.c: Connect to the QEMU monitor event
  for block_stream completion and emit a libvirt block pull event
* src/remote/remote_driver.c: Receive and dispatch events to application
* src/remote/remote_protocol.x: Wire protocol definition for the event
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c: Watch for BLOCK_STREAM_COMPLETED event
  from QEMU monitor

Signed-off-by: Adam Litke <agl@us.ibm.com>
14 files changed:
daemon/remote.c
include/libvirt/libvirt.h.in
python/libvirt-override-virConnect.py
python/libvirt-override.c
src/conf/domain_event.c
src/conf/domain_event.h
src/libvirt_private.syms
src/qemu/qemu_monitor.c
src/qemu/qemu_monitor.h
src/qemu/qemu_monitor_json.c
src/qemu/qemu_process.c
src/remote/remote_driver.c
src/remote/remote_protocol.x
src/remote_protocol-structs

index ac0285a499cc139778fb7d26bac73ccf5da03f5e..1914b25f8185a278e131197f38dc826c4109aae8 100644 (file)
@@ -396,6 +396,37 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static int remoteRelayDomainEventBlockPull(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                           virDomainPtr dom,
+                                           const char *path,
+                                           int status,
+                                           void *opaque)
+{
+    struct qemud_client *client = opaque;
+    remote_domain_event_block_pull_msg data;
+
+    if (!client)
+        return -1;
+
+    VIR_DEBUG("Relaying domain block pull event %s %d %s %i", dom->name, dom->id, path, status);
+
+    virMutexLock(&client->lock);
+
+    /* build return data */
+    memset(&data, 0, sizeof data);
+    make_nonnull_domain(&data.dom, dom);
+    data.path = (char*)path;
+    data.status = status;
+
+    remoteDispatchDomainEventSend(client,
+                                  REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL,
+                                  (xdrproc_t)xdr_remote_domain_event_block_pull_msg, &data);
+
+    virMutexUnlock(&client->lock);
+
+    return 0;
+}
+
 
 static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED,
                                               virDomainPtr dom,
@@ -434,6 +465,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = {
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
     VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError),
+    VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockPull),
 };
 
 verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST);
index 8c4ce59ff8b2fd852411bdfd850c6997567c33b5..8ceafe51aee8d770fc6ebfd496bba4bd49cd2192 100644 (file)
@@ -2665,6 +2665,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn,
                                                       virDomainEventGraphicsSubjectPtr subject,
                                                       void *opaque);
 
+/**
+ * virConnectDomainEventBlockPullStatus:
+ *
+ * The final status of a virDomainBlockPullAll() operation
+ */
+typedef enum {
+    VIR_DOMAIN_BLOCK_PULL_COMPLETED = 0,
+    VIR_DOMAIN_BLOCK_PULL_FAILED = 1,
+} virConnectDomainEventBlockPullStatus;
+
+/**
+ * virConnectDomainEventBlockPullCallback:
+ * @conn: connection object
+ * @dom: domain on which the event occurred
+ * @path: fully-qualified filename of the affected disk
+ * @status: final status of the operation (virConnectDomainEventBlockPullStatus)
+ *
+ * The callback signature to use when registering for an event of type
+ * VIR_DOMAIN_EVENT_ID_BLOCK_PULL with virConnectDomainEventRegisterAny()
+ */
+typedef void (*virConnectDomainEventBlockPullCallback)(virConnectPtr conn,
+                                                       virDomainPtr dom,
+                                                       const char *path,
+                                                       int status,
+                                                       void *opaque);
+
 /**
  * VIR_DOMAIN_EVENT_CALLBACK:
  *
@@ -2683,6 +2709,7 @@ typedef enum {
     VIR_DOMAIN_EVENT_ID_GRAPHICS = 5,        /* virConnectDomainEventGraphicsCallback */
     VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON = 6, /* virConnectDomainEventIOErrorReasonCallback */
     VIR_DOMAIN_EVENT_ID_CONTROL_ERROR = 7,   /* virConnectDomainEventGenericCallback */
+    VIR_DOMAIN_EVENT_ID_BLOCK_PULL = 8,      /* virConnectDomainEventBlockPullCallback */
 
     /*
      * NB: this enum value will increase over time as new events are
index e344303b1534e28753480507e3df04b0b64c9cca..362be75d2b75db1a604749bc3910fc5020700773 100644 (file)
         except AttributeError:
             pass
 
+    def dispatchDomainEventBlockPullCallback(self, dom, path, status, cbData):
+        """Dispatches events to python user domain blockPull event callbacks
+        """
+        try:
+            cb = cbData["cb"]
+            opaque = cbData["opaque"]
+
+            cb(self, virDomain(self, _obj=dom), path, status, opaque)
+            return 0
+        except AttributeError:
+            pass
+
     def domainEventDeregisterAny(self, callbackID):
         """Removes a Domain Event Callback. De-registering for a
            domain callback will disable delivery of this event type """
index cbdbc5415ad0d08771e89451a9ace6bb895bd4e3..61d5b7db6614da9acd25e1c3a71ebe7d9d923666 100644 (file)
@@ -3476,6 +3476,54 @@ libvirt_virConnectDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSE
     return ret;
 }
 
+static int
+libvirt_virConnectDomainEventBlockPullCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                               virDomainPtr dom,
+                                               const char *path,
+                                               int status,
+                                               void *opaque)
+{
+    PyObject *pyobj_cbData = (PyObject*)opaque;
+    PyObject *pyobj_dom;
+    PyObject *pyobj_ret;
+    PyObject *pyobj_conn;
+    PyObject *dictKey;
+    int ret = -1;
+
+    LIBVIRT_ENSURE_THREAD_STATE;
+
+    /* Create a python instance of this virDomainPtr */
+    virDomainRef(dom);
+    pyobj_dom = libvirt_virDomainPtrWrap(dom);
+    Py_INCREF(pyobj_cbData);
+
+    dictKey = libvirt_constcharPtrWrap("conn");
+    pyobj_conn = PyDict_GetItem(pyobj_cbData, dictKey);
+    Py_DECREF(dictKey);
+
+    /* Call the Callback Dispatcher */
+    pyobj_ret = PyObject_CallMethod(pyobj_conn,
+                                    (char*)"dispatchDomainEventBlockPullCallback",
+                                    (char*)"OsiO",
+                                    pyobj_dom, path, status, pyobj_cbData);
+
+    Py_DECREF(pyobj_cbData);
+    Py_DECREF(pyobj_dom);
+
+    if(!pyobj_ret) {
+#if DEBUG_ERROR
+        printf("%s - ret:%p\n", __FUNCTION__, pyobj_ret);
+#endif
+        PyErr_Print();
+    } else {
+        Py_DECREF(pyobj_ret);
+        ret = 0;
+    }
+
+    LIBVIRT_RELEASE_THREAD_STATE;
+    return ret;
+}
+
 static PyObject *
 libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self,
                                          PyObject * args)
@@ -3534,6 +3582,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject * self,
     case VIR_DOMAIN_EVENT_ID_CONTROL_ERROR:
         cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventGenericCallback);
         break;
+    case VIR_DOMAIN_EVENT_ID_BLOCK_PULL:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBlockPullCallback);
+        break;
     }
 
     if (!cb) {
index fabc1a53368bef13b1d1b93faef23f12d2c09809..a1f1b0f0add244fce0d8bb499c8b74961abc11b3 100644 (file)
@@ -84,6 +84,10 @@ struct _virDomainEvent {
             char *authScheme;
             virDomainEventGraphicsSubjectPtr subject;
         } graphics;
+        struct {
+            char *path;
+            int status;
+        } blockPull;
     } data;
 };
 
@@ -500,6 +504,11 @@ void virDomainEventFree(virDomainEventPtr event)
             }
             VIR_FREE(event->data.graphics.subject);
         }
+        break;
+
+    case VIR_DOMAIN_EVENT_ID_BLOCK_PULL:
+        VIR_FREE(event->data.blockPull.path);
+        break;
     }
 
     VIR_FREE(event->dom.name);
@@ -875,6 +884,41 @@ virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj,
     return ev;
 }
 
+static virDomainEventPtr
+virDomainEventBlockPullNew(int id, const char *name, unsigned char *uuid,
+                           const char *path, int status)
+{
+    virDomainEventPtr ev =
+        virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BLOCK_PULL,
+                                  id, name, uuid);
+
+    if (ev) {
+        if (!(ev->data.blockPull.path = strdup(path))) {
+            virReportOOMError();
+            virDomainEventFree(ev);
+            return NULL;
+        }
+        ev->data.blockPull.status = status;
+    }
+
+    return ev;
+}
+
+virDomainEventPtr virDomainEventBlockPullNewFromObj(virDomainObjPtr obj,
+                                                    const char *path,
+                                                    int status)
+{
+    return virDomainEventBlockPullNew(obj->def->id, obj->def->name,
+                                                 obj->def->uuid, path, status);
+}
+
+virDomainEventPtr virDomainEventBlockPullNewFromDom(virDomainPtr dom,
+                                                    const char *path,
+                                                    int status)
+{
+    return virDomainEventBlockPullNew(dom->id, dom->name, dom->uuid,
+                                                 path, status);
+}
 
 virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom)
 {
@@ -1028,6 +1072,13 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn,
              cbopaque);
         break;
 
+    case VIR_DOMAIN_EVENT_ID_BLOCK_PULL:
+        ((virConnectDomainEventBlockPullCallback)cb)(conn, dom,
+                                                     event->data.blockPull.path,
+                                                     event->data.blockPull.status,
+                                                     cbopaque);
+        break;
+
     default:
         VIR_WARN("Unexpected event ID %d", event->eventID);
         break;
index 68dc8a8d3d144c6be59b489f9c6ab909e45d0a40..a80868b7245988845fe7a8d9633992f680f13799 100644 (file)
@@ -170,7 +170,12 @@ virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj,
 virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom);
 virDomainEventPtr virDomainEventControlErrorNewFromObj(virDomainObjPtr obj);
 
-
+virDomainEventPtr virDomainEventBlockPullNewFromObj(virDomainObjPtr obj,
+                                                    const char *path,
+                                                    int status);
+virDomainEventPtr virDomainEventBlockPullNewFromDom(virDomainPtr dom,
+                                                    const char *path,
+                                                    int status);
 
 int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue,
                             virDomainEventPtr event);
index 635193839b8219eb0787d861e9175a1591ac6eef..ab110a3587d0a557464e07cf63d6b4e0f7ce7d75 100644 (file)
@@ -377,6 +377,8 @@ virDomainWatchdogModelTypeToString;
 
 
 # domain_event.h
+virDomainEventBlockPullNewFromObj;
+virDomainEventBlockPullNewFromDom;
 virDomainEventCallbackListAdd;
 virDomainEventCallbackListAddID;
 virDomainEventCallbackListCount;
index 0488dbb5c417bf013cb0783bba57119cdddede41..14289212ebd19161b9a0cbc85dd3639cf7ae2df5 100644 (file)
@@ -957,6 +957,18 @@ int qemuMonitorEmitGraphics(qemuMonitorPtr mon,
     return ret;
 }
 
+int qemuMonitorEmitBlockPull(qemuMonitorPtr mon,
+                             const char *diskAlias,
+                             int status)
+{
+    int ret = -1;
+    VIR_DEBUG("mon=%p", mon);
+
+    QEMU_MONITOR_CALLBACK(mon, ret, domainBlockPull, mon->vm,
+                          diskAlias, status);
+    return ret;
+}
+
 
 
 int qemuMonitorSetCapabilities(qemuMonitorPtr mon)
index 6fea700dbf2769e3adcf71010aaa9e48809fb16e..3bb026900eb83c51a0ceabbd6decedb4b03d1aed 100644 (file)
@@ -117,6 +117,10 @@ struct _qemuMonitorCallbacks {
                           const char *authScheme,
                           const char *x509dname,
                           const char *saslUsername);
+    int (*domainBlockPull)(qemuMonitorPtr mon,
+                           virDomainObjPtr vm,
+                           const char *diskAlias,
+                           int status);
 };
 
 
@@ -179,6 +183,10 @@ int qemuMonitorEmitGraphics(qemuMonitorPtr mon,
                             const char *authScheme,
                             const char *x509dname,
                             const char *saslUsername);
+int qemuMonitorEmitBlockPull(qemuMonitorPtr mon,
+                             const char *diskAlias,
+                             int status);
+
 
 
 int qemuMonitorStartCPUs(qemuMonitorPtr mon,
index 5048d09242c9743b8409bbf56e8ebe52097331b1..56ec65bb18561c308666c2231976b73fd4dc82ad 100644 (file)
@@ -56,6 +56,7 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr dat
 static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleBlockPull(qemuMonitorPtr mon, virJSONValuePtr data);
 
 struct {
     const char *type;
@@ -71,6 +72,7 @@ struct {
     { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
     { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
     { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
+    { "BLOCK_STREAM_COMPLETED", qemuMonitorJSONHandleBlockPull, },
 };
 
 
@@ -679,6 +681,34 @@ static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValueP
     qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
 }
 
+static void qemuMonitorJSONHandleBlockPull(qemuMonitorPtr mon, virJSONValuePtr data)
+{
+    const char *device;
+    unsigned long long offset, len;
+    int status = VIR_DOMAIN_BLOCK_PULL_FAILED;
+
+    if ((device = virJSONValueObjectGetString(data, "device")) == NULL) {
+        VIR_WARN("missing device in disk io error event");
+        goto out;
+    }
+
+    if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0) {
+        VIR_WARN("missing offset in block pull event");
+        goto out;
+    }
+
+    if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) {
+        VIR_WARN("missing len in block pull event");
+        goto out;
+    }
+
+    if (offset != 0 && offset == len)
+        status = VIR_DOMAIN_BLOCK_PULL_COMPLETED;
+
+out:
+    qemuMonitorEmitBlockPull(mon, device, status);
+}
+
 
 int
 qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
index 6bdc2987061aff0321d54b3158d0c27993f08249..683841700ce8880a1df4982113665d956ea92055 100644 (file)
@@ -561,6 +561,35 @@ qemuProcessHandleIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static int
+qemuProcessHandleBlockPull(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                           virDomainObjPtr vm,
+                           const char *diskAlias,
+                           int status)
+{
+    struct qemud_driver *driver = qemu_driver;
+    virDomainEventPtr event = NULL;
+    const char *path;
+    virDomainDiskDefPtr disk;
+
+    virDomainObjLock(vm);
+    disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias);
+
+    if (disk) {
+        path = disk->src;
+        event = virDomainEventBlockPullNewFromObj(vm, path, status);
+    }
+
+    virDomainObjUnlock(vm);
+
+    if (event) {
+        qemuDriverLock(driver);
+        qemuDomainEventQueue(driver, event);
+        qemuDriverUnlock(driver);
+    }
+
+    return 0;
+}
 
 static int
 qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
@@ -678,6 +707,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
     .domainWatchdog = qemuProcessHandleWatchdog,
     .domainIOError = qemuProcessHandleIOError,
     .domainGraphics = qemuProcessHandleGraphics,
+    .domainBlockPull = qemuProcessHandleBlockPull,
 };
 
 static int
index 86a0d1ee647bc1d94a624e3025d2c241f47a1a6c..f4b43e0df418131043c003a24ffda491d99849e6 100644 (file)
@@ -3857,6 +3857,32 @@ remoteDomainReadEventIOErrorReason(virConnectPtr conn, XDR *xdr)
     return event;
 }
 
+static virDomainEventPtr
+remoteDomainReadEventBlockPull(virConnectPtr conn, XDR *xdr)
+{
+    remote_domain_event_block_pull_msg msg;
+    virDomainPtr dom;
+    virDomainEventPtr event = NULL;
+    memset (&msg, 0, sizeof msg);
+
+    /* unmarshall parameters, and process it*/
+    if (! xdr_remote_domain_event_block_pull_msg(xdr, &msg) ) {
+        remoteError(VIR_ERR_RPC, "%s",
+                    _("unable to demarshall block_pull event"));
+        return NULL;
+    }
+
+    dom = get_nonnull_domain(conn,msg.dom);
+    if (!dom)
+        return NULL;
+
+    event = virDomainEventBlockPullNewFromDom(dom, msg.path, msg.status);
+    xdr_free ((xdrproc_t) &xdr_remote_domain_event_block_pull_msg, (char *) &msg);
+
+    virDomainFree(dom);
+    return event;
+}
+
 
 static virDomainEventPtr
 remoteDomainReadEventGraphics(virConnectPtr conn, XDR *xdr)
@@ -5581,6 +5607,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv,
         event = remoteDomainReadEventControlError(conn, xdr);
         break;
 
+    case REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL:
+        event = remoteDomainReadEventBlockPull(conn, xdr);
+        break;
+
     default:
         VIR_DEBUG("Unexpected event proc %d", hdr->proc);
         break;
index 41369f45d4d5638f7a153107c4c97045079e0bf1..0dd8b09aff73facc35be6994be66326ddf6a3340 100644 (file)
@@ -1923,6 +1923,12 @@ struct remote_domain_event_graphics_msg {
     remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>;
 };
 
+struct remote_domain_event_block_pull_msg {
+    remote_nonnull_domain dom;
+    remote_nonnull_string path;
+    int status;
+};
+
 struct remote_domain_managed_save_args {
     remote_nonnull_domain dom;
     unsigned int flags;
@@ -2398,7 +2404,8 @@ enum remote_procedure {
     REMOTE_PROC_DOMAIN_BLOCK_PULL_ALL = 230, /* autogen autogen */
 
     REMOTE_PROC_DOMAIN_BLOCK_PULL_ABORT = 231, /* autogen autogen */
-    REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO = 232 /* skipgen skipgen */
+    REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO = 232, /* skipgen skipgen */
+    REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL = 233 /* skipgen skipgen */
 
     /*
      * Notice how the entries are grouped in sets of 10 ?
index e5f39e9d458fb48969ae2961dc61f2ceb52f4802..3abf220420c321d6ca999e9af4c1488093d6f336 100644 (file)
@@ -1421,6 +1421,11 @@ struct remote_domain_event_graphics_msg {
                 remote_domain_event_graphics_identity * subject_val;
         } subject;
 };
+struct remote_domain_event_block_pull_msg {
+        remote_nonnull_domain      dom;
+        remote_nonnull_string      path;
+        int                        status;
+};
 struct remote_domain_managed_save_args {
         remote_nonnull_domain      dom;
         u_int                      flags;