From d489b04628e9e0ecf5c6e2b4121dfc7a9c3b2015 Mon Sep 17 00:00:00 2001 From: Adam Litke Date: Fri, 22 Jul 2011 13:57:42 +0800 Subject: [PATCH] Asynchronous event for BlockJob completion When an operation started by virDomainBlockPull completes (either with success or with failure), raise an event to indicate the final status. This API allow users to avoid polling on virDomainGetBlockJobInfo if they would prefer to use an 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/remote_protocol-structs: structure definitions for protocol verification * 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 --- daemon/remote.c | 31 +++++++++++++++ include/libvirt/libvirt.h.in | 29 ++++++++++++++ python/libvirt-override-virConnect.py | 12 ++++++ python/libvirt-override.c | 52 +++++++++++++++++++++++++ src/conf/domain_event.c | 56 +++++++++++++++++++++++++++ src/conf/domain_event.h | 9 ++++- src/libvirt_private.syms | 2 + src/qemu/qemu_monitor.c | 13 +++++++ src/qemu/qemu_monitor.h | 10 +++++ src/qemu/qemu_monitor_json.c | 40 +++++++++++++++++++ src/qemu/qemu_process.c | 31 +++++++++++++++ src/remote/remote_driver.c | 31 +++++++++++++++ src/remote/remote_protocol.x | 11 +++++- src/remote_protocol-structs | 7 ++++ 14 files changed, 332 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index b471abc092..939044c781 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -339,6 +339,36 @@ static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventBlockJob(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int type, + int status, + void *opaque) +{ + virNetServerClientPtr client = opaque; + remote_domain_event_block_job_msg data; + + if (!client) + return -1; + + VIR_DEBUG("Relaying domain block job event %s %d %s %i, %i", + dom->name, dom->id, path, type, status); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain(&data.dom, dom); + data.path = (char*)path; + data.type = type; + data.status = status; + + remoteDispatchDomainEventSend(client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB, + (xdrproc_t)xdr_remote_domain_event_block_job_msg, &data); + + return 0; +} + static int remoteRelayDomainEventControlError(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -373,6 +403,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventControlError), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventBlockJob), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 5710ca2867..5771ba7fe6 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2785,6 +2785,34 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainEventGraphicsSubjectPtr subject, void *opaque); +/** + * virConnectDomainEventBlockJobStatus: + * + * The final status of a virDomainBlockPullAll() operation + */ +typedef enum { + VIR_DOMAIN_BLOCK_JOB_COMPLETED = 0, + VIR_DOMAIN_BLOCK_JOB_FAILED = 1, +} virConnectDomainEventBlockJobStatus; + +/** + * virConnectDomainEventBlockJobCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @path: fully-qualified filename of the affected disk + * @type: type of block job (virDomainBlockJobType) + * @status: final status of the operation (virConnectDomainEventBlockJobStatus) + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_BLOCK_JOB with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventBlockJobCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *path, + int type, + int status, + void *opaque); + /** * VIR_DOMAIN_EVENT_CALLBACK: * @@ -2803,6 +2831,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_JOB = 8, /* virConnectDomainEventBlockJobCallback */ /* * 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 eeeedf9a16..65b53420c6 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -113,6 +113,18 @@ authScheme, subject, opaque) return 0 + def dispatchDomainEventBlockPullCallback(self, dom, path, type, status, cbData): + """Dispatches events to python user domain blockJob event callbacks + """ + try: + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, virDomain(self, _obj=dom), path, type, 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 e89bc9760e..db763153a3 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -3582,6 +3582,55 @@ libvirt_virConnectDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSE return ret; } +static int +libvirt_virConnectDomainEventBlockJobCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *path, + int type, + 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*)"OsiiO", + pyobj_dom, path, type, 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) @@ -3636,6 +3685,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_JOB: + cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventBlockJobCallback); + break; } if (!cb) { diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index d43331fb8e..2e0524bd84 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -83,6 +83,11 @@ struct _virDomainEvent { char *authScheme; virDomainEventGraphicsSubjectPtr subject; } graphics; + struct { + char *path; + int type; + int status; + } blockJob; } data; }; @@ -499,6 +504,11 @@ void virDomainEventFree(virDomainEventPtr event) } VIR_FREE(event->data.graphics.subject); } + break; + + case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: + VIR_FREE(event->data.blockJob.path); + break; } VIR_FREE(event->dom.name); @@ -874,6 +884,44 @@ virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj, return ev; } +static virDomainEventPtr +virDomainEventBlockJobNew(int id, const char *name, unsigned char *uuid, + const char *path, int type, int status) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_BLOCK_JOB, + id, name, uuid); + + if (ev) { + if (!(ev->data.blockJob.path = strdup(path))) { + virReportOOMError(); + virDomainEventFree(ev); + return NULL; + } + ev->data.blockJob.type = type; + ev->data.blockJob.status = status; + } + + return ev; +} + +virDomainEventPtr virDomainEventBlockJobNewFromObj(virDomainObjPtr obj, + const char *path, + int type, + int status) +{ + return virDomainEventBlockJobNew(obj->def->id, obj->def->name, + obj->def->uuid, path, type, status); +} + +virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, + const char *path, + int type, + int status) +{ + return virDomainEventBlockJobNew(dom->id, dom->name, dom->uuid, + path, type, status); +} virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom) { @@ -1027,6 +1075,14 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_BLOCK_JOB: + ((virConnectDomainEventBlockJobCallback)cb)(conn, dom, + event->data.blockJob.path, + event->data.blockJob.type, + event->data.blockJob.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 f56408f615..b06be1617b 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -169,7 +169,14 @@ virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj, virDomainEventPtr virDomainEventControlErrorNewFromDom(virDomainPtr dom); virDomainEventPtr virDomainEventControlErrorNewFromObj(virDomainObjPtr obj); - +virDomainEventPtr virDomainEventBlockJobNewFromObj(virDomainObjPtr obj, + const char *path, + int type, + int status); +virDomainEventPtr virDomainEventBlockJobNewFromDom(virDomainPtr dom, + const char *path, + int type, + int status); int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e7d25799df..acf7bb1edc 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -414,6 +414,8 @@ virDomainWatchdogModelTypeToString; # domain_event.h +virDomainEventBlockJobNewFromObj; +virDomainEventBlockJobNewFromDom; virDomainEventCallbackListAdd; virDomainEventCallbackListAddID; virDomainEventCallbackListCount; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 5fe6d0598e..db6107c247 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -957,6 +957,19 @@ int qemuMonitorEmitGraphics(qemuMonitorPtr mon, return ret; } +int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, + const char *diskAlias, + int type, + int status) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, ret, domainBlockJob, mon->vm, + diskAlias, type, status); + return ret; +} + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a93905eb0d..f241c9e4d3 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -117,6 +117,11 @@ struct _qemuMonitorCallbacks { const char *authScheme, const char *x509dname, const char *saslUsername); + int (*domainBlockJob)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *diskAlias, + int type, + int status); }; @@ -179,6 +184,11 @@ int qemuMonitorEmitGraphics(qemuMonitorPtr mon, const char *authScheme, const char *x509dname, const char *saslUsername); +int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, + const char *diskAlias, + int type, + int status); + int qemuMonitorStartCPUs(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index b19f993fe6..b7a6a129ea 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 qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data); struct { const char *type; @@ -71,6 +72,7 @@ struct { { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, + { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJob, }, }; @@ -678,6 +680,44 @@ static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValueP qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT); } +static void qemuMonitorJSONHandleBlockJob(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *device; + const char *type_str; + int type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; + unsigned long long offset, len; + int status = VIR_DOMAIN_BLOCK_JOB_FAILED; + + if ((device = virJSONValueObjectGetString(data, "device")) == NULL) { + VIR_WARN("missing device in block job event"); + goto out; + } + + if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0) { + VIR_WARN("missing offset in block job event"); + goto out; + } + + if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) { + VIR_WARN("missing len in block job event"); + goto out; + } + + if ((type_str = virJSONValueObjectGetString(data, "type")) == NULL) { + VIR_WARN("missing type in block job event"); + goto out; + } + + if (STREQ(type_str, "stream")) + type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; + + if (offset != 0 && offset == len) + status = VIR_DOMAIN_BLOCK_JOB_COMPLETED; + +out: + qemuMonitorEmitBlockJob(mon, device, type, status); +} + int qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index a0ac4fa3f6..646215e0ac 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -662,6 +662,36 @@ qemuProcessHandleIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED, return 0; } +static int +qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *diskAlias, + int type, + 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 = virDomainEventBlockJobNewFromObj(vm, path, type, status); + } + + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} static int qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED, @@ -779,6 +809,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainWatchdog = qemuProcessHandleWatchdog, .domainIOError = qemuProcessHandleIOError, .domainGraphics = qemuProcessHandleGraphics, + .domainBlockJob = qemuProcessHandleBlockJob, }; static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index d96b75eb12..ec4133b2bc 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -223,6 +223,11 @@ remoteDomainBuildEventControlError(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); +static void +remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + static virNetClientProgramEvent remoteDomainEvents[] = { { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, remoteDomainBuildEventRTCChange, @@ -256,6 +261,10 @@ static virNetClientProgramEvent remoteDomainEvents[] = { remoteDomainBuildEventControlError, sizeof(remote_domain_event_control_error_msg), (xdrproc_t)xdr_remote_domain_event_control_error_msg }, + { REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB, + remoteDomainBuildEventBlockJob, + sizeof(remote_domain_event_block_job_msg), + (xdrproc_t)xdr_remote_domain_event_block_job_msg }, }; enum virDrvOpenRemoteFlags { @@ -3095,6 +3104,28 @@ remoteDomainBuildEventIOErrorReason(virNetClientProgramPtr prog ATTRIBUTE_UNUSED remoteDomainEventQueue(priv, event); } +static void +remoteDomainBuildEventBlockJob(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_domain_event_block_job_msg *msg = evdata; + virDomainPtr dom; + virDomainEventPtr event = NULL; + + dom = get_nonnull_domain(conn, msg->dom); + if (!dom) + return; + + event = virDomainEventBlockJobNewFromDom(dom, msg->path, msg->type, + msg->status); + + virDomainFree(dom); + + remoteDomainEventQueue(priv, event); +} static void remoteDomainBuildEventGraphics(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ed1fc57be6..8f68808737 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1972,6 +1972,13 @@ struct remote_domain_event_graphics_msg { remote_domain_event_graphics_identity subject; }; +struct remote_domain_event_block_job_msg { + remote_nonnull_domain dom; + remote_nonnull_string path; + int type; + int status; +}; + struct remote_domain_managed_save_args { remote_nonnull_domain dom; unsigned int flags; @@ -2466,7 +2473,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_BLOCK_JOB_ABORT = 237, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_BLOCK_JOB_INFO = 238, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_BLOCK_JOB_SET_SPEED = 239, /* autogen autogen */ - REMOTE_PROC_DOMAIN_BLOCK_PULL = 240 /* autogen autogen */ + REMOTE_PROC_DOMAIN_BLOCK_PULL = 240, /* autogen autogen */ + + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB = 241 /* 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 c10fae4e06..91b3ca53b2 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1479,6 +1479,12 @@ struct remote_domain_event_graphics_msg { remote_domain_event_graphics_identity * subject_val; } subject; }; +struct remote_domain_event_block_job_msg { + remote_nonnull_domain dom; + remote_nonnull_string path; + int type; + int status; +}; struct remote_domain_managed_save_args { remote_nonnull_domain dom; u_int flags; @@ -1929,4 +1935,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_BLOCK_JOB_INFO = 238, REMOTE_PROC_DOMAIN_BLOCK_JOB_SET_SPEED = 239, REMOTE_PROC_DOMAIN_BLOCK_PULL = 240, + REMOTE_PROC_DOMAIN_EVENT_BLOCK_JOB = 241, };