]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd: add C and python bindings for diff_iterate
authorJosh Durgin <josh.durgin@inktank.com>
Sun, 31 Mar 2013 14:48:49 +0000 (07:48 -0700)
committerJosh Durgin <josh.durgin@inktank.com>
Mon, 1 Apr 2013 15:56:07 +0000 (08:56 -0700)
The python interface is a bit awkward since it maps directly
to the C interface, but it'll work well enough and not use
tons of memory.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
src/include/rbd/librbd.h
src/librbd/librbd.cc
src/pybind/rbd.py
src/test/pybind/test_rbd.py

index 155cfff66a9fe03852815eb84b5fa8af6ef7d444..f7ba2e4cb54be1dbc3d02a0d595751095f6e40c3 100644 (file)
@@ -313,6 +313,29 @@ typedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg);
 ssize_t rbd_read(rbd_image_t image, uint64_t ofs, size_t len, char *buf);
 int64_t rbd_read_iterate(rbd_image_t image, uint64_t ofs, size_t len,
                         int (*cb)(uint64_t, size_t, const char *, void *), void *arg);
+/**
+ * get difference between two versions of an image
+ *
+ * This will return the differences between two versions of an image
+ * via a callback, which gets the offset and length and a flag
+ * indicating whether the extent exists (1), or is known/defined to
+ * be zeros (a hole, 0).  If the source snapshot name is NULL, we
+ * interpret that as the beginning of time and return all allocated
+ * regions of the image.  The end version is whatever is currently
+ * selected for the image handle (either a snapshot or the writeable
+ * head).
+ *
+ * @param fromsnapname start snapshot name, or NULL
+ * @param ofs start offset
+ * @param len len in bytes of region to report on
+ * @param cb callback to call for each allocated region
+ * @param arg argument to pass to the callback
+ * @returns 0 on success, or negative error code on error
+ */
+int rbd_diff_iterate(rbd_image_t image,
+                    const char *fromsnapname,
+                    uint64_t ofs, size_t len,
+                    int (*cb)(uint64_t, size_t, int, void *), void *arg);
 ssize_t rbd_write(rbd_image_t image, uint64_t ofs, size_t len, const char *buf);
 int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len);
 int rbd_aio_write(rbd_image_t image, uint64_t off, size_t len, const char *buf, rbd_completion_t c);
index 20dbf62393b451fcdfd6b3646b57b4b7a88923bc..f5f102f1370e18c6d71859156ee1fb6ba2b439a5 100644 (file)
@@ -1032,11 +1032,11 @@ extern "C" int64_t rbd_read_iterate(rbd_image_t image, uint64_t ofs, size_t len,
   return librbd::read_iterate(ictx, ofs, len, cb, arg);
 }
 
-extern "C" int diff_iterate(rbd_image_t image,
-                           const char *fromsnapname,
-                           uint64_t ofs, uint64_t len,
-                           int (*cb)(uint64_t, size_t, int, void *),
-                           void *arg)
+extern "C" int rbd_diff_iterate(rbd_image_t image,
+                               const char *fromsnapname,
+                               uint64_t ofs, uint64_t len,
+                               int (*cb)(uint64_t, size_t, int, void *),
+                               void *arg)
 {
   librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
   return librbd::diff_iterate(ictx, fromsnapname, ofs, len, cb, arg);
index 4ce1a0e819b22c4855cddd58c13e0a582bc56585..b542b12efc536ed7bce77397632ae744b9d2d8d1 100644 (file)
@@ -16,7 +16,8 @@ methods, a :class:`TypeError` will be raised.
 """
 # Copyright 2011 Josh Durgin
 from ctypes import CDLL, c_char, c_char_p, c_size_t, c_void_p, c_int, \
-    create_string_buffer, byref, Structure, c_uint64, c_int64, c_uint8
+    create_string_buffer, byref, Structure, c_uint64, c_int64, c_uint8, \
+    CFUNCTYPE, pointer
 import ctypes
 import errno
 
@@ -628,6 +629,58 @@ class Image(object):
             raise make_ex(ret, 'error reading %s %ld~%ld' % (self.image, offset, length))
         return ctypes.string_at(ret_buf, ret)
 
+    def diff_iterate(self, offset, length, from_snapshot, iterate_cb):
+        """
+        Iterate over the changed extents of an image.
+
+        This will call iterate_cb with three arguments:
+
+        (offset, length, exists)
+
+        where the changed extent starts at offset bytes, continues for
+        length bytes, and is full of data (if exists is True) or zeroes
+        (if exists is False).
+
+        If from_snapshot is None, it is interpreted as the beginning
+        of time and this generates all allocated extents.
+
+        The end version is whatever is currently selected (via set_snap)
+        for the image.
+
+        Raises :class:`InvalidArgument` if from_snapshot is after
+        the currently set snapshot.
+
+        Raises :class:`ImageNotFound` if from_snapshot is not the name
+        of a snapshot of the image.
+
+        :param offset: start offset in bytes
+        :type offset: int
+        :param length: size of region to report on, in bytes
+        :type length: int
+        :param from_snapshot: starting snapshot name, or None
+        :type from_snapshot: str or None
+        :param iterate_cb: function to call for each extent
+        :type iterate_cb: function acception arguments for offset,
+                           length, and exists
+        :raises: :class:`InvalidArgument`, :class:`IOError`,
+                 :class:`ImageNotFound`
+        """
+        if from_snapshot is not None and not isinstance(from_snapshot, str):
+            raise TypeError('client must be a string')
+
+        RBD_DIFF_CB = CFUNCTYPE(c_int, c_uint64, c_size_t, c_int, c_void_p)
+        cb_holder = DiffIterateCB(iterate_cb)
+        cb = RBD_DIFF_CB(cb_holder.callback)
+        ret = self.librbd.diff_iterate(self.image,
+                                       c_char_p(from_snapshot),
+                                       c_uint64(offset),
+                                       c_uint64(length),
+                                       cb,
+                                       c_void_p(None))
+        if ret < 0:
+            msg = 'error generating diff from snapshot %s' % from_snapshot
+            raise make_ex(ret, msg)
+
     def write(self, data, offset):
         """
         Write data to the image. Raises :class:`InvalidArgument` if
@@ -794,6 +847,13 @@ written." % (self.name, ret, length))
         if ret < 0:
             raise make_ex(ret, 'error unlocking image')
 
+class DiffIterateCB(object):
+    def __init__(self, cb):
+        self.cb = cb
+
+    def callback(self, offset, length, exists, unused):
+        self.cb(offset, length, exists == 1)
+        return 0
 
 class SnapIterator(object):
     """
index 821d721ebf17faae57e17e20f743c2235bf18f04..9c59f218b468ba342cc50b5cad73e60acb6834c4 100644 (file)
@@ -454,6 +454,25 @@ class TestImage(object):
             self.image.unlock(str(i))
         eq([], self.image.list_lockers())
 
+    def test_diff_iterate(self):
+        check_diff(self.image, 0, IMG_SIZE, None, [])
+        self.image.write('a' * 256, 0)
+        check_diff(self.image, 0, IMG_SIZE, None, [(0, 256, True)])
+        self.image.write('b' * 256, 256)
+        check_diff(self.image, 0, IMG_SIZE, None, [(0, 512, True)])
+        
+
+def check_diff(image, offset, length, from_snapshot, expected):
+    cb_holder = DiffExtents()
+    image.diff_iterate(0, IMG_SIZE, None, cb_holder.callback)
+    eq(cb_holder.extents, expected)
+
+class DiffExtents(object):
+    def __init__(self):
+        self.extents = []
+
+    def callback(self, offset, length, exists):
+        self.extents.append((offset, length, exists))
 
 class TestClone(object):