[Libguestfs] [PATCH nbdkit] python: Implement can_extents + extents.
Eric Blake
eblake at redhat.com
Mon Aug 10 13:07:37 UTC 2020
On 8/10/20 5:50 AM, Richard W.M. Jones wrote:
> ---
> plugins/python/nbdkit-python-plugin.pod | 19 ++++++-
> plugins/python/python.c | 75 +++++++++++++++++++++++++
> tests/test-python-plugin.py | 8 ++-
> tests/test_python.py | 73 +++++++++++++++++++++++-
> 4 files changed, 169 insertions(+), 6 deletions(-)
>
> diff --git a/plugins/python/nbdkit-python-plugin.pod b/plugins/python/nbdkit-python-plugin.pod
> index b20f51f7..d7b6033f 100644
> --- a/plugins/python/nbdkit-python-plugin.pod
> +++ b/plugins/python/nbdkit-python-plugin.pod
> @@ -271,6 +271,13 @@ contents will be garbage collected.
> # return nbdkit.CACHE_NONE or nbdkit.CACHE_EMULATE
> # or nbdkit.CACHE_NATIVE
We expose various constants through 'import nbdkit', so...
> +++ b/plugins/python/python.c
> @@ -1020,6 +1020,79 @@ py_can_cache (void *handle)
> return NBDKIT_CACHE_NONE;
> }
>
> +static int
> +py_can_extents (void *handle)
> +{
> + ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
> + return boolean_callback (handle, "can_extents", "extents");
> +}
> +
> +static int
> +py_extents (void *handle, uint32_t count, uint64_t offset,
> + uint32_t flags, struct nbdkit_extents *extents)
> +{
> + ACQUIRE_PYTHON_GIL_FOR_CURRENT_SCOPE;
> + struct handle *h = handle;
> + PyObject *fn;
> + PyObject *r;
> + Py_ssize_t i, size;
> +
> + if (callback_defined ("extents", &fn)) {
> + PyErr_Clear ();
> +
> + r = PyObject_CallFunction (fn, "OiLI", h->py_h, count, offset, flags);
> + Py_DECREF (fn);
> + if (check_python_failure ("extents") == -1)
> + return -1;
> +
> + /* We expect a list of extents to be returned. Each extent is a
> + * tuple (offset, length, type).
> + */
> + if (!PyList_Check (r)) {
> + nbdkit_error ("extents method did not return a list");
> + Py_DECREF (r);
> + return -1;
> + }
> +
> + size = PyList_Size (r);
> + for (i = 0; i < size; ++i) {
> + PyObject *t, *py_offset, *py_length, *py_type;
> + uint64_t extent_offset, extent_length;
> + uint32_t extent_type;
> +
> + t = PyList_GetItem (r, i);
> + if (!PyTuple_Check (t) || PyTuple_Size (t) != 3) {
> + nbdkit_error ("extents method did not return a list of 3-tuples");
> + Py_DECREF (r);
> + return -1;
> + }
> + py_offset = PyTuple_GetItem (t, 0);
> + py_length = PyTuple_GetItem (t, 1);
> + py_type = PyTuple_GetItem (t, 2);
> + extent_offset = PyLong_AsUnsignedLongLong (py_offset);
> + extent_length = PyLong_AsUnsignedLongLong (py_length);
> + extent_type = PyLong_AsUnsignedLong (py_type);
> + if (check_python_failure ("PyLong") == -1) {
Is it really right to be doing error checking only once, but after three
calls into PyLong_* functions? I'm thinking of someone returning
('a',1,1). But as long as the later successful calls don't wipe out an
earlier failure, this looks like it works.
> + Py_DECREF (r);
> + return -1;
> + }
> + if (nbdkit_add_extent (extents,
> + extent_offset, extent_length, extent_type) == -1) {
> + Py_DECREF (r);
> + return -1;
> + }
> + }
> +
> + Py_DECREF (r);
> + }
> + else {
> + nbdkit_error ("%s not implemented", "extents");
> + return -1;
Here, we may be better off doing the same fallback as the C code, and
just reporting the entire image as a single data extent, rather than
always failing.
> +++ b/tests/test_python.py
> @@ -220,3 +232,60 @@ class Test (unittest.TestCase):
> """Test cache."""
> self.connect ({"size": 512, "can_cache": "native"})
> self.h.cache (512, 0)
> +
> + # We don't have access to the magic constants defined in the
> + # nbdkit module, so redefine them here.
> + EXTENT_HOLE = 1
> + EXTENT_ZERO = 2
...these constants should have been available through 'import nbdkit'.
Otherwise this looks reasonable.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3226
Virtualization: qemu.org | libvirt.org
More information about the Libguestfs
mailing list