[libvirt] [PATCH] Fix up python bindings for new event callbacks

Daniel P. Berrange berrange at redhat.com
Wed Apr 7 17:37:46 UTC 2010


The generator was disabled for the new event callbacks, since they
need to be hand written. This patch  adds the C and python glue to
expose the new APIs in the python binding. The python example
program is extended to demonstrate of the code

* python/libvirt-override.c: Registration and dispatch of events
   at the C layer
* python/libvirt-override-virConnect.py: Python glue for events
* examples/domain-events/events-python/event-test.py: Demo use
  of new event callbacks
---
 examples/domain-events/events-python/event-test.py |   22 +-
 python/libvirt-override-virConnect.py              |   45 ++
 python/libvirt-override.c                          |  451 ++++++++++++++++++++
 3 files changed, 517 insertions(+), 1 deletions(-)

diff --git a/examples/domain-events/events-python/event-test.py b/examples/domain-events/events-python/event-test.py
index 181b389..0c6e2f0 100644
--- a/examples/domain-events/events-python/event-test.py
+++ b/examples/domain-events/events-python/event-test.py
@@ -412,6 +412,21 @@ def myDomainEventCallback1 (conn, dom, event, detail, opaque):
 def myDomainEventCallback2 (conn, dom, event, detail, opaque):
     print "myDomainEventCallback2 EVENT: Domain %s(%s) %s %d" % (dom.name(), dom.ID(), eventToString(event), detail)
 
+def myDomainEventRebootCallback(conn, dom, opaque):
+    print "myDomainEventRebootCallback: Domain %s(%s)" % (dom.name(), dom.ID())
+
+def myDomainEventRTCChangeCallback(conn, dom, utcoffset, opaque):
+    print "myDomainEventRTCChangeCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), utcoffset)
+
+def myDomainEventWatchdogCallback(conn, dom, action, opaque):
+    print "myDomainEventWatchdogCallback: Domain %s(%s) %d" % (dom.name(), dom.ID(), action)
+
+def myDomainEventIOErrorCallback(conn, dom, srcpath, devalias, action, opaque):
+    print "myDomainEventIOErrorCallback: Domain %s(%s) %s %s %d" % (dom.name(), dom.ID(), srcpath, devalias, action)
+
+def myDomainEventGraphicsCallback(conn, dom, phase, localAddr, remoteAddr, authScheme, subject, opaque):
+    print "myDomainEventGraphicsCallback: Domain %s(%s) %d %s" % (dom.name(), dom.ID(), phase, authScheme)
+
 def usage():
         print "usage: "+os.path.basename(sys.argv[0])+" [uri]"
         print "   uri will default to qemu:///system"
@@ -451,7 +466,12 @@ def main():
 
     #Add 2 callbacks to prove this works with more than just one
     vc.domainEventRegister(myDomainEventCallback1,None)
-    vc.domainEventRegister(myDomainEventCallback2,None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, myDomainEventCallback2, None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_REBOOT, myDomainEventRebootCallback, None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_RTC_CHANGE, myDomainEventRTCChangeCallback, None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_IO_ERROR, myDomainEventIOErrorCallback, None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_WATCHDOG, myDomainEventWatchdogCallback, None)
+    vc.domainEventRegisterAny(None, libvirt.VIR_DOMAIN_EVENT_ID_GRAPHICS, myDomainEventGraphicsCallback, None)
 
     # The rest of your app would go here normally, but for sake
     # of demo we'll just go to sleep. The other option is to
diff --git a/python/libvirt-override-virConnect.py b/python/libvirt-override-virConnect.py
index 1fdf548..444a499 100644
--- a/python/libvirt-override-virConnect.py
+++ b/python/libvirt-override-virConnect.py
@@ -41,3 +41,48 @@
             return 0
         except AttributeError:
             pass
+
+    def dispatchDomainEventLifecycleCallback(self, dom, event, detail, cbData):
+        """Dispatches events to python user domain event callbacks
+        """
+        cb = cbData["cb"]
+        opaque = cbData["opaque"]
+
+        cb(self, virDomain(self, _obj=dom), event, detail, opaque)
+        return 0
+
+    def dispatchDomainEventGenericCallback(self, dom, cbData):
+        """Dispatches events to python user domain event callbacks
+        """
+        try:
+            cb = cbData["cb"]
+            opaque = cbData["opaque"]
+
+            cb(self, virDomain(self, _obj=dom), 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 """
+        try:
+            ret = libvirtmod.virConnectDomainEventDeregisterAny(self._o, callbackID)
+            if ret == -1: raise libvirtError ('virConnectDomainEventDeregisterAny() failed', conn=self)
+            del self.domainEventCallbackID[callbackID]
+        except AttributeError:
+            pass
+
+    def domainEventRegisterAny(self, dom, eventID, cb, opaque):
+        """Adds a Domain Event Callback. Registering for a domain
+           callback will enable delivery of the events """
+        if not hasattr(self, 'domainEventCallbackID'):
+            self.domainEventCallbackID = {}
+        cbData = { "cb": cb, "conn": self, "opaque": opaque }
+        if dom is None:
+            ret = libvirtmod.virConnectDomainEventRegisterAny(self._o, None, eventID, cbData)
+        else:
+            ret = libvirtmod.virConnectDomainEventRegisterAny(self._o, dom._o, eventID, cbData)
+        if ret == -1:
+            raise libvirtError ('virConnectDomainEventRegisterAny() failed', conn=self)
+        self.domainEventCallbackID[ret] = opaque
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index e27bce6..02bc313 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -2761,6 +2761,455 @@ libvirt_virEventInvokeTimeoutCallback(PyObject *self ATTRIBUTE_UNUSED,
     return VIR_PY_INT_SUCCESS;
 }
 
+
+static void
+libvirt_virConnectDomainEventFreeFunc(void *opaque)
+{
+    PyObject *pyobj_conn = (PyObject*)opaque;
+    LIBVIRT_ENSURE_THREAD_STATE;
+    Py_DECREF(pyobj_conn);
+    LIBVIRT_RELEASE_THREAD_STATE;
+}
+
+static int
+libvirt_virConnectDomainEventLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                               virDomainPtr dom,
+                                               int event,
+                                               int detail,
+                                               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*)"dispatchDomainEventLifecycleCallback",
+                                    (char*)"OiiO",
+                                    pyobj_dom,
+                                    event, detail,
+                                    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 int
+libvirt_virConnectDomainEventGenericCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                             virDomainPtr dom,
+                                             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*)"dispatchDomainEventGenericCallback",
+                                    (char*)"OO",
+                                    pyobj_dom, 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 int
+libvirt_virConnectDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                               virDomainPtr dom,
+                                               long long utcoffset,
+                                               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*)"dispatchDomainEventRTCChangeCallback",
+                                    (char*)"OLO",
+                                    pyobj_dom,
+                                    (PY_LONG_LONG)utcoffset,
+                                    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 int
+libvirt_virConnectDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                              virDomainPtr dom,
+                                              int action,
+                                              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*)"dispatchDomainEventWatchdogCallback",
+                                    (char*)"OiO",
+                                    pyobj_dom,
+                                    action,
+                                    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 int
+libvirt_virConnectDomainEventIOErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                             virDomainPtr dom,
+                                             const char *srcPath,
+                                             const char *devAlias,
+                                             int action,
+                                             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*)"dispatchDomainEventIOErrorCallback",
+                                    (char*)"OssiO",
+                                    pyobj_dom,
+                                    srcPath, devAlias, action,
+                                    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 int
+libvirt_virConnectDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                              virDomainPtr dom,
+                                              int phase,
+                                              virDomainEventGraphicsAddressPtr local,
+                                              virDomainEventGraphicsAddressPtr remote,
+                                              const char *authScheme,
+                                              virDomainEventGraphicsSubjectPtr subject,
+                                              void *opaque)
+{
+    PyObject *pyobj_cbData = (PyObject*)opaque;
+    PyObject *pyobj_dom;
+    PyObject *pyobj_ret;
+    PyObject *pyobj_conn;
+    PyObject *dictKey;
+    PyObject *pyobj_local;
+    PyObject *pyobj_remote;
+    PyObject *pyobj_subject;
+    int ret = -1;
+    int i;
+
+    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);
+
+    pyobj_local = PyDict_New();
+    PyDict_SetItem(pyobj_local,
+                   libvirt_constcharPtrWrap("family"),
+                   libvirt_intWrap(local->family));
+    PyDict_SetItem(pyobj_local,
+                   libvirt_constcharPtrWrap("node"),
+                   libvirt_constcharPtrWrap(local->node));
+    PyDict_SetItem(pyobj_local,
+                   libvirt_constcharPtrWrap("service"),
+                   libvirt_constcharPtrWrap(local->service));
+
+    pyobj_remote = PyDict_New();
+    PyDict_SetItem(pyobj_remote,
+                   libvirt_constcharPtrWrap("family"),
+                   libvirt_intWrap(remote->family));
+    PyDict_SetItem(pyobj_remote,
+                   libvirt_constcharPtrWrap("node"),
+                   libvirt_constcharPtrWrap(remote->node));
+    PyDict_SetItem(pyobj_remote,
+                   libvirt_constcharPtrWrap("service"),
+                   libvirt_constcharPtrWrap(remote->service));
+
+    pyobj_subject = PyList_New(subject->nidentity);
+    for (i = 0 ; i < subject->nidentity ; i++) {
+        PyObject *pair = PyTuple_New(2);
+        PyTuple_SetItem(pair, 0, libvirt_constcharPtrWrap(subject->identities[i].type));
+        PyTuple_SetItem(pair, 1, libvirt_constcharPtrWrap(subject->identities[i].name));
+
+        PyList_SetItem(pyobj_subject, i, pair);
+    }
+
+    /* Call the Callback Dispatcher */
+    pyobj_ret = PyObject_CallMethod(pyobj_conn,
+                                    (char*)"dispatchDomainEventGraphicsCallback",
+                                    (char*)"OiOOsOO",
+                                    pyobj_dom,
+                                    phase, pyobj_local, pyobj_remote,
+                                    authScheme, pyobj_subject,
+                                    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)
+{
+    PyObject *py_retval;        /* return value */
+    PyObject *pyobj_conn;       /* virConnectPtr */
+    PyObject *pyobj_dom;
+    PyObject *pyobj_cbData;     /* hash of callback data */
+    int eventID;
+    virConnectPtr conn;
+    int ret = 0;
+    virConnectDomainEventGenericCallback cb = NULL;
+    virDomainPtr dom;
+
+    if (!PyArg_ParseTuple
+        (args, (char *) "OOiO:virConnectDomainEventRegisterAny",
+         &pyobj_conn, &pyobj_dom, &eventID, &pyobj_cbData)) {
+#if DEBUG_ERROR
+        printf("%s failed parsing tuple\n", __FUNCTION__);
+#endif
+        return VIR_PY_INT_FAIL;
+    }
+
+#ifdef DEBUG_ERROR
+    printf("libvirt_virConnectDomainEventRegister(%p %p %d %p) called\n",
+           pyobj_conn, pyobj_dom, eventID, pyobj_cbData);
+#endif
+    conn = PyvirConnect_Get(pyobj_conn);
+    if (pyobj_dom == Py_None)
+        dom = NULL;
+    else
+        dom = PyvirDomain_Get(pyobj_dom);
+
+    switch (eventID) {
+    case VIR_DOMAIN_EVENT_ID_LIFECYCLE:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventLifecycleCallback);
+        break;
+    case VIR_DOMAIN_EVENT_ID_REBOOT:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventGenericCallback);
+        break;
+    case VIR_DOMAIN_EVENT_ID_RTC_CHANGE:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventRTCChangeCallback);
+        break;
+    case VIR_DOMAIN_EVENT_ID_WATCHDOG:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventWatchdogCallback);
+        break;
+    case VIR_DOMAIN_EVENT_ID_IO_ERROR:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventIOErrorCallback);
+        break;
+    case VIR_DOMAIN_EVENT_ID_GRAPHICS:
+        cb = VIR_DOMAIN_EVENT_CALLBACK(libvirt_virConnectDomainEventGraphicsCallback);
+        break;
+    }
+
+    if (!cb) {
+        return VIR_PY_INT_FAIL;
+    }
+
+    Py_INCREF(pyobj_cbData);
+
+    LIBVIRT_BEGIN_ALLOW_THREADS;
+    ret = virConnectDomainEventRegisterAny(conn, dom, eventID,
+                                           cb, 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_virConnectDomainEventDeregisterAny(ATTRIBUTE_UNUSED PyObject * self,
+                                           PyObject * args)
+{
+    PyObject *py_retval;
+    PyObject *pyobj_conn;
+    int callbackID;
+    virConnectPtr conn;
+    int ret = 0;
+
+    if (!PyArg_ParseTuple
+        (args, (char *) "Oi:virConnectDomainEventDeregister",
+         &pyobj_conn, &callbackID))
+        return (NULL);
+
+#ifdef DEBUG_ERROR
+    printf("libvirt_virConnectDomainEventDeregister(%p) called\n", pyobj_conn);
+#endif
+
+    conn   = (virConnectPtr) PyvirConnect_Get(pyobj_conn);
+
+    LIBVIRT_BEGIN_ALLOW_THREADS;
+
+    ret = virConnectDomainEventDeregisterAny(conn, callbackID);
+
+    LIBVIRT_END_ALLOW_THREADS;
+    py_retval = libvirt_intWrap(ret);
+    return (py_retval);
+}
+
+
 /************************************************************************
  *									*
  *			The registration stuff				*
@@ -2776,6 +3225,8 @@ static PyMethodDef libvirtMethods[] = {
     {(char *) "virConnectListDefinedDomains", libvirt_virConnectListDefinedDomains, METH_VARARGS, NULL},
     {(char *) "virConnectDomainEventRegister", libvirt_virConnectDomainEventRegister, METH_VARARGS, NULL},
     {(char *) "virConnectDomainEventDeregister", libvirt_virConnectDomainEventDeregister, METH_VARARGS, NULL},
+    {(char *) "virConnectDomainEventRegisterAny", libvirt_virConnectDomainEventRegisterAny, METH_VARARGS, NULL},
+    {(char *) "virConnectDomainEventDeregisterAny", libvirt_virConnectDomainEventDeregisterAny, METH_VARARGS, NULL},
     {(char *) "virDomainGetInfo", libvirt_virDomainGetInfo, METH_VARARGS, NULL},
     {(char *) "virNodeGetInfo", libvirt_virNodeGetInfo, METH_VARARGS, NULL},
     {(char *) "virDomainGetUUID", libvirt_virDomainGetUUID, METH_VARARGS, NULL},
-- 
1.6.6.1




More information about the libvir-list mailing list