From 6dc8249c7ba2553acb686a2462de93239f1f07b4 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Mon, 15 Jul 2019 17:35:37 -0400 Subject: [PATCH] pybind/rbd: flatten, remove, trash_remove, migration progress callback This callback can be used to track progress and also to attempt to cancel the operation while it's in-progress by returning a negative error code from the callback. Signed-off-by: Jason Dillaman --- src/pybind/rbd/rbd.pyx | 107 +++++++++++++++++++++++++++++------- src/test/pybind/test_rbd.py | 81 +++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 21 deletions(-) diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 71ee821db43..5bc68f90e23 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -53,6 +53,9 @@ cdef extern from "rados/librados.h": enum: _LIBRADOS_SNAP_HEAD "LIBRADOS_SNAP_HEAD" +cdef extern from "rbd/librbd.h": + ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr) + cdef extern from "rbd/librbd.h" nogil: enum: _RBD_FEATURE_LAYERING "RBD_FEATURE_LAYERING" @@ -261,7 +264,6 @@ cdef extern from "rbd/librbd.h" nogil: _RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS "RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS" ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg) - ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr) void rbd_version(int *major, int *minor, int *extra) @@ -295,7 +297,8 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name, const char *p_snapname, rados_ioctx_t c_ioctx, const char *c_name, rbd_image_options_t c_opts) - int rbd_remove(rados_ioctx_t io, const char *name) + int rbd_remove_with_progress(rados_ioctx_t io, const char *name, + librbd_progress_fn_t cb, void *cbdata) int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname, const char *destname) @@ -308,15 +311,26 @@ cdef extern from "rbd/librbd.h" nogil: void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries, size_t num_entries) int rbd_trash_purge(rados_ioctx_t io, time_t expire_ts, float threshold) - int rbd_trash_remove(rados_ioctx_t io, const char *id, int force) + int rbd_trash_remove_with_progress(rados_ioctx_t io, const char *id, + int force, librbd_progress_fn_t cb, + void *cbdata) int rbd_trash_restore(rados_ioctx_t io, const char *id, const char *name) int rbd_migration_prepare(rados_ioctx_t io_ctx, const char *image_name, - rados_ioctx_t dest_io_ctx, const char *dest_image_name, + rados_ioctx_t dest_io_ctx, + const char *dest_image_name, rbd_image_options_t opts) - int rbd_migration_execute(rados_ioctx_t io_ctx, const char *image_name) - int rbd_migration_commit(rados_ioctx_t io_ctx, const char *image_name) - int rbd_migration_abort(rados_ioctx_t io_ctx, const char *image_name) + int rbd_migration_execute_with_progress(rados_ioctx_t io_ctx, + const char *image_name, + librbd_progress_fn_t cb, + void *cbdata) + int rbd_migration_commit_with_progress(rados_ioctx_t io_ctx, + const char *image_name, + librbd_progress_fn_t cb, + void *cbdata) + int rbd_migration_abort_with_progress(rados_ioctx_t io_ctx, + const char *image_name, + librbd_progress_fn_t cb, void *cbdata) int rbd_migration_status(rados_ioctx_t io_ctx, const char *image_name, rbd_image_migration_status_t *status, size_t status_size) @@ -452,7 +466,8 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_snap_get_trash_namespace(rbd_image_t image, uint64_t snap_id, char *original_name, size_t max_length) - int rbd_flatten(rbd_image_t image) + int rbd_flatten_with_progress(rbd_image_t image, librbd_progress_fn_t cb, + void *cbdata) int rbd_sparsify(rbd_image_t image, size_t sparse_size) int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb, void *cbdata) @@ -861,7 +876,10 @@ cdef make_ex(ret, msg, exception_map=errno_to_exception): cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL: return ioctx.io -cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr) nogil: +cdef int progress_callback(uint64_t offset, uint64_t total, void* ptr) with gil: + return (ptr)(offset, total) + +cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr): return 0 def cstr(val, name, encoding="utf-8", opt=False): @@ -1223,7 +1241,7 @@ class RBD(object): """ return ImageIterator(ioctx) - def remove(self, ioctx, name): + def remove(self, ioctx, name, on_progress=None): """ Delete an RBD image. This may take a long time, since it does not return until every object that comprises the image has @@ -1237,6 +1255,8 @@ class RBD(object): :type ioctx: :class:`rados.Ioctx` :param name: the name of the image to remove :type name: str + :param on_progress: optional progress callback function + :type on_progress: callback function :raises: :class:`ImageNotFound`, :class:`ImageBusy`, :class:`ImageHasSnapshots` """ @@ -1244,8 +1264,13 @@ class RBD(object): cdef: rados_ioctx_t _ioctx = convert_ioctx(ioctx) char *_name = name + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_remove(_ioctx, _name) + ret = rbd_remove_with_progress(_ioctx, _name, _prog_cb, _prog_arg) if ret != 0: raise make_ex(ret, 'error removing image') @@ -1327,7 +1352,7 @@ class RBD(object): if ret != 0: raise make_ex(ret, 'error purging images from trash') - def trash_remove(self, ioctx, image_id, force=False): + def trash_remove(self, ioctx, image_id, force=False, on_progress=None): """ Delete an RBD image from trash. If image deferment time has not expired :class:`PermissionError` is raised. @@ -1338,6 +1363,8 @@ class RBD(object): :type image_id: str :param force: force remove even if deferment time has not expired :type force: bool + :param on_progress: optional progress callback function + :type on_progress: callback function :raises: :class:`ImageNotFound`, :class:`PermissionError` """ image_id = cstr(image_id, 'image_id') @@ -1345,8 +1372,14 @@ class RBD(object): rados_ioctx_t _ioctx = convert_ioctx(ioctx) char *_image_id = image_id int _force = force + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_trash_remove(_ioctx, _image_id, _force) + ret = rbd_trash_remove_with_progress(_ioctx, _image_id, _force, + _prog_cb, _prog_arg) if ret != 0: raise make_ex(ret, 'error deleting image from trash') @@ -1491,7 +1524,7 @@ class RBD(object): if ret < 0: raise make_ex(ret, 'error migrating image %s' % (image_name)) - def migration_execute(self, ioctx, image_name): + def migration_execute(self, ioctx, image_name, on_progress=None): """ Execute a prepared RBD image migration. @@ -1499,18 +1532,26 @@ class RBD(object): :type ioctx: :class:`rados.Ioctx` :param image_name: the name of the image :type image_name: str + :param on_progress: optional progress callback function + :type on_progress: callback function :raises: :class:`ImageNotFound` """ image_name = cstr(image_name, 'image_name') cdef: rados_ioctx_t _ioctx = convert_ioctx(ioctx) char *_image_name = image_name + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_migration_execute(_ioctx, _image_name) + ret = rbd_migration_execute_with_progress(_ioctx, _image_name, + _prog_cb, _prog_arg) if ret != 0: raise make_ex(ret, 'error aborting migration') - def migration_commit(self, ioctx, image_name): + def migration_commit(self, ioctx, image_name, on_progress=None): """ Commit an executed RBD image migration. @@ -1518,18 +1559,26 @@ class RBD(object): :type ioctx: :class:`rados.Ioctx` :param image_name: the name of the image :type image_name: str + :param on_progress: optional progress callback function + :type on_progress: callback function :raises: :class:`ImageNotFound` """ image_name = cstr(image_name, 'image_name') cdef: rados_ioctx_t _ioctx = convert_ioctx(ioctx) char *_image_name = image_name + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_migration_commit(_ioctx, _image_name) + ret = rbd_migration_commit_with_progress(_ioctx, _image_name, + _prog_cb, _prog_arg) if ret != 0: raise make_ex(ret, 'error aborting migration') - def migration_abort(self, ioctx, image_name): + def migration_abort(self, ioctx, image_name, on_progress=None): """ Cancel a previously started but interrupted migration. @@ -1537,14 +1586,22 @@ class RBD(object): :type ioctx: :class:`rados.Ioctx` :param image_name: the name of the image :type image_name: str + :param on_progress: optional progress callback function + :type on_progress: callback function :raises: :class:`ImageNotFound` """ image_name = cstr(image_name, 'image_name') cdef: rados_ioctx_t _ioctx = convert_ioctx(ioctx) char *_image_name = image_name + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_migration_abort(_ioctx, _image_name) + ret = rbd_migration_abort_with_progress(_ioctx, _image_name, + _prog_cb, _prog_arg) if ret != 0: raise make_ex(ret, 'error aborting migration') @@ -3765,12 +3822,20 @@ written." % (self.name, ret, length)) raise make_ex(ret, 'error getting modify timestamp for image: %s' % (self.name)) return datetime.fromtimestamp(timestamp.tv_sec) - def flatten(self): + def flatten(self, on_progress=None): """ Flatten clone image (copy all blocks from parent to child) + :param on_progress: optional progress callback function + :type on_progress: callback function """ + cdef: + librbd_progress_fn_t _prog_cb = &no_op_progress_callback + void *_prog_arg = NULL + if on_progress: + _prog_cb = &progress_callback + _prog_arg = on_progress with nogil: - ret = rbd_flatten(self.image) + ret = rbd_flatten_with_progress(self.image, _prog_cb, _prog_arg) if ret < 0: raise make_ex(ret, "error flattening %s" % self.name) diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 1035ae319db..f18918a3f11 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -1,4 +1,5 @@ # vim: expandtab smarttab shiftwidth=4 softtabstop=4 +import errno import functools import socket import os @@ -324,6 +325,24 @@ def test_list(): image_id = image.id() eq([{'id': image_id, 'name': image_name}], list(RBD().list2(ioctx))) +@with_setup(create_image) +def test_remove_with_progress(): + d = {'received_callback': False} + def progress_cb(current, total): + d['received_callback'] = True + return 0 + + RBD().remove(ioctx, image_name, on_progress=progress_cb) + eq(True, d['received_callback']) + +@with_setup(create_image) +def test_remove_canceled(): + def progress_cb(current, total): + return -errno.ESHUTDOWN + + assert_raises(ConnectionShutdown, RBD().remove, ioctx, image_name, + on_progress=progress_cb) + @with_setup(create_image, remove_image) def test_rename(): rbd = RBD() @@ -1519,6 +1538,22 @@ class TestClone(object): self.clone.remove_snap('snap2') self.rbd.remove(ioctx, clone_name3) + def test_flatten_with_progress(self): + d = {'received_callback': False} + def progress_cb(current, total): + d['received_callback'] = True + return 0 + + global ioctx + global features + clone_name = get_temp_image_name() + self.rbd.clone(ioctx, image_name, 'snap1', ioctx, clone_name, + features, 0) + with Image(ioctx, clone_name) as clone: + clone.flatten(on_progress=progress_cb) + self.rbd.remove(ioctx, clone_name) + eq(True, d['received_callback']) + def test_resize_flatten_multi_level(self): self.clone.create_snap('snap2') self.clone.protect_snap('snap2') @@ -1919,6 +1954,20 @@ class TestTrash(object): RBD().trash_move(ioctx, image_name, 0) RBD().trash_remove(ioctx, image_id) + def test_remove_with_progress(self): + d = {'received_callback': False} + def progress_cb(current, total): + d['received_callback'] = True + return 0 + + create_image() + with Image(ioctx, image_name) as image: + image_id = image.id() + + RBD().trash_move(ioctx, image_name, 0) + RBD().trash_remove(ioctx, image_id, on_progress=progress_cb) + eq(True, d['received_callback']) + def test_get(self): create_image() with Image(ioctx, image_name) as image: @@ -2165,6 +2214,24 @@ class TestMigration(object): RBD().migration_commit(ioctx, image_name) remove_image() + def test_migration_with_progress(self): + d = {'received_callback': False} + def progress_cb(current, total): + d['received_callback'] = True + return 0 + + create_image() + RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63, + order=23, stripe_unit=1<<23, stripe_count=1, + data_pool=None) + RBD().migration_execute(ioctx, image_name, on_progress=progress_cb) + eq(True, d['received_callback']) + d['received_callback'] = False + + RBD().migration_commit(ioctx, image_name, on_progress=progress_cb) + eq(True, d['received_callback']) + remove_image() + def test_migrate_abort(self): create_image() RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63, @@ -2172,3 +2239,17 @@ class TestMigration(object): data_pool=None) RBD().migration_abort(ioctx, image_name) remove_image() + + def test_migrate_abort_with_progress(self): + d = {'received_callback': False} + def progress_cb(current, total): + d['received_callback'] = True + return 0 + + create_image() + RBD().migration_prepare(ioctx, image_name, ioctx, image_name, features=63, + order=23, stripe_unit=1<<23, stripe_count=1, + data_pool=None) + RBD().migration_abort(ioctx, image_name, on_progress=progress_cb) + eq(True, d['received_callback']) + remove_image() -- 2.39.5