diff --git a/daemon/remote.c b/daemon/remote.c index ac0285a499..1914b25f81 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -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); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 8c4ce59ff8..8ceafe51ae 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -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 diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index e344303b15..362be75d2b 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -124,6 +124,18 @@ 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 """ diff --git a/python/libvirt-override.c b/python/libvirt-override.c index cbdbc5415a..61d5b7db66 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -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) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index fabc1a5336..a1f1b0f0ad 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -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; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 68dc8a8d3d..a80868b724 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -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); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 635193839b..ab110a3587 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -377,6 +377,8 @@ virDomainWatchdogModelTypeToString; # domain_event.h +virDomainEventBlockPullNewFromObj; +virDomainEventBlockPullNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; virDomainEventCallbackListCount; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 0488dbb5c4..14289212eb 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -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) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 6fea700dbf..3bb026900e 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -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, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 5048d09242..56ec65bb18 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -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, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 6bdc298706..683841700c 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -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 diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 86a0d1ee64..f4b43e0df4 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -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; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 41369f45d4..0dd8b09aff 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1923,6 +1923,12 @@ struct remote_domain_event_graphics_msg { remote_domain_event_graphics_identity 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; 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 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index e5f39e9d45..3abf220420 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -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;