[Libvir] PATCH: Add missing APIs to python binding

The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. This patch implements them manually. Now a few words about the impls since they are non-trivial * virDomainGetSchedulerType The C binding has a return value, and an out parameter. I translated this into a tuple in python (schedular-type, n-parms) eg print dom.schedulerType() ('credit', 2) Arguably we could drop the nparams field - its less use in python since you don't have to pre-allocate a virSchedParam list. * virDomainGetSchedulerParameters This has an out parameter which is a list of virSchedParam structs, and a param specifying the length of the passed in virSchedParam list. The latter is irrelevant; The former I translated into a hash table return value eg print dom.schedulerParameters() {'cap': 0, 'weight': 100} * virDomainSetSchedulerParameters This has two in parameters, a list of virSchedParma structs, and a param specifying length of the list. I just convert this into a hash. I also make all the fields optional - it'll copy existing values for any params which are omimtted. eg dom.setSchedulerParameters({'weight': 100}) * virDomainGetVcpus This has two out parameters, a list of virVcpuInfo structs, and a bitmap both pre-allocated, and their size passed in. This needs to be turned into a 2 element tuple return value. First element returning vcpu info, and the second element returning mapping. The vcpu info will be returned as a list of lists, and the cpumap will be another list of lists - a True indicating affinity. eg (cpuinfo, cpumap) = dom.vcpus() print cpuinfo print cpumap [(0, 2, 7753916026L, 1), (1, 2, 5698028782L, 0), (2, 2, 4916522979L, 0), (3, 2, 3773416711L, 1)] [(False, True), (True, True), (True, False), (True, True)] * virDomainPinVcpu This takes a vcpu number and a bitmap of affinity. I turn the bitmap into a list of True/False values eg dom.pinVcpu(1, (True, True)) The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths. Here is the code I used to test the functions: #!/usr/bin/python import libvirt con = libvirt.open("xen:///") dom = con.lookupByName("rhel5pv") dominfo = dom.info() nodeinfo = con.getInfo() pcpus = nodeinfo[4] * nodeinfo[5] * nodeinfo[6] * nodeinfo[7] (cpuinfo, cpumap) = dom.vcpus() print cpuinfo print cpumap for i in range(dominfo[3]): print "vcpu: %d" % i if cpuinfo[i][1] == 0: print "state: offline" elif cpuinfo[i][1] == 2: print "state: blocked" else: print "state: running" print "cputime: %d" % cpuinfo[i][2] print "pcpu: %d" % cpuinfo[i][3] affinity = "" for p in range(pcpus): if cpumap[i][p] == True: affinity = affinity + "y" else: affinity = affinity + "-" print "affinity: %s" % affinity print dom.pinVcpu(1, (True, True)) print dom.schedulerType() print dom.schedulerParameters() dom.setSchedulerParameters({"weight": 100}) And here is the diffstat: generator.py | 26 ++++ libvir.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 344 insertions(+) Dan. diff -r 8468c0d428c9 python/generator.py --- a/python/generator.py Sun Jan 20 12:06:47 2008 -0500 +++ b/python/generator.py Sun Jan 20 12:07:59 2008 -0500 @@ -229,6 +229,7 @@ py_types = { 'double': ('d', None, "double", "double"), 'unsigned int': ('i', None, "int", "int"), 'unsigned long': ('l', None, "long", "long"), + 'unsigned long long': ('l', None, "longlong", "long long"), 'unsigned char *': ('z', None, "charPtr", "char *"), 'char *': ('z', None, "charPtr", "char *"), 'const char *': ('z', None, "charPtrConst", "const char *"), @@ -279,6 +280,11 @@ skip_impl = ( 'virDomainBlockStats', 'virDomainInterfaceStats', 'virNodeGetCellsFreeMemory', + 'virDomainGetSchedulerType', + 'virDomainGetSchedulerParameters', + 'virDomainSetSchedulerParameters', + 'virDomainGetVcpus', + 'virDomainPinVcpu', ) def skip_function(name): @@ -918,6 +924,16 @@ def buildWrappers(): txt.write(" %s()\n" % func); n = 0 for arg in args: + if name == "virDomainPinVcpu" and arg[0] == "maplen": + continue + if name == "virDomainGetSchedulerType" and arg[0] == "nparams": + continue + if name == "virDomainSetSchedulerParameters" and arg[0] == "nparams": + continue + if name == "virDomainGetSchedulerParameters" and arg[0] != "domain": + continue + if name == "virDomainGetVcpus" and arg[0] != "domain": + continue if n != index: classes.write(", %s" % arg[0]) n = n + 1 @@ -939,6 +955,16 @@ def buildWrappers(): classes.write("libvirtmod.%s(" % name) n = 0 for arg in args: + if name == "virDomainPinVcpu" and arg[0] == "maplen": + continue + if name == "virDomainGetSchedulerType" and arg[0] == "nparams": + continue + if name == "virDomainSetSchedulerParameters" and arg[0] == "nparams": + continue + if name == "virDomainGetSchedulerParameters" and arg[0] != "domain": + continue + if name == "virDomainGetVcpus" and arg[0] != "domain": + continue if n != 0: classes.write(", "); if n != index: diff -r 8468c0d428c9 python/libvir.c --- a/python/libvir.c Sun Jan 20 12:06:47 2008 -0500 +++ b/python/libvir.c Sun Jan 20 12:07:59 2008 -0500 @@ -102,6 +102,319 @@ libvirt_virDomainInterfaceStats(PyObject PyTuple_SetItem(info, 7, PyLong_FromLongLong(stats.tx_drop)); return(info); } + + +static PyObject * +libvirt_virDomainGetSchedulerType(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetScedulerType", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + + /* convert to a Python tupple of long objects */ + if ((info = PyTuple_New(2)) == NULL) { + free(c_retval); + return VIR_PY_NONE; + } + + PyTuple_SetItem(info, 0, libvirt_constcharPtrWrap(c_retval)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long)nparams)); + free(c_retval); + return(info); +} + +static PyObject * +libvirt_virDomainGetSchedulerParameters(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams, i; + virSchedParameterPtr params; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetScedulerParameters", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + free(c_retval); + + if ((params = malloc(sizeof(*params)*nparams)) == NULL) + return VIR_PY_NONE; + + if (virDomainGetSchedulerParameters(domain, params, &nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + /* convert to a Python tupple of long objects */ + if ((info = PyDict_New()) == NULL) { + free(params); + return VIR_PY_NONE; + } + for (i = 0 ; i < nparams ; i++) { + PyObject *key, *val; + + switch (params[i].type) { + case VIR_DOMAIN_SCHED_FIELD_INT: + val = PyInt_FromLong((long)params[i].value.i); + break; + + case VIR_DOMAIN_SCHED_FIELD_UINT: + val = PyInt_FromLong((long)params[i].value.ui); + break; + + case VIR_DOMAIN_SCHED_FIELD_LLONG: + val = PyLong_FromLongLong((long long)params[i].value.l); + break; + + case VIR_DOMAIN_SCHED_FIELD_ULLONG: + val = PyLong_FromLongLong((long long)params[i].value.ul); + break; + + case VIR_DOMAIN_SCHED_FIELD_DOUBLE: + val = PyFloat_FromDouble((double)params[i].value.d); + break; + + case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: + val = PyBool_FromLong((long)params[i].value.b); + break; + + default: + free(params); + Py_DECREF(info); + return VIR_PY_NONE; + } + + key = libvirt_constcharPtrWrap(params[i].field); + PyDict_SetItem(info, key, val); + } + free(params); + return(info); +} + +static PyObject * +libvirt_virDomainSetSchedulerParameters(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams, i; + virSchedParameterPtr params; + + if (!PyArg_ParseTuple(args, (char *)"OO:virDomainSetScedulerParameters", + &pyobj_domain, &info)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + free(c_retval); + + if ((params = malloc(sizeof(*params)*nparams)) == NULL) + return VIR_PY_NONE; + + if (virDomainGetSchedulerParameters(domain, params, &nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + /* convert to a Python tupple of long objects */ + for (i = 0 ; i < nparams ; i++) { + PyObject *key, *val; + key = libvirt_constcharPtrWrap(params[i].field); + val = PyDict_GetItem(info, key); + Py_DECREF(key); + + if (val == NULL) + continue; + + switch (params[i].type) { + case VIR_DOMAIN_SCHED_FIELD_INT: + params[i].value.i = (int)PyInt_AS_LONG(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_UINT: + params[i].value.ui = (unsigned int)PyInt_AS_LONG(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_LLONG: + params[i].value.l = (long long)PyLong_AsLongLong(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_ULLONG: + params[i].value.ul = (unsigned long long)PyLong_AsLongLong(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_DOUBLE: + params[i].value.d = (double)PyFloat_AsDouble(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: + { + /* Hack - Python's definition of Py_True breaks strict + * aliasing rules, so can't directly compare :-( + */ + PyObject *hacktrue = PyBool_FromLong(1); + params[i].value.b = hacktrue == val ? 1 : 0; + Py_DECREF(hacktrue); + } + break; + + default: + free(params); + return VIR_PY_NONE; + } + } + + if (virDomainSetSchedulerParameters(domain, params, nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + free(params); + return VIR_PY_NONE; +} + +static PyObject * +libvirt_virDomainGetVcpus(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *pyretval = NULL, *pycpuinfo = NULL, *pycpumap = NULL; + virNodeInfo nodeinfo; + virDomainInfo dominfo; + virVcpuInfoPtr cpuinfo = NULL; + unsigned char *cpumap = NULL; + int cpumaplen, i; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetVcpus", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (virNodeGetInfo(virDomainGetConnect(domain), &nodeinfo) != 0) + return VIR_PY_NONE; + + if (virDomainGetInfo(domain, &dominfo) != 0) + return VIR_PY_NONE; + + if ((cpuinfo = malloc(sizeof(*cpuinfo)*dominfo.nrVirtCpu)) == NULL) + return VIR_PY_NONE; + + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + if ((cpumap = malloc(dominfo.nrVirtCpu * cpumaplen)) == NULL) + goto cleanup; + + if (virDomainGetVcpus(domain, + cpuinfo, dominfo.nrVirtCpu, + cpumap, cpumaplen) < 0) + goto cleanup; + + /* convert to a Python tupple of long objects */ + if ((pyretval = PyTuple_New(2)) == NULL) + goto cleanup; + if ((pycpuinfo = PyList_New(dominfo.nrVirtCpu)) == NULL) + goto cleanup; + if ((pycpumap = PyList_New(dominfo.nrVirtCpu)) == NULL) + goto cleanup; + + for (i = 0 ; i < dominfo.nrVirtCpu ; i++) { + PyObject *info = PyTuple_New(4); + if (info == NULL) + goto cleanup; + PyTuple_SetItem(info, 0, PyInt_FromLong((long)cpuinfo[i].number)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long)cpuinfo[i].state)); + PyTuple_SetItem(info, 2, PyLong_FromLongLong((long long)cpuinfo[i].cpuTime)); + PyTuple_SetItem(info, 3, PyInt_FromLong((long)cpuinfo[i].cpu)); + PyList_SetItem(pycpuinfo, i, info); + } + for (i = 0 ; i < dominfo.nrVirtCpu ; i++) { + PyObject *info = PyTuple_New(VIR_NODEINFO_MAXCPUS(nodeinfo)); + int j; + if (info == NULL) + goto cleanup; + for (j = 0 ; j < VIR_NODEINFO_MAXCPUS(nodeinfo) ; j++) { + PyTuple_SetItem(info, j, PyBool_FromLong(VIR_CPU_USABLE(cpumap, cpumaplen, i, j))); + } + PyList_SetItem(pycpumap, i, info); + } + PyTuple_SetItem(pyretval, 0, pycpuinfo); + PyTuple_SetItem(pyretval, 1, pycpumap); + + free(cpuinfo); + free(cpumap); + + return(pyretval); + + cleanup: + free(cpuinfo); + free(cpumap); + /* NB, Py_DECREF is a badly defined macro, so we require + * braces here to avoid 'ambiguous else' warnings from + * the compiler. + * NB. this comment is true at of time of writing wrt to + * at least python2.5. + */ + if (pyretval) { Py_DECREF(pyretval); } + if (pycpuinfo) { Py_DECREF(pycpuinfo); } + if (pycpumap) { Py_DECREF(pycpumap); } + return VIR_PY_NONE; +} + + +static PyObject * +libvirt_virDomainPinVcpu(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *pycpumap, *truth; + virNodeInfo nodeinfo; + unsigned char *cpumap; + int cpumaplen, i, vcpu; + + if (!PyArg_ParseTuple(args, (char *)"OiO:virDomainPinVcpu", + &pyobj_domain, &vcpu, &pycpumap)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (virNodeGetInfo(virDomainGetConnect(domain), &nodeinfo) != 0) + return VIR_PY_NONE; + + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + if ((cpumap = malloc(cpumaplen)) == NULL) + return VIR_PY_NONE; + memset(cpumap, 0, cpumaplen); + + truth = PyBool_FromLong(1); + for (i = 0 ; i < VIR_NODEINFO_MAXCPUS(nodeinfo) ; i++) { + PyObject *flag = PyTuple_GetItem(pycpumap, i); + if (flag == truth) + VIR_USE_CPU(cpumap, i); + else + VIR_UNUSE_CPU(cpumap, i); + } + + virDomainPinVcpu(domain, vcpu, cpumap, cpumaplen); + Py_DECREF(truth); + free(cpumap); + + return VIR_PY_NONE; +} + + /************************************************************************ * * * Global error handler at the Python level * @@ -876,6 +1189,11 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainBlockStats", libvirt_virDomainBlockStats, METH_VARARGS, NULL}, {(char *) "virDomainInterfaceStats", libvirt_virDomainInterfaceStats, METH_VARARGS, NULL}, {(char *) "virNodeGetCellsFreeMemory", libvirt_virNodeGetCellsFreeMemory, METH_VARARGS, NULL}, + {(char *) "virDomainGetSchedulerType", libvirt_virDomainGetSchedulerType, METH_VARARGS, NULL}, + {(char *) "virDomainGetSchedulerParameters", libvirt_virDomainGetSchedulerParameters, METH_VARARGS, NULL}, + {(char *) "virDomainSetSchedulerParameters", libvirt_virDomainSetSchedulerParameters, METH_VARARGS, NULL}, + {(char *) "virDomainGetVcpus", libvirt_virDomainGetVcpus, METH_VARARGS, NULL}, + {(char *) "virDomainPinVcpu", libvirt_virDomainPinVcpu, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Jan 21, 2008 at 04:04:46AM -0500, Daniel Veillard wrote:
On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related.
I've no idea what python/libvirt-python-api.xml does - it just seems to duplicate info already in docs/libvirt-api.xml ? Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Mon, Jan 21, 2008 at 01:58:07PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 04:04:46AM -0500, Daniel Veillard wrote:
On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related.
I've no idea what python/libvirt-python-api.xml does - it just seems to duplicate info already in docs/libvirt-api.xml ?
Well, docs/libvirt-api.xml is automatically generated from the headers (and C modules), but python/libvirt-python-api.xml is manually created to describe similar input but for the functions where the C binding is done manually. This allows the generator to plug them at the libvirt.py level. Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Daniel Veillard wrote:
Well, docs/libvirt-api.xml is automatically generated from the headers (and C modules), but python/libvirt-python-api.xml is manually created to describe similar input but for the functions where the C binding is done manually. This allows the generator to plug them at the libvirt.py level.
Presumably that's there to generate the Python code? In the OCaml bindings we just #include a C file containing the one-off bindings into the auto-generated C code. Rich. -- Emerging Technologies, Red Hat - http://et.redhat.com/~rjones/ Registered Address: Red Hat UK Ltd, Amberley Place, 107-111 Peascod Street, Windsor, Berkshire, SL4 1TE, United Kingdom. Registered in England and Wales under Company Registration No. 03798903

On Mon, Jan 21, 2008 at 09:02:09AM -0500, Daniel Veillard wrote:
On Mon, Jan 21, 2008 at 01:58:07PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 04:04:46AM -0500, Daniel Veillard wrote:
On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related.
I've no idea what python/libvirt-python-api.xml does - it just seems to duplicate info already in docs/libvirt-api.xml ?
Well, docs/libvirt-api.xml is automatically generated from the headers (and C modules), but python/libvirt-python-api.xml is manually created to describe similar input but for the functions where the C binding is done manually. This allows the generator to plug them at the libvirt.py level.
Ok, here's a re-done patch to use that to override the python signature generator.py | 6 libvir.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++++ libvirt-python-api.xml | 28 ++++ 3 files changed, 352 insertions(+) Dan. diff -r 8468c0d428c9 python/generator.py --- a/python/generator.py Sun Jan 20 12:06:47 2008 -0500 +++ b/python/generator.py Mon Jan 21 09:24:13 2008 -0500 @@ -229,6 +229,7 @@ py_types = { 'double': ('d', None, "double", "double"), 'unsigned int': ('i', None, "int", "int"), 'unsigned long': ('l', None, "long", "long"), + 'unsigned long long': ('l', None, "longlong", "long long"), 'unsigned char *': ('z', None, "charPtr", "char *"), 'char *': ('z', None, "charPtr", "char *"), 'const char *': ('z', None, "charPtrConst", "const char *"), @@ -279,6 +280,11 @@ skip_impl = ( 'virDomainBlockStats', 'virDomainInterfaceStats', 'virNodeGetCellsFreeMemory', + 'virDomainGetSchedulerType', + 'virDomainGetSchedulerParameters', + 'virDomainSetSchedulerParameters', + 'virDomainGetVcpus', + 'virDomainPinVcpu', ) def skip_function(name): diff -r 8468c0d428c9 python/libvir.c --- a/python/libvir.c Sun Jan 20 12:06:47 2008 -0500 +++ b/python/libvir.c Mon Jan 21 09:24:13 2008 -0500 @@ -102,6 +102,319 @@ libvirt_virDomainInterfaceStats(PyObject PyTuple_SetItem(info, 7, PyLong_FromLongLong(stats.tx_drop)); return(info); } + + +static PyObject * +libvirt_virDomainGetSchedulerType(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetScedulerType", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + + /* convert to a Python tupple of long objects */ + if ((info = PyTuple_New(2)) == NULL) { + free(c_retval); + return VIR_PY_NONE; + } + + PyTuple_SetItem(info, 0, libvirt_constcharPtrWrap(c_retval)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long)nparams)); + free(c_retval); + return(info); +} + +static PyObject * +libvirt_virDomainGetSchedulerParameters(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams, i; + virSchedParameterPtr params; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetScedulerParameters", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + free(c_retval); + + if ((params = malloc(sizeof(*params)*nparams)) == NULL) + return VIR_PY_NONE; + + if (virDomainGetSchedulerParameters(domain, params, &nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + /* convert to a Python tupple of long objects */ + if ((info = PyDict_New()) == NULL) { + free(params); + return VIR_PY_NONE; + } + for (i = 0 ; i < nparams ; i++) { + PyObject *key, *val; + + switch (params[i].type) { + case VIR_DOMAIN_SCHED_FIELD_INT: + val = PyInt_FromLong((long)params[i].value.i); + break; + + case VIR_DOMAIN_SCHED_FIELD_UINT: + val = PyInt_FromLong((long)params[i].value.ui); + break; + + case VIR_DOMAIN_SCHED_FIELD_LLONG: + val = PyLong_FromLongLong((long long)params[i].value.l); + break; + + case VIR_DOMAIN_SCHED_FIELD_ULLONG: + val = PyLong_FromLongLong((long long)params[i].value.ul); + break; + + case VIR_DOMAIN_SCHED_FIELD_DOUBLE: + val = PyFloat_FromDouble((double)params[i].value.d); + break; + + case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: + val = PyBool_FromLong((long)params[i].value.b); + break; + + default: + free(params); + Py_DECREF(info); + return VIR_PY_NONE; + } + + key = libvirt_constcharPtrWrap(params[i].field); + PyDict_SetItem(info, key, val); + } + free(params); + return(info); +} + +static PyObject * +libvirt_virDomainSetSchedulerParameters(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *info; + char *c_retval; + int nparams, i; + virSchedParameterPtr params; + + if (!PyArg_ParseTuple(args, (char *)"OO:virDomainSetScedulerParameters", + &pyobj_domain, &info)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + c_retval = virDomainGetSchedulerType(domain, &nparams); + if (c_retval == NULL) + return VIR_PY_NONE; + free(c_retval); + + if ((params = malloc(sizeof(*params)*nparams)) == NULL) + return VIR_PY_NONE; + + if (virDomainGetSchedulerParameters(domain, params, &nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + /* convert to a Python tupple of long objects */ + for (i = 0 ; i < nparams ; i++) { + PyObject *key, *val; + key = libvirt_constcharPtrWrap(params[i].field); + val = PyDict_GetItem(info, key); + Py_DECREF(key); + + if (val == NULL) + continue; + + switch (params[i].type) { + case VIR_DOMAIN_SCHED_FIELD_INT: + params[i].value.i = (int)PyInt_AS_LONG(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_UINT: + params[i].value.ui = (unsigned int)PyInt_AS_LONG(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_LLONG: + params[i].value.l = (long long)PyLong_AsLongLong(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_ULLONG: + params[i].value.ul = (unsigned long long)PyLong_AsLongLong(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_DOUBLE: + params[i].value.d = (double)PyFloat_AsDouble(val); + break; + + case VIR_DOMAIN_SCHED_FIELD_BOOLEAN: + { + /* Hack - Python's definition of Py_True breaks strict + * aliasing rules, so can't directly compare :-( + */ + PyObject *hacktrue = PyBool_FromLong(1); + params[i].value.b = hacktrue == val ? 1 : 0; + Py_DECREF(hacktrue); + } + break; + + default: + free(params); + return VIR_PY_NONE; + } + } + + if (virDomainSetSchedulerParameters(domain, params, nparams) < 0) { + free(params); + return VIR_PY_NONE; + } + + free(params); + return VIR_PY_NONE; +} + +static PyObject * +libvirt_virDomainGetVcpus(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *pyretval = NULL, *pycpuinfo = NULL, *pycpumap = NULL; + virNodeInfo nodeinfo; + virDomainInfo dominfo; + virVcpuInfoPtr cpuinfo = NULL; + unsigned char *cpumap = NULL; + int cpumaplen, i; + + if (!PyArg_ParseTuple(args, (char *)"O:virDomainGetVcpus", + &pyobj_domain)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (virNodeGetInfo(virDomainGetConnect(domain), &nodeinfo) != 0) + return VIR_PY_NONE; + + if (virDomainGetInfo(domain, &dominfo) != 0) + return VIR_PY_NONE; + + if ((cpuinfo = malloc(sizeof(*cpuinfo)*dominfo.nrVirtCpu)) == NULL) + return VIR_PY_NONE; + + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + if ((cpumap = malloc(dominfo.nrVirtCpu * cpumaplen)) == NULL) + goto cleanup; + + if (virDomainGetVcpus(domain, + cpuinfo, dominfo.nrVirtCpu, + cpumap, cpumaplen) < 0) + goto cleanup; + + /* convert to a Python tupple of long objects */ + if ((pyretval = PyTuple_New(2)) == NULL) + goto cleanup; + if ((pycpuinfo = PyList_New(dominfo.nrVirtCpu)) == NULL) + goto cleanup; + if ((pycpumap = PyList_New(dominfo.nrVirtCpu)) == NULL) + goto cleanup; + + for (i = 0 ; i < dominfo.nrVirtCpu ; i++) { + PyObject *info = PyTuple_New(4); + if (info == NULL) + goto cleanup; + PyTuple_SetItem(info, 0, PyInt_FromLong((long)cpuinfo[i].number)); + PyTuple_SetItem(info, 1, PyInt_FromLong((long)cpuinfo[i].state)); + PyTuple_SetItem(info, 2, PyLong_FromLongLong((long long)cpuinfo[i].cpuTime)); + PyTuple_SetItem(info, 3, PyInt_FromLong((long)cpuinfo[i].cpu)); + PyList_SetItem(pycpuinfo, i, info); + } + for (i = 0 ; i < dominfo.nrVirtCpu ; i++) { + PyObject *info = PyTuple_New(VIR_NODEINFO_MAXCPUS(nodeinfo)); + int j; + if (info == NULL) + goto cleanup; + for (j = 0 ; j < VIR_NODEINFO_MAXCPUS(nodeinfo) ; j++) { + PyTuple_SetItem(info, j, PyBool_FromLong(VIR_CPU_USABLE(cpumap, cpumaplen, i, j))); + } + PyList_SetItem(pycpumap, i, info); + } + PyTuple_SetItem(pyretval, 0, pycpuinfo); + PyTuple_SetItem(pyretval, 1, pycpumap); + + free(cpuinfo); + free(cpumap); + + return(pyretval); + + cleanup: + free(cpuinfo); + free(cpumap); + /* NB, Py_DECREF is a badly defined macro, so we require + * braces here to avoid 'ambiguous else' warnings from + * the compiler. + * NB. this comment is true at of time of writing wrt to + * at least python2.5. + */ + if (pyretval) { Py_DECREF(pyretval); } + if (pycpuinfo) { Py_DECREF(pycpuinfo); } + if (pycpumap) { Py_DECREF(pycpumap); } + return VIR_PY_NONE; +} + + +static PyObject * +libvirt_virDomainPinVcpu(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) { + virDomainPtr domain; + PyObject *pyobj_domain, *pycpumap, *truth; + virNodeInfo nodeinfo; + unsigned char *cpumap; + int cpumaplen, i, vcpu; + + if (!PyArg_ParseTuple(args, (char *)"OiO:virDomainPinVcpu", + &pyobj_domain, &vcpu, &pycpumap)) + return(NULL); + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + if (virNodeGetInfo(virDomainGetConnect(domain), &nodeinfo) != 0) + return VIR_PY_NONE; + + cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo)); + if ((cpumap = malloc(cpumaplen)) == NULL) + return VIR_PY_NONE; + memset(cpumap, 0, cpumaplen); + + truth = PyBool_FromLong(1); + for (i = 0 ; i < VIR_NODEINFO_MAXCPUS(nodeinfo) ; i++) { + PyObject *flag = PyTuple_GetItem(pycpumap, i); + if (flag == truth) + VIR_USE_CPU(cpumap, i); + else + VIR_UNUSE_CPU(cpumap, i); + } + + virDomainPinVcpu(domain, vcpu, cpumap, cpumaplen); + Py_DECREF(truth); + free(cpumap); + + return VIR_PY_NONE; +} + + /************************************************************************ * * * Global error handler at the Python level * @@ -876,6 +1189,11 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virDomainBlockStats", libvirt_virDomainBlockStats, METH_VARARGS, NULL}, {(char *) "virDomainInterfaceStats", libvirt_virDomainInterfaceStats, METH_VARARGS, NULL}, {(char *) "virNodeGetCellsFreeMemory", libvirt_virNodeGetCellsFreeMemory, METH_VARARGS, NULL}, + {(char *) "virDomainGetSchedulerType", libvirt_virDomainGetSchedulerType, METH_VARARGS, NULL}, + {(char *) "virDomainGetSchedulerParameters", libvirt_virDomainGetSchedulerParameters, METH_VARARGS, NULL}, + {(char *) "virDomainSetSchedulerParameters", libvirt_virDomainSetSchedulerParameters, METH_VARARGS, NULL}, + {(char *) "virDomainGetVcpus", libvirt_virDomainGetVcpus, METH_VARARGS, NULL}, + {(char *) "virDomainPinVcpu", libvirt_virDomainPinVcpu, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; diff -r 8468c0d428c9 python/libvirt-python-api.xml --- a/python/libvirt-python-api.xml Sun Jan 20 12:06:47 2008 -0500 +++ b/python/libvirt-python-api.xml Mon Jan 21 09:24:13 2008 -0500 @@ -82,5 +82,33 @@ <arg name='maxCells' type='int' info='number of cell in the list'/> <return type='int *' info="the list available memory in the cells"/> </function> + <function name='virDomainGetSchedulerParameters' file='python'> + <info>Get the scheduler parameters, the @params array will be filled with the values.</info> + <return type='int' info='-1 in case of error, 0 in case of success.'/> + <arg name='domain' type='virDomainPtr' info='pointer to domain object'/> + </function> + <function name='virDomainGetSchedulerType' file='python'> + <info>Get the scheduler type.</info> + <return type='char *' info='NULL in case of error. The caller must free the returned string.'/> + <arg name='domain' type='virDomainPtr' info='pointer to domain object'/> + </function> + <function name='virDomainGetVcpus' file='python'> + <info>Extract information about virtual CPUs of domain, store it in info array and also in cpumaps if this pointer is'nt NULL.</info> + <return type='int' info='the number of info filled in case of success, -1 in case of failure.'/> + <arg name='domain' type='virDomainPtr' info='pointer to domain object, or NULL for Domain0'/> + </function> + <function name='virDomainPinVcpu' file='python'> + <info>Dynamically change the real CPUs which can be allocated to a virtual CPU. This function requires priviledged access to the hypervisor.</info> + <return type='int' info='0 in case of success, -1 in case of failure.'/> + <arg name='domain' type='virDomainPtr' info='pointer to domain object, or NULL for Domain0'/> + <arg name='vcpu' type='unsigned int' info='virtual CPU number'/> + <arg name='cpumap' type='unsigned char *' info='pointer to a bit map of real CPUs (in 8-bit bytes) (IN) Each bit set to 1 means that corresponding CPU is usable. Bytes are stored in little-endian order: CPU0-7, 8-15... In each byte, lowest CPU number is least significant bit.'/> + </function> + <function name='virDomainSetSchedulerParameters' file='python'> + <info>Change the scheduler parameters</info> + <return type='int' info='-1 in case of error, 0 in case of success.'/> + <arg name='domain' type='virDomainPtr' info='pointer to domain object'/> + <arg name='params' type='virSchedParameterPtr' info='pointer to scheduler parameter objects'/> + </function> </symbols> </api> -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Mon, Jan 21, 2008 at 02:26:36PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 09:02:09AM -0500, Daniel Veillard wrote:
On Mon, Jan 21, 2008 at 01:58:07PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 04:04:46AM -0500, Daniel Veillard wrote:
On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related.
I've no idea what python/libvirt-python-api.xml does - it just seems to duplicate info already in docs/libvirt-api.xml ?
Well, docs/libvirt-api.xml is automatically generated from the headers (and C modules), but python/libvirt-python-api.xml is manually created to describe similar input but for the functions where the C binding is done manually. This allows the generator to plug them at the libvirt.py level.
Ok, here's a re-done patch to use that to override the python signature
Looks fine by me, +1 then the other patch can be pushed too to insure we keep full coverage, thanks :-) Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

On Mon, Jan 21, 2008 at 09:29:12AM -0500, Daniel Veillard wrote:
On Mon, Jan 21, 2008 at 02:26:36PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 09:02:09AM -0500, Daniel Veillard wrote:
On Mon, Jan 21, 2008 at 01:58:07PM +0000, Daniel P. Berrange wrote:
On Mon, Jan 21, 2008 at 04:04:46AM -0500, Daniel Veillard wrote:
On Sun, Jan 20, 2008 at 05:23:46PM +0000, Daniel P. Berrange wrote:
The python binding is missing all the APIs relating to CPU pinning and schedular parameters because they are too hard for the generator to manage. [...] The generator fails on the C code part of the bindings completely, so that is hand-written. It also fails on the python part of the bindings, but the way the generator is structured doesn't enable us to hand-write the python part for methods within objects :-( So I have basically just editted the generator to blacklist all the out-parameters, and blacklist the in parameters which specify list lengths.
I'm not sure why you had to patch the generator.py , could you explain a bit ? Also I don't see in the patch any extra entry in python/libvirt-python-api.xml , its purpose is to help the generator writing the python side of the bindings for hand generated ones. So i wonder if the two are not related.
I've no idea what python/libvirt-python-api.xml does - it just seems to duplicate info already in docs/libvirt-api.xml ?
Well, docs/libvirt-api.xml is automatically generated from the headers (and C modules), but python/libvirt-python-api.xml is manually created to describe similar input but for the functions where the C binding is done manually. This allows the generator to plug them at the libvirt.py level.
Ok, here's a re-done patch to use that to override the python signature
Looks fine by me, +1 then the other patch can be pushed too to insure we keep full coverage,
Committed. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Richard W.M. Jones