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, };