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
This commit is contained in:
Adam Litke 2011-07-22 13:57:42 +08:00 committed by Daniel Veillard
parent f50750b2d0
commit d489b04628
14 changed files with 332 additions and 2 deletions

View File

@ -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);

View File

@ -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

View File

@ -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 """

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -414,6 +414,8 @@ virDomainWatchdogModelTypeToString;
# domain_event.h
virDomainEventBlockJobNewFromObj;
virDomainEventBlockJobNewFromDom;
virDomainEventCallbackListAdd;
virDomainEventCallbackListAddID;
virDomainEventCallbackListCount;

View File

@ -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)

View File

@ -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,

View 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 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,

View File

@ -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

View File

@ -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,

View File

@ -1972,6 +1972,13 @@ struct remote_domain_event_graphics_msg {
remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>;
};
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 ?

View File

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