From 1465876a04798a891c1d2c20eb5d6aee9eafb1d1 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Mon, 30 Jul 2012 17:30:42 +0100 Subject: [PATCH] Bind connection close callback APIs to python binding Add code in the python binding to cope with the new APIs virConnectRegisterCloseCallback and virConnectUnregisterCloseCallback. Also demonstrate their use in the python domain events demo Signed-off-by: Daniel P. Berrange --- .../domain-events/events-python/event-test.py | 14 ++- python/generator.py | 5 +- python/libvirt-override-virConnect.py | 23 ++++ python/libvirt-override.c | 107 ++++++++++++++++++ 4 files changed, 146 insertions(+), 3 deletions(-) diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py index 9fa0ea675e..3fb046e7a3 100644 --- a/examples/domain-events/events-python/event-test.py +++ b/examples/domain-events/events-python/event-test.py @@ -495,6 +495,16 @@ def myDomainEventBalloonChangeCallback(conn, dom, utcoffset, actual): def myDomainEventPMSuspendDiskCallback(conn, dom, reason, opaque): print "myDomainEventPMSuspendDiskCallback: Domain %s(%s) system pmsuspend_disk" % ( dom.name(), dom.ID()) + +run = True + +def myConnectionCloseCallback(conn, reason, opaque): + reasonStrings = ( + "Error", "End-of-file", "Keepalive", "Client", + ) + print "myConnectionCloseCallback: %s: %s" % (conn.getURI(), reasonStrings[reason]) + run = False + def usage(out=sys.stderr): print >>out, "usage: "+os.path.basename(sys.argv[0])+" [-hdl] [uri]" print >>out, " uri will default to qemu:///system" @@ -544,6 +554,8 @@ def main(): if (old_exitfunc): old_exitfunc() sys.exitfunc = exit + vc.registerCloseCallback(myConnectionCloseCallback, None) + #Add 2 callbacks to prove this works with more than just one vc.domainEventRegister(myDomainEventCallback1,None) vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, myDomainEventCallback2, None) @@ -565,7 +577,7 @@ def main(): # of demo we'll just go to sleep. The other option is to # run the event loop in your main thread if your app is # totally event based. - while vc.isAlive() == 1: + while run: time.sleep(1) diff --git a/python/generator.py b/python/generator.py index c76ff2a5b0..e9b92709f2 100755 --- a/python/generator.py +++ b/python/generator.py @@ -425,8 +425,6 @@ skip_impl = ( 'virDomainGetInterfaceParameters', 'virDomainGetCPUStats', 'virDomainGetDiskErrors', - 'virConnectUnregisterCloseCallback', - 'virConnectRegisterCloseCallback', 'virNodeGetMemoryParameters', 'virNodeSetMemoryParameters', 'virNodeGetCPUMap', @@ -476,6 +474,9 @@ skip_function = ( 'virStreamRecv', # overridden in libvirt-override-virStream.py 'virStreamSend', # overridden in libvirt-override-virStream.py + 'virConnectUnregisterCloseCallback', # overriden in virConnect.py + 'virConnectRegisterCloseCallback', # overriden in virConnect.py + # 'Ref' functions have no use for bindings users. "virConnectRef", "virDomainRef", diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py index cb8d892ac8..84d6cc3345 100644 --- a/python/libvirt-override-virConnect.py +++ b/python/libvirt-override-virConnect.py @@ -287,3 +287,26 @@ retlist.append(virSecret(self, _obj=secret_ptr)) return retlist + + def _dispatchCloseCallback(self, reason, cbData): + """Dispatches events to python user close callback""" + cb = cbData["cb"] + opaque = cbData["opaque"] + + cb(self, reason, opaque) + return 0 + + + def unregisterCloseCallback(self): + """Removes a close event callback""" + ret = libvirtmod.virConnectUnregisterCloseCallback(self._o) + if ret == -1: raise libvirtError ('virConnectUnregisterCloseCallback() failed', conn=self) + + def registerCloseCallback(self, cb, opaque): + """Adds a close event callback, providing a notification + when a connection fails / closes""" + cbData = { "cb": cb, "conn": self, "opaque": opaque } + ret = libvirtmod.virConnectRegisterCloseCallback(self._o, cbData) + if ret == -1: + raise libvirtError ('virConnectRegisterCloseCallback() failed', conn=self) + return ret diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 169df11b34..573c0325eb 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -6001,6 +6001,111 @@ libvirt_virConnectDomainEventDeregisterAny(ATTRIBUTE_UNUSED PyObject * self, return py_retval; } + +static void +libvirt_virConnectCloseCallbackDispatch(virConnectPtr conn ATTRIBUTE_UNUSED, + int reason, + void *opaque) +{ + PyObject *pyobj_cbData = (PyObject*)opaque; + PyObject *pyobj_ret; + PyObject *pyobj_conn; + PyObject *dictKey; + + LIBVIRT_ENSURE_THREAD_STATE; + + 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*)"_dispatchCloseCallback", + (char*)"iO", + reason, + pyobj_cbData); + + Py_DECREF(pyobj_cbData); + + if(!pyobj_ret) { + DEBUG("%s - ret:%p\n", __FUNCTION__, pyobj_ret); + PyErr_Print(); + } else { + Py_DECREF(pyobj_ret); + } + + LIBVIRT_RELEASE_THREAD_STATE; +} + +static PyObject * +libvirt_virConnectRegisterCloseCallback(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; /* return value */ + PyObject *pyobj_conn; /* virConnectPtr */ + PyObject *pyobj_cbData; /* hash of callback data */ + virConnectPtr conn; + int ret = 0; + + if (!PyArg_ParseTuple + (args, (char *) "OO:virConnectRegisterCloseCallback", + &pyobj_conn, &pyobj_cbData)) { + DEBUG("%s failed parsing tuple\n", __FUNCTION__); + return VIR_PY_INT_FAIL; + } + + DEBUG("libvirt_virConnectRegisterCloseCallback(%p %p) called\n", + pyobj_conn, pyobj_cbData); + conn = PyvirConnect_Get(pyobj_conn); + + Py_INCREF(pyobj_cbData); + + LIBVIRT_BEGIN_ALLOW_THREADS; + ret = virConnectRegisterCloseCallback(conn, + libvirt_virConnectCloseCallbackDispatch, + pyobj_cbData, + libvirt_virConnectDomainEventFreeFunc); + LIBVIRT_END_ALLOW_THREADS; + + if (ret < 0) { + Py_DECREF(pyobj_cbData); + } + + py_retval = libvirt_intWrap(ret); + return py_retval; +} + +static PyObject * +libvirt_virConnectUnregisterCloseCallback(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_conn; + virConnectPtr conn; + int ret = 0; + + if (!PyArg_ParseTuple + (args, (char *) "O:virConnectDomainEventUnregister", + &pyobj_conn)) + return NULL; + + DEBUG("libvirt_virConnectDomainEventUnregister(%p) called\n", + pyobj_conn); + + conn = PyvirConnect_Get(pyobj_conn); + + LIBVIRT_BEGIN_ALLOW_THREADS; + + ret = virConnectUnregisterCloseCallback(conn, + libvirt_virConnectCloseCallbackDispatch); + + LIBVIRT_END_ALLOW_THREADS; + py_retval = libvirt_intWrap(ret); + return py_retval; +} + static void libvirt_virStreamEventFreeFunc(void *opaque) { @@ -6502,6 +6607,8 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virConnectDomainEventDeregister", libvirt_virConnectDomainEventDeregister, METH_VARARGS, NULL}, {(char *) "virConnectDomainEventRegisterAny", libvirt_virConnectDomainEventRegisterAny, METH_VARARGS, NULL}, {(char *) "virConnectDomainEventDeregisterAny", libvirt_virConnectDomainEventDeregisterAny, METH_VARARGS, NULL}, + {(char *) "virConnectRegisterCloseCallback", libvirt_virConnectRegisterCloseCallback, METH_VARARGS, NULL}, + {(char *) "virConnectUnregisterCloseCallback", libvirt_virConnectUnregisterCloseCallback, METH_VARARGS, NULL}, {(char *) "virStreamEventAddCallback", libvirt_virStreamEventAddCallback, METH_VARARGS, NULL}, {(char *) "virStreamRecv", libvirt_virStreamRecv, METH_VARARGS, NULL}, {(char *) "virStreamSend", libvirt_virStreamSend, METH_VARARGS, NULL}, -- GitLab