[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH v2] python: add virDomainGetCPUStats python binding API



On 03/14/2012 07:03 AM, Guannan Ren wrote:
>     dom.getCPUStats(True, 0)
>       [{'cpu_time': 92913537401L, 'system_time': 5470000000L, 'user_time': 310000000L}]
> 
>     dom.getCPUStats(False, 0)
>       [{'cpu_time': 39476858499L}, {'cpu_time': 10627048370L}, {'cpu_time': 21270945682L}, {'cpu_time': 21556420641L}]
> 
>     *generator.py Add a new naming rule
>     *libvirt-override-api.xml The API function description
>     *libvirt-override.c Implement it.
> ---
>  python/generator.py             |    5 +-
>  python/libvirt-override-api.xml |   10 +++
>  python/libvirt-override.c       |  164 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 178 insertions(+), 1 deletions(-)
> 
> +++ b/python/libvirt-override-api.xml
> @@ -149,6 +149,16 @@
>        <arg name='path' type='char *' info='the path for the block device'/>
>        <arg name='flags' type='int' info='flags (unused; pass 0)'/>
>      </function>
> +    <function name='virDomainGetCPUStats' file='python'>
> +      <info>Extracts CPU statistics for a running domain, On success it will return a list of data of dictionary type.

s/, On/. On/

Long lines; can you wrap this to fit in 80 columns?

> +   If boolean total is True, the first element of the list refers to CPU0 on the host, second element is CPU1, and so on.

s/total is True/total is False/

> +   The format of data struct is like [{cpu_time:xxx},{cpu_time:xxx}, ...]
> +   If it is False, it returns total domain CPU statistics like [{cpu_time:xxx, user_time:xxx, system_time:xxx}]</info>

s/False/True/

> +
> +    if (!PyBool_Check(totalbool)) {
> +        PyErr_Format(PyExc_TypeError,
> +                    "The \"total\" attribute must be bool");
> +        return NULL;
> +    }
> +
> +    if ((ret = PyList_New(0)) == NULL)
> +        return NULL;
> +
> +    if (totalbool == Py_False) {

Per other code in libvirt-override.c, you can't compare totalbool (type
PyObject) with Py_False, at least not on all compilers.  You need
something like this instead:

            /* Hack - Python's definition of Py_True breaks strict
             * aliasing rules, so can't directly compare
             */
            if (PyBool_Check(value)) {
                PyObject *hacktrue = PyBool_FromLong(1);
                temp->value.b = hacktrue == value ? 1 : 0;
                Py_DECREF(hacktrue);

> +        LIBVIRT_BEGIN_ALLOW_THREADS;
> +        ncpus = virDomainGetCPUStats(domain, NULL, 0, 0, 0, flags);
> +        LIBVIRT_END_ALLOW_THREADS;
> +
> +        if (ncpus < 0) {
> +            error = VIR_PY_NONE;
> +            goto failed;
> +        }
> +
> +        LIBVIRT_BEGIN_ALLOW_THREADS;
> +        nparams = virDomainGetCPUStats(domain, NULL, 0, 0, 1, flags);
> +        LIBVIRT_END_ALLOW_THREADS;
> +
> +        if (nparams < 0) {
> +            error = VIR_PY_NONE;
> +            goto failed;
> +        }

So far, so good.

> +
> +        if (!nparams) {
> +            if ((cpu = PyDict_New()) == NULL) {
> +                error = NULL;

Initialize error to NULL at the front, and you won't have to set it here.

> +                goto failed;
> +            }
> +
> +            if (PyList_Append(ret, cpu) < 0) {
> +                Py_DECREF(cpu);
> +                error = NULL;
> +                goto failed;
> +            }
> +
> +            Py_DECREF(cpu);
> +            return ret;

So why are we populating the list with a single empty dictionary?
Shouldn't we instead be populating it with one empty dictionary per cpu?
 That is, this code returns [{}], but if ncpus is 4, it should return
[{},{},{},{}].

> +        }
> +        sumparams = nparams * ncpus;
> +
> +        if (VIR_ALLOC_N(params, sumparams) < 0) {
> +            error = PyErr_NoMemory();
> +            goto failed;
> +        }
> +
> +        LIBVIRT_BEGIN_ALLOW_THREADS;
> +        i_retval = virDomainGetCPUStats(domain, params, nparams, 0, ncpus, flags);

This will fail if ncpus > 128.  You have to do this in a for loop,
grabbing only 128 cpus per iteration.

> +        LIBVIRT_END_ALLOW_THREADS;
> +
> +        if (i_retval < 0) {
> +            error = VIR_PY_NONE;
> +            goto cleanup;
> +        }
> +
> +        for (i = 0; i < ncpus; i++) {
> +            if (params[i * nparams].type == 0)
> +                continue;

Technically, this is only possible if you called virDomainGetCPUStats
with ncpus larger than what the hypervisor already told you to use.  But
I guess it doesn't hurt to leave these two lines in.

> +
> +            cpuparams = &params[i * nparams];
> +            if ((cpu = getPyVirTypedParameter(cpuparams, nparams)) == NULL) {

Here, you want to iterate over the number of returned parameters, not
the number of array slots you passed in.  s/nparams/i_retval/

> +                error = NULL;
> +                goto cleanup;
> +            }
> +            if (PyList_Append(ret, cpu) < 0) {
> +                Py_DECREF(cpu);
> +                error = NULL;
> +                goto cleanup;
> +            }
> +            Py_DECREF(cpu);
> +        }
> +
> +        virTypedParameterArrayClear(params, sumparams);
> +        VIR_FREE(params);
> +        return ret;
> +    } else {
> +        LIBVIRT_BEGIN_ALLOW_THREADS;
> +        nparams = virDomainGetCPUStats(domain, NULL, 0, -1, 1, flags);
> +        LIBVIRT_END_ALLOW_THREADS;
> +
> +        if (nparams < 0) {
> +            error = VIR_PY_NONE;
> +            goto failed;
> +        }
> +
> +        if (!nparams) {
> +            if ((total = PyDict_New()) == NULL) {
> +                error = NULL;
> +                goto failed;
> +            }
> +            if (PyList_Append(ret, total) < 0) {
> +               Py_DECREF(total);
> +               error = NULL;

Again, several instances of error=NULL that could be avoided if you just
initialize it that way at the beginning.

> +               goto failed;
> +            }
> +
> +            Py_DECREF(total);
> +            return ret;
> +        }
> +        sumparams = nparams;
> +
> +        if (VIR_ALLOC_N(params, nparams) < 0) {
> +            error = PyErr_NoMemory();
> +            goto failed;
> +        }
> +
> +        LIBVIRT_BEGIN_ALLOW_THREADS;
> +        i_retval = virDomainGetCPUStats(domain, params, nparams, -1, 1, flags);
> +        LIBVIRT_END_ALLOW_THREADS;
> +
> +        if (i_retval < 0) {
> +            error = VIR_PY_NONE;
> +            goto cleanup;
> +        }
> +
> +        if ((total = getPyVirTypedParameter(params, nparams)) == NULL) {

again, s/nparams/i_retval/

> +            error = NULL;
> +            goto cleanup;
> +        }
> +        if (PyList_Append(ret, total) < 0) {
> +            Py_DECREF(total);
> +            error = NULL;
> +            goto cleanup;
> +        }
> +        Py_DECREF(total);
> +
> +        virTypedParameterArrayClear(params, sumparams);
> +        VIR_FREE(params);
> +        return ret;

These last three lines are common to both arms of the if/then; I'd
factor them out to occur after the } and before the cleanup: label.

> +    }
> +
> +cleanup:

Actually, I'm not sure I like the name cleanup; we tend to use that when
the success case also uses the lable, but you only ever go to this label
on error conditions.  Furthermore,

> +    virTypedParameterArrayClear(params, sumparams);
> +    VIR_FREE(params);

these two calls are always safe, if you initialize params to NULL at the
beginning of the method.  Therefore, you only need one label instead of
two, and per HACKING, it should be named 'error:' not 'failed:'.

> +
> +failed:
> +    Py_DECREF(ret);
> +    return error;
> +}

Not quite ready, but I'm looking forward to v3.

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

Attachment: signature.asc
Description: OpenPGP digital signature


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]