mirror of
https://github.com/ceph/ceph
synced 2025-02-22 10:37:15 +00:00
librbd: add C and python bindings for diff_iterate
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>
This commit is contained in:
parent
e83fd3b937
commit
c0e3f642b1
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user