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(a)us.ibm.com>
---
daemon/remote.c | 32 ++++++++++++++++++++
include/libvirt/libvirt.h.in | 27 +++++++++++++++++
python/libvirt-override-virConnect.py | 12 ++++++++
python/libvirt-override.c | 51 +++++++++++++++++++++++++++++++++
src/conf/domain_event.c | 50 ++++++++++++++++++++++++++++++++
src/conf/domain_event.h | 7 ++++-
src/libvirt_private.syms | 2 +
src/qemu/qemu_monitor.c | 12 ++++++++
src/qemu/qemu_monitor.h | 8 +++++
src/qemu/qemu_monitor_json.c | 30 +++++++++++++++++++
src/qemu/qemu_process.c | 28 ++++++++++++++++++
src/remote/remote_driver.c | 30 +++++++++++++++++++
src/remote/remote_protocol.x | 9 +++++-
13 files changed, 296 insertions(+), 2 deletions(-)
diff --git a/daemon/remote.c b/daemon/remote.c
index f6aa78e..f45052c 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -378,6 +378,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 virConnectDomainEventGenericCallback domainEventCallbacks[] = {
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle),
@@ -387,6 +418,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] =
{
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics),
VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOErrorReason),
+ 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 9af1b76..c85e204 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2505,6 +2505,32 @@ typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr
conn,
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:
*
* Used to cast the event specific callback into the generic one
@@ -2521,6 +2547,7 @@ typedef enum {
VIR_DOMAIN_EVENT_ID_IO_ERROR = 4, /* virConnectDomainEventIOErrorCallback */
VIR_DOMAIN_EVENT_ID_GRAPHICS = 5, /* virConnectDomainEventGraphicsCallback */
VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON = 6, /*
virConnectDomainEventIOErrorReasonCallback */
+ VIR_DOMAIN_EVENT_ID_BLOCK_PULL = 7, /* 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 e344303..362be75 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 ca90ef0..9950ea8 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -3452,6 +3452,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)
@@ -3507,6 +3555,9 @@ libvirt_virConnectDomainEventRegisterAny(ATTRIBUTE_UNUSED PyObject *
self,
case VIR_DOMAIN_EVENT_ID_GRAPHICS:
cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventGraphicsCallback);
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 688bf6c..94dfb8e 100644
--- a/src/conf/domain_event.c
+++ b/src/conf/domain_event.c
@@ -83,6 +83,10 @@ struct _virDomainEvent {
char *authScheme;
virDomainEventGraphicsSubjectPtr subject;
} graphics;
+ struct {
+ char *path;
+ int status;
+ } blockPull;
} data;
};
@@ -499,6 +503,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);
@@ -782,6 +791,40 @@ 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))) {
+ 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);
+}
/**
* virDomainEventQueueFree:
@@ -932,6 +975,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 c03a159..383b5e6 100644
--- a/src/conf/domain_event.h
+++ b/src/conf/domain_event.h
@@ -154,7 +154,12 @@ virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr
obj,
const char *authScheme,
virDomainEventGraphicsSubjectPtr
subject);
-
+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 e2e706d..e36f23f 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -352,6 +352,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 60e4ee2..3f9afcf 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -893,6 +893,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 62875a3..1a2cd56 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -112,6 +112,10 @@ struct _qemuMonitorCallbacks {
const char *authScheme,
const char *x509dname,
const char *saslUsername);
+ int (*domainBlockPull)(qemuMonitorPtr mon,
+ virDomainObjPtr vm,
+ const char *diskAlias,
+ int status);
};
@@ -173,6 +177,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 bc491e0..dea491f 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, },
};
@@ -675,6 +677,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_WARN0("missing device in disk io error event");
+ goto out;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0)
{
+ VIR_WARN0("missing offset in block pull event");
+ goto out;
+ }
+
+ if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) {
+ VIR_WARN0("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 bd7c932..ab48981 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -515,6 +515,33 @@ 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 blockPullEvent = NULL;
+ const char *path;
+ virDomainDiskDefPtr disk;
+
+ virDomainObjLock(vm);
+ disk = qemuProcessFindDomainDiskByAlias(vm, diskAlias);
+
+ if (disk)
+ path = disk->src;
+ else
+ path = "";
+ blockPullEvent = virDomainEventBlockPullNewFromObj(vm, path, status);
+
+ virDomainObjUnlock(vm);
+ qemuDriverLock(driver);
+ qemuDomainEventQueue(driver, blockPullEvent);
+ qemuDriverUnlock(driver);
+
+ return 0;
+}
static int
qemuProcessHandleGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
@@ -631,6 +658,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 0a885a9..a07ce2d 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -4094,6 +4094,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)
@@ -5603,6 +5629,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data
*priv,
event = remoteDomainReadEventGraphics(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 2f52ceb..8f4e5e4 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -1862,6 +1862,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 flags;
@@ -2218,7 +2224,8 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_BLOCK_PULL_ALL = 211,
REMOTE_PROC_DOMAIN_BLOCK_PULL_ABORT = 212,
- REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO = 213
+ REMOTE_PROC_DOMAIN_GET_BLOCK_PULL_INFO = 213,
+ REMOTE_PROC_DOMAIN_EVENT_BLOCK_PULL = 214
/*
* Notice how the entries are grouped in sets of 10 ?
--
1.7.3