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);
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);
"""
# 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
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
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):
"""
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):