Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

2012-01-25 Thread Guan Nan Ren


- Original Message -
From: Eric Blake ebl...@redhat.com
To: Guan Nan Ren g...@redhat.com
Cc: libvir-list@redhat.com
Sent: Tuesday, January 24, 2012 8:56:30 PM
Subject: Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

On 01/24/2012 05:07 AM, Guan Nan Ren wrote:
  static PyObject *
 +libvirt_virDomainSetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {
 
 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;
 
 Why are we getting the existing parameters, instead of just blindly
 constructing params based on args?  I think that's a waste of effort,
 considering that libvirt will already reject things if the user passes
 in unknown key names.

I finally realized _why_ we do it - that's because we want to pass the
correct types to libvirt.c, but python is not strongly typed.  That is,
if libvirt is expecting a particular named value to be
VIR_TYPED_PARAM_ULLONG, but the python code passes '1', we should be
able to properly convert that python value to C's 1ULL, rather than
rejecting the python code for using an incorrect type, since there is
not quite a notion of incorrect type in python.

There is another reason for this, we couldn't determine which 
one or more parameter will be set in case of multiple parameters, 
unless we hard-code the parameter name to compare. So the a little of overhead
caused by the loop and reassigning new value to given parameter based on 
arg is acceptable.

That means that either we hard-code the list of all known parameter
names and their type, or we make things done via a runtime query by
getting all parameters to learn their types before formatting the user's
settings back into something that libvirt will parse.

It may also mean that libvirt itself should be a bit nicer about things
- now that we have virTypedParam.c, maybe that function should be taught
to do some type conversions (if the user passes in VIR_TYPED_PARAM_INT,
but the code wanted VIR_TYPED_PARAM_ULLONG, libvirt could do the type
conversion on the user's behalf); if libvirt is made smarter, then the
python glue code might be simpler.  But there's still the issue of
back-compat - a newer python library talking to an older libvirt that
didn't do automatic type conversion would still be stuck needing to
query for types first.

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list


Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

2012-01-24 Thread Guan Nan Ren

  Thanks for these comments.
  I will refactor the loop code, rethink about other codes and make v3 patch.

  Guannan Ren

- Original Message -
From: Eric Blake ebl...@redhat.com
To: Guannan Ren g...@redhat.com
Cc: libvir-list@redhat.com
Sent: Saturday, January 21, 2012 11:04:15 PM
Subject: Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

On 01/19/2012 02:16 AM, Guannan Ren wrote:
 *virDomainSetNumaParameters
 *virDomainGetNumaParameters
 ---
  python/libvirt-override-api.xml |   13 +++
  python/libvirt-override.c   |  186 
 +++
  2 files changed, 199 insertions(+), 0 deletions(-)
 

  static PyObject *
 +libvirt_virDomainSetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {

 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;

Why are we getting the existing parameters, instead of just blindly
constructing params based on args?  I think that's a waste of effort,
considering that libvirt will already reject things if the user passes
in unknown key names.  Besides,

 +
 +if (i_retval  0) {
 +free(params);
 +return VIR_PY_INT_FAIL;
 +}
 +
 +/* convert to a Python tuple 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;

this says that you will silently discard any unknown keys passed in from
the python code, whereas libvirt would noisily reject unknown keys.  I
don't think the python code should behave differently from the C code.

 +
 +switch (params[i].type) {
 +case VIR_TYPED_PARAM_INT:
 +params[i].value.i = (int)PyInt_AS_LONG(val);
 +break;

We're starting to repeat this code sequence in several places - we ought
to refactor things to share this conversion to and from python types and
virTypedParameter, so that all of the glue code can share the same
conversions (and fixing bugs in one will fix all of them at the same time).

 +static PyObject *
 +libvirt_virDomainGetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {

Indentation; also, we've lately been sticking the { of a function on
column 1:

static PyObject *
libvirt_virDomainGetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
   PyObject *args)
{

 +
 +if (i_retval  0)
 +return VIR_PY_NONE;

I think this is okay; it tells python that the C call successfully
reported an error, and that the caller can then query for the last
libvirt error.

 +
 +if ((params = malloc(sizeof(*params)*nparams)) == NULL)
 +return VIR_PY_NONE;

Bad - we didn't raise any libvirt error, so the user wouldn't be able to
tell why we returned NONE.  Rather, we should really be calling return
PyErr_NoMemory() in situations where we failed to malloc(), so that
python can correctly raise a memory exception.  (You were just
copying-and-pasting; the problem is pervasive throughout this file).

 +
 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;
 +
 +if (i_retval  0) {
 +free(params);
 +return VIR_PY_NONE;
 +}

Okay.

 +
 +/* convert to a Python tuple of long objects */
 +if ((info = PyDict_New()) == NULL) {
 +free(params);

Memory leak - params might include VIR_TYPED_PARAM_STRING contents, so
we need to call virTypedParameterArrayClear before freeing params.

 +return VIR_PY_NONE;

Bad - PyDict_New already raised the python exception for no memory, but
we silently discarded it by returning NONE instead of NULL.  We should
really be propagating any python errors back up the call chain.

 +}
 +
 +for (i = 0 ; i  nparams ; i++) {
 +PyObject *key, *val;
 +
 +switch (params[i].type) {
 +case VIR_TYPED_PARAM_INT:
 +val = PyInt_FromLong((long)params[i].value.i);
 +break;

Again, worth refactoring code to share this loop.

 +
 +default:
 +free(params);

Another memory leak if params had any string contents.

 +Py_DECREF(info);
 +return VIR_PY_NONE;
 +}
 +
 +key = libvirt_constcharPtrWrap(params[i].field);
 +PyDict_SetItem(info, key, val);

Bad - we should be checking for failure of PyDict_SetItem, and
propagating that failure back to the caller.

 +}
 +free(params);

Another memory leak.

 +return(info);
 +}
 +

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman

Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

2012-01-24 Thread Eric Blake
On 01/24/2012 05:07 AM, Guan Nan Ren wrote:
  static PyObject *
 +libvirt_virDomainSetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {
 
 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;
 
 Why are we getting the existing parameters, instead of just blindly
 constructing params based on args?  I think that's a waste of effort,
 considering that libvirt will already reject things if the user passes
 in unknown key names.

I finally realized _why_ we do it - that's because we want to pass the
correct types to libvirt.c, but python is not strongly typed.  That is,
if libvirt is expecting a particular named value to be
VIR_TYPED_PARAM_ULLONG, but the python code passes '1', we should be
able to properly convert that python value to C's 1ULL, rather than
rejecting the python code for using an incorrect type, since there is
not quite a notion of incorrect type in python.

That means that either we hard-code the list of all known parameter
names and their type, or we make things done via a runtime query by
getting all parameters to learn their types before formatting the user's
settings back into something that libvirt will parse.

It may also mean that libvirt itself should be a bit nicer about things
- now that we have virTypedParam.c, maybe that function should be taught
to do some type conversions (if the user passes in VIR_TYPED_PARAM_INT,
but the code wanted VIR_TYPED_PARAM_ULLONG, libvirt could do the type
conversion on the user's behalf); if libvirt is made smarter, then the
python glue code might be simpler.  But there's still the issue of
back-compat - a newer python library talking to an older libvirt that
didn't do automatic type conversion would still be stuck needing to
query for types first.

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org



signature.asc
Description: OpenPGP digital signature
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Re: [libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

2012-01-21 Thread Eric Blake
On 01/19/2012 02:16 AM, Guannan Ren wrote:
 *virDomainSetNumaParameters
 *virDomainGetNumaParameters
 ---
  python/libvirt-override-api.xml |   13 +++
  python/libvirt-override.c   |  186 
 +++
  2 files changed, 199 insertions(+), 0 deletions(-)
 

  static PyObject *
 +libvirt_virDomainSetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {

 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;

Why are we getting the existing parameters, instead of just blindly
constructing params based on args?  I think that's a waste of effort,
considering that libvirt will already reject things if the user passes
in unknown key names.  Besides,

 +
 +if (i_retval  0) {
 +free(params);
 +return VIR_PY_INT_FAIL;
 +}
 +
 +/* convert to a Python tuple 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;

this says that you will silently discard any unknown keys passed in from
the python code, whereas libvirt would noisily reject unknown keys.  I
don't think the python code should behave differently from the C code.

 +
 +switch (params[i].type) {
 +case VIR_TYPED_PARAM_INT:
 +params[i].value.i = (int)PyInt_AS_LONG(val);
 +break;

We're starting to repeat this code sequence in several places - we ought
to refactor things to share this conversion to and from python types and
virTypedParameter, so that all of the glue code can share the same
conversions (and fixing bugs in one will fix all of them at the same time).

 +static PyObject *
 +libvirt_virDomainGetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
 + PyObject *args) {

Indentation; also, we've lately been sticking the { of a function on
column 1:

static PyObject *
libvirt_virDomainGetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
   PyObject *args)
{

 +
 +if (i_retval  0)
 +return VIR_PY_NONE;

I think this is okay; it tells python that the C call successfully
reported an error, and that the caller can then query for the last
libvirt error.

 +
 +if ((params = malloc(sizeof(*params)*nparams)) == NULL)
 +return VIR_PY_NONE;

Bad - we didn't raise any libvirt error, so the user wouldn't be able to
tell why we returned NONE.  Rather, we should really be calling return
PyErr_NoMemory() in situations where we failed to malloc(), so that
python can correctly raise a memory exception.  (You were just
copying-and-pasting; the problem is pervasive throughout this file).

 +
 +LIBVIRT_BEGIN_ALLOW_THREADS;
 +i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
 +LIBVIRT_END_ALLOW_THREADS;
 +
 +if (i_retval  0) {
 +free(params);
 +return VIR_PY_NONE;
 +}

Okay.

 +
 +/* convert to a Python tuple of long objects */
 +if ((info = PyDict_New()) == NULL) {
 +free(params);

Memory leak - params might include VIR_TYPED_PARAM_STRING contents, so
we need to call virTypedParameterArrayClear before freeing params.

 +return VIR_PY_NONE;

Bad - PyDict_New already raised the python exception for no memory, but
we silently discarded it by returning NONE instead of NULL.  We should
really be propagating any python errors back up the call chain.

 +}
 +
 +for (i = 0 ; i  nparams ; i++) {
 +PyObject *key, *val;
 +
 +switch (params[i].type) {
 +case VIR_TYPED_PARAM_INT:
 +val = PyInt_FromLong((long)params[i].value.i);
 +break;

Again, worth refactoring code to share this loop.

 +
 +default:
 +free(params);

Another memory leak if params had any string contents.

 +Py_DECREF(info);
 +return VIR_PY_NONE;
 +}
 +
 +key = libvirt_constcharPtrWrap(params[i].field);
 +PyDict_SetItem(info, key, val);

Bad - we should be checking for failure of PyDict_SetItem, and
propagating that failure back to the caller.

 +}
 +free(params);

Another memory leak.

 +return(info);
 +}
 +

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org



signature.asc
Description: OpenPGP digital signature
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

[libvirt] [PATCH v2] Add two NUMA tuning python bindings APIs

2012-01-19 Thread Guannan Ren
*virDomainSetNumaParameters
*virDomainGetNumaParameters
---
 python/libvirt-override-api.xml |   13 +++
 python/libvirt-override.c   |  186 +++
 2 files changed, 199 insertions(+), 0 deletions(-)

diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml
index 704fee9..e09290c 100644
--- a/python/libvirt-override-api.xml
+++ b/python/libvirt-override-api.xml
@@ -248,6 +248,19 @@
   arg name='domain' type='virDomainPtr' info='pointer to domain object'/
   arg name='flags' type='int' info='an ORapos;ed set of 
virDomainModificationImpact'/
 /function
+function name='virDomainSetNumaParameters' file='python'
+  infoChange the NUMA tunables/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='virTypedParameterPtr' info='pointer to numa 
tunable objects'/
+  arg name='flags'  type='int' info='an ORapos;ed set of 
virDomainModificationImpact'/
+/function
+function name='virDomainGetNumaParameters' file='python'
+  infoGet the NUMA parameters/info
+  return type='virTypedParameterPtr' info='the numa tunables value or 
None in case of error'/
+  arg name='domain' type='virDomainPtr' info='pointer to domain object'/
+  arg name='flags' type='int' info='an ORapos;ed set of 
virDomainModificationImpact'/
+/function
 function name='virConnectListStoragePools' file='python'
   infolist the storage pools, stores the pointers to the names in 
@names/info
   arg name='conn' type='virConnectPtr' info='pointer to the hypervisor 
connection'/
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index d2aad0f..27ad1d8 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -1000,6 +1000,190 @@ libvirt_virDomainGetMemoryParameters(PyObject *self 
ATTRIBUTE_UNUSED,
 }
 
 static PyObject *
+libvirt_virDomainSetNumaParameters(PyObject *self ATTRIBUTE_UNUSED,
+ PyObject *args) {
+virDomainPtr domain;
+PyObject *pyobj_domain, *info;
+int i_retval;
+int nparams = 0, i;
+unsigned int flags;
+virTypedParameterPtr params;
+
+if (!PyArg_ParseTuple(args,
+  (char *)OOi:virDomainSetNumaParameters,
+  pyobj_domain, info, flags))
+return(NULL);
+domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+LIBVIRT_BEGIN_ALLOW_THREADS;
+i_retval = virDomainGetNumaParameters(domain, NULL, nparams, flags);
+LIBVIRT_END_ALLOW_THREADS;
+
+if (i_retval  0)
+return VIR_PY_INT_FAIL;
+
+if ((params = malloc(sizeof(*params)*nparams)) == NULL)
+return VIR_PY_INT_FAIL;
+
+LIBVIRT_BEGIN_ALLOW_THREADS;
+i_retval = virDomainGetNumaParameters(domain, params, nparams, flags);
+LIBVIRT_END_ALLOW_THREADS;
+
+if (i_retval  0) {
+free(params);
+return VIR_PY_INT_FAIL;
+}
+
+/* convert to a Python tuple 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_TYPED_PARAM_INT:
+params[i].value.i = (int)PyInt_AS_LONG(val);
+break;
+
+case VIR_TYPED_PARAM_UINT:
+params[i].value.ui = (unsigned int)PyInt_AS_LONG(val);
+break;
+
+case VIR_TYPED_PARAM_LLONG:
+params[i].value.l = (long long)PyLong_AsLongLong(val);
+break;
+
+case VIR_TYPED_PARAM_ULLONG:
+params[i].value.ul = (unsigned long long)PyLong_AsLongLong(val);
+break;
+
+case VIR_TYPED_PARAM_DOUBLE:
+params[i].value.d = (double)PyFloat_AsDouble(val);
+break;
+
+case VIR_TYPED_PARAM_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;
+
+case VIR_TYPED_PARAM_STRING:
+params[i].value.s = PyString_AsString(val);
+break;
+
+default:
+free(params);
+return VIR_PY_INT_FAIL;
+}
+}
+
+LIBVIRT_BEGIN_ALLOW_THREADS;
+i_retval = virDomainSetNumaParameters(domain, params, nparams, flags);
+LIBVIRT_END_ALLOW_THREADS;
+if (i_retval  0) {
+free(params);
+return VIR_PY_INT_FAIL;
+}
+
+free(params);
+return VIR_PY_INT_SUCCESS;
+}
+
+static PyObject *