From: Mehdi Abaakouk Date: Thu, 11 Feb 2016 08:30:52 +0000 (+0100) Subject: Move pybind rbd module into it own directory X-Git-Tag: v10.1.0~369^2~6 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=d3ac713d88ce9cfc5700e24f7b3a3bcb36b77f80;p=ceph-ci.git Move pybind rbd module into it own directory To allow to create a autonomous rados module with cython. We move the current librbdpy to the rbd sub directory. Signed-off-by: Mehdi Abaakouk --- diff --git a/src/pybind/Makefile.am b/src/pybind/Makefile.am index fd23fcb52f4..78bc558e916 100644 --- a/src/pybind/Makefile.am +++ b/src/pybind/Makefile.am @@ -1,4 +1,3 @@ -EXTRA_DIST += $(srcdir)/pybind/setup.py $(srcdir)/pybind/rbd.pyx if ENABLE_CLIENT if WITH_RADOS @@ -12,38 +11,7 @@ PY_DISTUTILS = \ CYTHON_BUILD_DIR="$(shell readlink -f $(builddir))/build" \ ${PYTHON} ./setup.py -pybind-all: librbd.la ${srcdir}/ceph_ver.h - cd $(srcdir)/pybind; $(PY_DISTUTILS) build \ - --build-base $(shell readlink -f $(builddir))/build \ - --verbose - -pybind-clean: ${srcdir}/ceph_ver.h - cd $(srcdir)/pybind; $(PY_DISTUTILS) clean \ - --build-base $(shell readlink -f $(builddir))/build \ - --verbose - -pybind-install-exec: ${srcdir}/ceph_ver.h - if test "$(DESTDIR)" ; then \ - if lsb_release -si | grep --quiet 'Ubuntu\|Debian\|Devuan' ; then \ - options=--install-layout=deb ; \ - else \ - options=--prefix=/usr ; \ - fi ; \ - root="--root=$(DESTDIR)" ; \ - else \ - options=--prefix=$(prefix) ; \ - fi ; \ - cd $(srcdir)/pybind; $(PY_DISTUTILS) build \ - --build-base $(shell readlink -f $(builddir))/build \ - install \ - $$options $$root \ - --single-version-externally-managed \ - --record /dev/null \ - --verbose - -LOCAL_ALL += pybind-all -LOCAL_CLEAN += pybind-clean -LOCAL_INSTALLEXEC += pybind-install-exec +include pybind/rbd/Makefile.am endif endif diff --git a/src/pybind/rbd.pyx b/src/pybind/rbd.pyx deleted file mode 100644 index a03d97578d3..00000000000 --- a/src/pybind/rbd.pyx +++ /dev/null @@ -1,1442 +0,0 @@ -# cython: embedsignature=True -""" -This module is a thin wrapper around librbd. - -It currently provides all the synchronous methods of librbd that do -not use callbacks. - -Error codes from librbd are turned into exceptions that subclass -:class:`Error`. Almost all methods may raise :class:`Error` -(the base class of all rbd exceptions), :class:`PermissionError` -and :class:`IOError`, in addition to those documented for the -method. -""" -# Copyright 2011 Josh Durgin -# Copyright 2015 Hector Martin - -from cpython cimport PyObject, ref, exc -from libc cimport errno -from libc.stdint cimport * -from libc.stdlib cimport realloc, free - -from collections import Iterable - -cdef extern from "Python.h": - # These are in cpython/string.pxd, but use "object" types instead of - # PyObject*, which invokes assumptions in cpython that we need to - # legitimately break to implement zero-copy string buffers in Image.read(). - # This is valid use of the Python API and documented as a special case. - PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL - char* PyBytes_AsString(PyObject *string) except NULL - int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1 - -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" - _RBD_FEATURE_STRIPINGV2 "RBD_FEATURE_STRIPINGV2" - _RBD_FEATURE_EXCLUSIVE_LOCK "RBD_FEATURE_EXCLUSIVE_LOCK" - _RBD_FEATURE_OBJECT_MAP "RBD_FEATURE_OBJECT_MAP" - _RBD_FEATURE_FAST_DIFF "RBD_FEATURE_FAST_DIFF" - _RBD_FEATURE_DEEP_FLATTEN "RBD_FEATURE_DEEP_FLATTEN" - _RBD_FEATURE_JOURNALING "RBD_FEATURE_JOURNALING" - - _RBD_FEATURES_INCOMPATIBLE "RBD_FEATURES_INCOMPATIBLE" - _RBD_FEATURES_RW_INCOMPATIBLE "RBD_FEATURES_RW_INCOMPATIBLE" - _RBD_FEATURES_MUTABLE "RBD_FEATURES_MUTABLE" - _RBD_FEATURES_SINGLE_CLIENT "RBD_FEATURES_SINGLE_CLIENT" - _RBD_FEATURES_ALL "RBD_FEATURES_ALL" - - _RBD_FLAG_OBJECT_MAP_INVALID "RBD_FLAG_OBJECT_MAP_INVALID" - _RBD_FLAG_FAST_DIFF_INVALID "RBD_FLAG_FAST_DIFF_INVALID" - - _RBD_IMAGE_OPTION_FORMAT "RBD_IMAGE_OPTION_FORMAT" - _RBD_IMAGE_OPTION_FEATURES "RBD_IMAGE_OPTION_FEATURES" - _RBD_IMAGE_OPTION_ORDER "RBD_IMAGE_OPTION_ORDER" - _RBD_IMAGE_OPTION_STRIPE_UNIT "RBD_IMAGE_OPTION_STRIPE_UNIT" - _RBD_IMAGE_OPTION_STRIPE_COUNT "RBD_IMAGE_OPTION_STRIPE_COUNT" - - RBD_MAX_BLOCK_NAME_SIZE - RBD_MAX_IMAGE_NAME_SIZE - - ctypedef void* rados_ioctx_t - ctypedef void* rbd_image_t - ctypedef void* rbd_image_options_t - - ctypedef struct rbd_image_info_t: - uint64_t size - uint64_t obj_size - uint64_t num_objs - int order - char block_name_prefix[RBD_MAX_BLOCK_NAME_SIZE] - uint64_t parent_pool - char parent_name[RBD_MAX_IMAGE_NAME_SIZE] - - ctypedef struct rbd_snap_info_t: - uint64_t id - uint64_t size - char *name - - void rbd_version(int *major, int *minor, int *extra) - - void rbd_image_options_create(rbd_image_options_t* opts) - void rbd_image_options_destroy(rbd_image_options_t opts) - int rbd_image_options_set_string(rbd_image_options_t opts, int optname, - const char* optval) - int rbd_image_options_set_uint64(rbd_image_options_t opts, int optname, - uint64_t optval) - int rbd_image_options_get_string(rbd_image_options_t opts, int optname, - char* optval, size_t maxlen) - int rbd_image_options_get_uint64(rbd_image_options_t opts, int optname, - uint64_t* optval) - int rbd_image_options_unset(rbd_image_options_t opts, int optname) - void rbd_image_options_clear(rbd_image_options_t opts) - int rbd_image_options_is_empty(rbd_image_options_t opts) - - int rbd_list(rados_ioctx_t io, char *names, size_t *size) - int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, - int *order) - int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size, - rbd_image_options_t opts) - 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_rename(rados_ioctx_t src_io_ctx, const char *srcname, - const char *destname) - int rbd_open(rados_ioctx_t io, const char *name, - rbd_image_t *image, const char *snap_name) - int rbd_open_read_only(rados_ioctx_t io, const char *name, - rbd_image_t *image, const char *snap_name) - int rbd_close(rbd_image_t image) - int rbd_resize(rbd_image_t image, uint64_t size) - int rbd_stat(rbd_image_t image, rbd_image_info_t *info, size_t infosize) - int rbd_get_old_format(rbd_image_t image, uint8_t *old) - int rbd_get_size(rbd_image_t image, uint64_t *size) - int rbd_get_features(rbd_image_t image, uint64_t *features) - int rbd_update_features(rbd_image_t image, uint64_t features, - uint8_t enabled) - int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit) - int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count) - int rbd_get_overlap(rbd_image_t image, uint64_t *overlap) - int rbd_get_parent_info(rbd_image_t image, - char *parent_poolname, size_t ppoolnamelen, - char *parent_name, size_t pnamelen, - char *parent_snapname, size_t psnapnamelen) - int rbd_get_flags(rbd_image_t image, uint64_t *flags) - int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner) - ssize_t rbd_read2(rbd_image_t image, uint64_t ofs, size_t len, - char *buf, int op_flags) - ssize_t rbd_write2(rbd_image_t image, uint64_t ofs, size_t len, - const char *buf, int op_flags) - int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len) - int rbd_copy3(rbd_image_t src, rados_ioctx_t dest_io_ctx, - const char *destname, rbd_image_options_t dest_opts) - int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, - int *max_snaps) - void rbd_snap_list_end(rbd_snap_info_t *snaps) - int rbd_snap_create(rbd_image_t image, const char *snapname) - int rbd_snap_remove(rbd_image_t image, const char *snapname) - int rbd_snap_rollback(rbd_image_t image, const char *snapname) - int rbd_snap_rename(rbd_image_t image, const char *snapname, - const char* dstsnapsname) - int rbd_snap_protect(rbd_image_t image, const char *snap_name) - int rbd_snap_unprotect(rbd_image_t image, const char *snap_name) - int rbd_snap_is_protected(rbd_image_t image, const char *snap_name, - int *is_protected) - int rbd_snap_set(rbd_image_t image, const char *snapname) - int rbd_flatten(rbd_image_t image) - int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb, - void *cbdata) - ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len, - char *images, size_t *images_len) - ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive, - char *tag, size_t *tag_len, - char *clients, size_t *clients_len, - char *cookies, size_t *cookies_len, - char *addrs, size_t *addrs_len) - int rbd_lock_exclusive(rbd_image_t image, const char *cookie) - int rbd_lock_shared(rbd_image_t image, const char *cookie, - const char *tag) - int rbd_unlock(rbd_image_t image, const char *cookie) - int rbd_break_lock(rbd_image_t image, const char *client, - const char *cookie) - - # We use -9000 to propagate Python exceptions. We use except? to make sure - # things still work as intended if -9000 happens to be a valid errno value - # somewhere. - int rbd_diff_iterate2(rbd_image_t image, const char *fromsnapname, - uint64_t ofs, uint64_t len, - uint8_t include_parent, uint8_t whole_object, - int (*cb)(uint64_t, size_t, int, void *) - nogil except? -9000, - void *arg) except? -9000 - - int rbd_flush(rbd_image_t image) - int rbd_invalidate_cache(rbd_image_t image) - - -RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING -RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2 -RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK -RBD_FEATURE_OBJECT_MAP = _RBD_FEATURE_OBJECT_MAP -RBD_FEATURE_FAST_DIFF = _RBD_FEATURE_FAST_DIFF -RBD_FEATURE_DEEP_FLATTEN = _RBD_FEATURE_DEEP_FLATTEN -RBD_FEATURE_JOURNALING = _RBD_FEATURE_JOURNALING - -RBD_FEATURES_INCOMPATIBLE = _RBD_FEATURES_INCOMPATIBLE -RBD_FEATURES_RW_INCOMPATIBLE = _RBD_FEATURES_RW_INCOMPATIBLE -RBD_FEATURES_MUTABLE = _RBD_FEATURES_MUTABLE -RBD_FEATURES_SINGLE_CLIENT = _RBD_FEATURES_SINGLE_CLIENT -RBD_FEATURES_ALL = _RBD_FEATURES_ALL - -RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID - -RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT -RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES -RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER -RBD_IMAGE_OPTION_STRIPE_UNIT = _RBD_IMAGE_OPTION_STRIPE_UNIT -RBD_IMAGE_OPTION_STRIPE_COUNT = _RBD_IMAGE_OPTION_STRIPE_COUNT - - -class Error(Exception): - pass - - -class PermissionError(Error): - pass - - -class ImageNotFound(Error): - pass - - -class ImageExists(Error): - pass - - -class IOError(Error): - pass - - -class NoSpace(Error): - pass - - -class IncompleteWriteError(Error): - pass - - -class InvalidArgument(Error): - pass - - -class LogicError(Error): - pass - - -class ReadOnlyImage(Error): - pass - - -class ImageBusy(Error): - pass - - -class ImageHasSnapshots(Error): - pass - - -class FunctionNotSupported(Error): - pass - - -class ArgumentOutOfRange(Error): - pass - - -class ConnectionShutdown(Error): - pass - - -class Timeout(Error): - pass - - -cdef errno_to_exception = { - errno.EPERM : PermissionError, - errno.ENOENT : ImageNotFound, - errno.EIO : IOError, - errno.ENOSPC : NoSpace, - errno.EEXIST : ImageExists, - errno.EINVAL : InvalidArgument, - errno.EROFS : ReadOnlyImage, - errno.EBUSY : ImageBusy, - errno.ENOTEMPTY : ImageHasSnapshots, - errno.ENOSYS : FunctionNotSupported, - errno.EDOM : ArgumentOutOfRange, - errno.ESHUTDOWN : ConnectionShutdown, - errno.ETIMEDOUT : Timeout, -} - -cdef make_ex(ret, msg): - """ - Translate a librbd return code into an exception. - - :param ret: the return code - :type ret: int - :param msg: the error message to use - :type msg: str - :returns: a subclass of :class:`Error` - """ - ret = abs(ret) - if ret in errno_to_exception: - return errno_to_exception[ret](msg) - else: - return Error(msg + (": error code %d" % ret)) - -cdef rados_ioctx_t convert_ioctx(ioctx) except? NULL: - return ioctx.io.value - -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): - """ - Create a byte string from a Python string - - :param basestring val: Python string - :param str name: Name of the string parameter, for exceptions - :param str encoding: Encoding to use - :param bool opt: If True, None is allowed - :rtype: bytes - :raises: :class:`InvalidArgument` - """ - if opt and val is None: - return None - if isinstance(val, bytes): - return val - elif isinstance(val, unicode): - return val.encode(encoding) - else: - raise InvalidArgument('%s must be a string' % name) - -def decode_cstr(val, encoding="utf-8"): - """ - Decode a byte string into a Python string. - - :param bytes val: byte string - :rtype: unicode or None - """ - if val is None: - return None - - return val.decode(encoding) - - -cdef char* opt_str(s) except? NULL: - if s is None: - return NULL - return s - -cdef void* realloc_chk(void* ptr, size_t size) except NULL: - cdef void *ret = realloc(ptr, size) - if ret == NULL: - raise MemoryError("realloc failed") - return ret - -class RBD(object): - """ - This class wraps librbd CRUD functions. - """ - def version(self): - """ - Get the version number of the ``librbd`` C library. - - :returns: a tuple of ``(major, minor, extra)`` components of the - librbd version - """ - cdef int major = 0 - cdef int minor = 0 - cdef int extra = 0 - rbd_version(&major, &minor, &extra) - return (major, minor, extra) - - def create(self, ioctx, name, size, order=None, old_format=True, - features=0, stripe_unit=0, stripe_count=0): - """ - Create an rbd image. - - :param ioctx: the context in which to create the image - :type ioctx: :class:`rados.Ioctx` - :param name: what the image is called - :type name: str - :param size: how big the image is in bytes - :type size: int - :param order: the image is split into (2**order) byte objects - :type order: int - :param old_format: whether to create an old-style image that - is accessible by old clients, but can't - use more advanced features like layering. - :type old_format: bool - :param features: bitmask of features to enable - :type features: int - :param stripe_unit: stripe unit in bytes (default 0 for object size) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :raises: :class:`ImageExists` - :raises: :class:`TypeError` - :raises: :class:`InvalidArgument` - :raises: :class:`FunctionNotSupported` - """ - name = cstr(name, 'name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_name = name - uint64_t _size = size - int _order = 0 - rbd_image_options_t opts - if order is not None: - _order = order - if old_format: - if features != 0 or stripe_unit != 0 or stripe_count != 0: - raise InvalidArgument('format 1 images do not support feature' - ' masks or non-default striping') - with nogil: - ret = rbd_create(_ioctx, _name, _size, &_order) - else: - rbd_image_options_create(&opts) - try: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FORMAT, - 1 if old_format else 2) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, - features) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - _order) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - rbd_image_options_set_uint64(opts, - RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - with nogil: - ret = rbd_create4(_ioctx, _name, _size, opts) - finally: - rbd_image_options_destroy(opts) - if ret < 0: - raise make_ex(ret, 'error creating image') - - def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name, - features=0, order=None, stripe_unit=0, stripe_count=0): - """ - Clone a parent rbd snapshot into a COW sparse child. - - :param p_ioctx: the parent context that represents the parent snap - :type ioctx: :class:`rados.Ioctx` - :param p_name: the parent image name - :type name: str - :param p_snapname: the parent image snapshot name - :type name: str - :param c_ioctx: the child context that represents the new clone - :type ioctx: :class:`rados.Ioctx` - :param c_name: the clone (child) name - :type name: str - :param features: bitmask of features to enable; if set, must include layering - :type features: int - :param order: the image is split into (2**order) byte objects - :type order: int - :param stripe_unit: stripe unit in bytes (default 0 for object size) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :raises: :class:`TypeError` - :raises: :class:`InvalidArgument` - :raises: :class:`ImageExists` - :raises: :class:`FunctionNotSupported` - :raises: :class:`ArgumentOutOfRange` - """ - p_snapname = cstr(p_snapname, 'p_snapname') - p_name = cstr(p_name, 'p_name') - c_name = cstr(c_name, 'c_name') - cdef: - rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx) - rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx) - char *_p_name = p_name - char *_p_snapname = p_snapname - char *_c_name = c_name - rbd_image_options_t opts - if order is None: - order = 0 - - rbd_image_options_create(&opts) - try: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, - features) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - order) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - with nogil: - ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname, - _c_ioctx, _c_name, opts) - finally: - rbd_image_options_destroy(opts) - if ret < 0: - raise make_ex(ret, 'error creating clone') - - def list(self, ioctx): - """ - List image names. - - :param ioctx: determines which RADOS pool is read - :type ioctx: :class:`rados.Ioctx` - :returns: list -- a list of image names - """ - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - size_t size = 512 - char *c_names = NULL - try: - while True: - c_names = realloc_chk(c_names, size) - with nogil: - ret = rbd_list(_ioctx, c_names, &size) - if ret >= 0: - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing images') - return [decode_cstr(name) for name in c_names[:ret].split('\0') - if name] - finally: - free(c_names) - - def remove(self, ioctx, name): - """ - Delete an RBD image. This may take a long time, since it does - not return until every object that comprises the image has - been deleted. Note that all snapshots must be deleted before - the image can be removed. If there are snapshots left, - :class:`ImageHasSnapshots` is raised. If the image is still - open, or the watch from a crashed client has not expired, - :class:`ImageBusy` is raised. - - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param name: the name of the image to remove - :type name: str - :raises: :class:`ImageNotFound`, :class:`ImageBusy`, - :class:`ImageHasSnapshots` - """ - name = cstr(name, 'name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_name = name - with nogil: - ret = rbd_remove(_ioctx, _name) - if ret != 0: - raise make_ex(ret, 'error removing image') - - def rename(self, ioctx, src, dest): - """ - Rename an RBD image. - - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param src: the current name of the image - :type src: str - :param dest: the new name of the image - :type dest: str - :raises: :class:`ImageNotFound`, :class:`ImageExists` - """ - src = cstr(src, 'src') - dest = cstr(dest, 'dest') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_src = src - char *_dest = dest - with nogil: - ret = rbd_rename(_ioctx, _src, _dest) - if ret != 0: - raise make_ex(ret, 'error renaming image') - - -cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \ - except? -9000 with gil: - # Make sure that if we wound up with an exception from a previous callback, - # we stop calling back (just in case librbd ever fails to bail out on the - # first negative return, as older versions did) - if exc.PyErr_Occurred(): - return -9000 - ret = (cb)(offset, length, bool(write)) - if ret is None: - return 0 - return ret - - -cdef class Image(object): - """ - This class represents an RBD image. It is used to perform I/O on - the image and interact with snapshots. - - **Note**: Any method of this class may raise :class:`ImageNotFound` - if the image has been deleted. - """ - cdef rbd_image_t image - cdef bint closed - cdef object name - cdef object ioctx - cdef rados_ioctx_t _ioctx - - def __init__(self, ioctx, name, snapshot=None, read_only=False): - """ - Open the image at the given snapshot. - If a snapshot is specified, the image will be read-only, unless - :func:`Image.set_snap` is called later. - - If read-only mode is used, metadata for the :class:`Image` - object (such as which snapshots exist) may become obsolete. See - the C api for more details. - - To clean up from opening the image, :func:`Image.close` should - be called. For ease of use, this is done automatically when - an :class:`Image` is used as a context manager (see :pep:`343`). - - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param name: the name of the image - :type name: str - :param snapshot: which snapshot to read from - :type snaphshot: str - :param read_only: whether to open the image in read-only mode - :type read_only: bool - """ - name = cstr(name, 'name') - snapshot = cstr(snapshot, 'snapshot', opt=True) - self.closed = True - self.name = name - # Keep around a reference to the ioctx, so it won't get deleted - self.ioctx = ioctx - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_name = name - char *_snapshot = opt_str(snapshot) - if read_only: - with nogil: - ret = rbd_open_read_only(_ioctx, _name, &self.image, _snapshot) - else: - with nogil: - ret = rbd_open(_ioctx, _name, &self.image, _snapshot) - if ret != 0: - raise make_ex(ret, 'error opening image %s at snapshot %s' % (name, snapshot)) - self.closed = False - - def __enter__(self): - return self - - def __exit__(self, type_, value, traceback): - """ - Closes the image. See :func:`close` - """ - self.close() - return False - - def close(self): - """ - Release the resources used by this image object. - - After this is called, this object should not be used. - """ - if not self.closed: - with nogil: - ret = rbd_close(self.image) - if ret < 0: - raise make_ex(ret, 'error while closing image %s' % ( - self.name,)) - self.closed = True - - def __del__(self): - self.close() - - def __repr__(self): - return "rbd.Image(ioctx, %r)" % self.name - - def resize(self, size): - """ - Change the size of the image. - - :param size: the new size of the image - :type size: int - """ - cdef uint64_t _size = size - with nogil: - ret = rbd_resize(self.image, _size) - if ret < 0: - raise make_ex(ret, 'error resizing image %s' % (self.name,)) - - def stat(self): - """ - Get information about the image. Currently parent pool and - parent name are always -1 and ''. - - :returns: dict - contains the following keys: - - * ``size`` (int) - the size of the image in bytes - - * ``obj_size`` (int) - the size of each object that comprises the - image - - * ``num_objs`` (int) - the number of objects in the image - - * ``order`` (int) - log_2(object_size) - - * ``block_name_prefix`` (str) - the prefix of the RADOS objects used - to store the image - - * ``parent_pool`` (int) - deprecated - - * ``parent_name`` (str) - deprecated - - See also :meth:`format` and :meth:`features`. - - """ - cdef rbd_image_info_t info - with nogil: - ret = rbd_stat(self.image, &info, sizeof(info)) - if ret != 0: - raise make_ex(ret, 'error getting info for image %s' % (self.name,)) - return { - 'size' : info.size, - 'obj_size' : info.obj_size, - 'num_objs' : info.num_objs, - 'order' : info.order, - 'block_name_prefix' : decode_cstr(info.block_name_prefix), - 'parent_pool' : info.parent_pool, - 'parent_name' : info.parent_name - } - - def parent_info(self): - """ - Get information about a cloned image's parent (if any) - - :returns: tuple - ``(pool name, image name, snapshot name)`` components - of the parent image - :raises: :class:`ImageNotFound` if the image doesn't have a parent - """ - cdef: - int ret = -errno.ERANGE - size_t size = 8 - char *pool = NULL - char *name = NULL - char *snapname = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - pool = realloc_chk(pool, size) - name = realloc_chk(name, size) - snapname = realloc_chk(snapname, size) - with nogil: - ret = rbd_get_parent_info(self.image, pool, size, name, - size, snapname, size) - if ret == -errno.ERANGE: - size *= 2 - - if ret != 0: - raise make_ex(ret, 'error getting parent info for image %s' % (self.name,)) - return (decode_cstr(pool), decode_cstr(name), decode_cstr(snapname)) - finally: - free(pool) - free(name) - free(snapname) - - def old_format(self): - """ - Find out whether the image uses the old RBD format. - - :returns: bool - whether the image uses the old RBD format - """ - cdef uint8_t old - with nogil: - ret = rbd_get_old_format(self.image, &old) - if ret != 0: - raise make_ex(ret, 'error getting old_format for image' % (self.name)) - return old != 0 - - def size(self): - """ - Get the size of the image. If open to a snapshot, returns the - size of that snapshot. - - :returns: the size of the image in bytes - """ - cdef uint64_t image_size - with nogil: - ret = rbd_get_size(self.image, &image_size) - if ret != 0: - raise make_ex(ret, 'error getting size for image' % (self.name)) - return image_size - - def features(self): - """ - Gets the features bitmask of the image. - - :returns: int - the features bitmask of the image - """ - cdef uint64_t features - with nogil: - ret = rbd_get_features(self.image, &features) - if ret != 0: - raise make_ex(ret, 'error getting features for image' % (self.name)) - return features - - def update_features(self, features, enabled): - """ - Updates the features bitmask of the image by enabling/disabling - a single feature. The feature must support the ability to be - dynamically enabled/disabled. - - :param features: feature bitmask to enable/disable - :type features: int - :param enabled: whether to enable/disable the feature - :type enabled: bool - :raises: :class:`InvalidArgument` - """ - cdef: - uint64_t _features = features - uint8_t _enabled = bool(enabled) - with nogil: - ret = rbd_update_features(self.image, _features, _enabled) - if ret != 0: - raise make_ex(ret, 'error updating features for image %s' % - (self.name)) - - def overlap(self): - """ - Gets the number of overlapping bytes between the image and its parent - image. If open to a snapshot, returns the overlap between the snapshot - and the parent image. - - :returns: int - the overlap in bytes - :raises: :class:`ImageNotFound` if the image doesn't have a parent - """ - cdef uint64_t overlap - with nogil: - ret = rbd_get_overlap(self.image, &overlap) - if ret != 0: - raise make_ex(ret, 'error getting overlap for image' % (self.name)) - return overlap - - def flags(self): - """ - Gets the flags bitmask of the image. - - :returns: int - the flags bitmask of the image - """ - cdef uint64_t flags - with nogil: - ret = rbd_get_flags(self.image, &flags) - if ret != 0: - raise make_ex(ret, 'error getting flags for image' % (self.name)) - return flags - - def is_exclusive_lock_owner(self): - """ - Gets the status of the image exclusive lock. - - :returns: bool - true if the image is exclusively locked - """ - cdef int owner - with nogil: - ret = rbd_is_exclusive_lock_owner(self.image, &owner) - if ret != 0: - raise make_ex(ret, 'error getting lock status for image' % (self.name)) - return owner == 1 - - def copy(self, dest_ioctx, dest_name, features=0, order=None, stripe_unit=0, - stripe_count=0): - """ - Copy the image to another location. - - :param dest_ioctx: determines which pool to copy into - :type dest_ioctx: :class:`rados.Ioctx` - :param dest_name: the name of the copy - :type dest_name: str - :param features: bitmask of features to enable; if set, must include layering - :type features: int - :param order: the image is split into (2**order) byte objects - :type order: int - :param stripe_unit: stripe unit in bytes (default 0 for object size) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :raises: :class:`TypeError` - :raises: :class:`InvalidArgument` - :raises: :class:`ImageExists` - :raises: :class:`FunctionNotSupported` - :raises: :class:`ArgumentOutOfRange` - """ - if order is None: - order = 0 - dest_name = cstr(dest_name, 'dest_name') - cdef: - rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx) - char *_dest_name = dest_name - rbd_image_options_t opts - - rbd_image_options_create(&opts) - try: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, - features) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - order) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - with nogil: - ret = rbd_copy3(self.image, _dest_ioctx, _dest_name, opts) - finally: - rbd_image_options_destroy(opts) - if ret < 0: - raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name)) - - def list_snaps(self): - """ - Iterate over the snapshots of an image. - - :returns: :class:`SnapIterator` - """ - return SnapIterator(self) - - def create_snap(self, name): - """ - Create a snapshot of the image. - - :param name: the name of the snapshot - :type name: str - :raises: :class:`ImageExists` - """ - name = cstr(name, 'name') - cdef char *_name = name - with nogil: - ret = rbd_snap_create(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error creating snapshot %s from %s' % (name, self.name)) - - def rename_snap(self, srcname, dstname): - """ - rename a snapshot of the image. - - :param srcname: the src name of the snapshot - :type srcname: str - :param dstname: the dst name of the snapshot - :type dstname: str - :raises: :class:`ImageExists` - """ - srcname = cstr(srcname, 'srcname') - dstname = cstr(dstname, 'dstname') - cdef: - char *_srcname = srcname - char *_dstname = dstname - with nogil: - ret = rbd_snap_rename(self.image, _srcname, _dstname) - if ret != 0: - raise make_ex(ret, 'error renaming snapshot of %s from %s to %s' % (self.name, srcname, dstname)) - - def remove_snap(self, name): - """ - Delete a snapshot of the image. - - :param name: the name of the snapshot - :type name: str - :raises: :class:`IOError`, :class:`ImageBusy` - """ - name = cstr(name, 'name') - cdef char *_name = name - with nogil: - ret = rbd_snap_remove(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error removing snapshot %s from %s' % (name, self.name)) - - def rollback_to_snap(self, name): - """ - Revert the image to its contents at a snapshot. This is a - potentially expensive operation, since it rolls back each - object individually. - - :param name: the snapshot to rollback to - :type name: str - :raises: :class:`IOError` - """ - name = cstr(name, 'name') - cdef char *_name = name - with nogil: - ret = rbd_snap_rollback(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error rolling back image %s to snapshot %s' % (self.name, name)) - - def protect_snap(self, name): - """ - Mark a snapshot as protected. This means it can't be deleted - until it is unprotected. - - :param name: the snapshot to protect - :type name: str - :raises: :class:`IOError`, :class:`ImageNotFound` - """ - name = cstr(name, 'name') - cdef char *_name = name - with nogil: - ret = rbd_snap_protect(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error protecting snapshot %s@%s' % (self.name, name)) - - def unprotect_snap(self, name): - """ - Mark a snapshot unprotected. This allows it to be deleted if - it was protected. - - :param name: the snapshot to unprotect - :type name: str - :raises: :class:`IOError`, :class:`ImageNotFound` - """ - name = cstr(name, 'name') - cdef char *_name = name - with nogil: - ret = rbd_snap_unprotect(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error unprotecting snapshot %s@%s' % (self.name, name)) - - def is_protected_snap(self, name): - """ - Find out whether a snapshot is protected from deletion. - - :param name: the snapshot to check - :type name: str - :returns: bool - whether the snapshot is protected - :raises: :class:`IOError`, :class:`ImageNotFound` - """ - name = cstr(name, 'name') - cdef: - char *_name = name - int is_protected - with nogil: - ret = rbd_snap_is_protected(self.image, _name, &is_protected) - if ret != 0: - raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name)) - return is_protected == 1 - - def set_snap(self, name): - """ - Set the snapshot to read from. Writes will raise ReadOnlyImage - while a snapshot is set. Pass None to unset the snapshot - (reads come from the current image) , and allow writing again. - - :param name: the snapshot to read from, or None to unset the snapshot - :type name: str or None - """ - name = cstr(name, 'name', opt=True) - cdef char *_name = opt_str(name) - with nogil: - ret = rbd_snap_set(self.image, _name) - if ret != 0: - raise make_ex(ret, 'error setting image %s to snapshot %s' % (self.name, name)) - - def read(self, offset, length, fadvise_flags=0): - """ - Read data from the image. Raises :class:`InvalidArgument` if - part of the range specified is outside the image. - - :param offset: the offset to start reading at - :type offset: int - :param length: how many bytes to read - :type length: int - :param fadvise_flags: fadvise flags for this read - :type fadvise_flags: int - :returns: str - the data read - :raises: :class:`InvalidArgument`, :class:`IOError` - """ - - # This usage of the Python API allows us to construct a string - # that librbd directly reads into, avoiding an extra copy. Although - # strings are normally immutable, this usage is explicitly supported - # for freshly created string objects. - cdef: - char *ret_buf - uint64_t _offset = offset - size_t _length = length - int _fadvise_flags = fadvise_flags - PyObject* ret_s = NULL - ret_s = PyBytes_FromStringAndSize(NULL, length) - try: - ret_buf = PyBytes_AsString(ret_s) - with nogil: - ret = rbd_read2(self.image, _offset, _length, ret_buf, - _fadvise_flags) - if ret < 0: - raise make_ex(ret, 'error reading %s %ld~%ld' % (self.name, offset, length)) - - if ret != length: - _PyBytes_Resize(&ret_s, ret) - - return ret_s - finally: - # We DECREF unconditionally: the cast to object above will have - # INCREFed if necessary. This also takes care of exceptions, - # including if _PyString_Resize fails (that will free the string - # itself and set ret_s to NULL, hence XDECREF). - ref.Py_XDECREF(ret_s) - - def diff_iterate(self, offset, length, from_snapshot, iterate_cb, - include_parent = True, whole_object = False): - """ - 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. - - iterate_cb may raise an exception, which will abort the diff and will be - propagated to the caller. - - 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 - :param include_parent: True if full history diff should include parent - :type include_parent: bool - :param whole_object: True if diff extents should cover whole object - :type whole_object: bool - :raises: :class:`InvalidArgument`, :class:`IOError`, - :class:`ImageNotFound` - """ - from_snapshot = cstr(from_snapshot, 'from_snapshot', opt=True) - cdef: - char *_from_snapshot = opt_str(from_snapshot) - uint64_t _offset = offset, _length = length - uint8_t _include_parent = include_parent - uint8_t _whole_object = whole_object - with nogil: - ret = rbd_diff_iterate2(self.image, _from_snapshot, _offset, - _length, _include_parent, _whole_object, - &diff_iterate_cb, iterate_cb) - if ret < 0: - msg = 'error generating diff from snapshot %s' % from_snapshot - raise make_ex(ret, msg) - - def write(self, data, offset, fadvise_flags=0): - """ - Write data to the image. Raises :class:`InvalidArgument` if - part of the write would fall outside the image. - - :param data: the data to be written - :type data: bytes - :param offset: where to start writing data - :type offset: int - :param fadvise_flags: fadvise flags for this write - :type fadvise_flags: int - :returns: int - the number of bytes written - :raises: :class:`IncompleteWriteError`, :class:`LogicError`, - :class:`InvalidArgument`, :class:`IOError` - """ - if not isinstance(data, bytes): - raise TypeError('data must be a byte string') - cdef: - uint64_t _offset = offset, length = len(data) - char *_data = data - int _fadvise_flags = fadvise_flags - with nogil: - ret = rbd_write2(self.image, _offset, length, _data, _fadvise_flags) - - if ret == length: - return ret - elif ret < 0: - raise make_ex(ret, "error writing to %s" % (self.name,)) - elif ret < length: - raise IncompleteWriteError("Wrote only %ld out of %ld bytes" % (ret, length)) - else: - raise LogicError("logic error: rbd_write(%s) \ -returned %d, but %d was the maximum number of bytes it could have \ -written." % (self.name, ret, length)) - - def discard(self, offset, length): - """ - Trim the range from the image. It will be logically filled - with zeroes. - """ - cdef uint64_t _offset = offset, _length = length - with nogil: - ret = rbd_discard(self.image, _offset, _length) - if ret < 0: - msg = 'error discarding region %d~%d' % (offset, length) - raise make_ex(ret, msg) - - def flush(self): - """ - Block until all writes are fully flushed if caching is enabled. - """ - with nogil: - ret = rbd_flush(self.image) - if ret < 0: - raise make_ex(ret, 'error flushing image') - - def invalidate_cache(self): - """ - Drop any cached data for the image. - """ - with nogil: - ret = rbd_invalidate_cache(self.image) - if ret < 0: - raise make_ex(ret, 'error invalidating cache') - - def stripe_unit(self): - """ - Returns the stripe unit used for the image. - """ - cdef uint64_t stripe_unit - with nogil: - ret = rbd_get_stripe_unit(self.image, &stripe_unit) - if ret != 0: - raise make_ex(ret, 'error getting stripe unit for image' % (self.name)) - return stripe_unit - - def stripe_count(self): - """ - Returns the stripe count used for the image. - """ - cdef uint64_t stripe_count - with nogil: - ret = rbd_get_stripe_count(self.image, &stripe_count) - if ret != 0: - raise make_ex(ret, 'error getting stripe count for image' % (self.name)) - return stripe_count - - def flatten(self): - """ - Flatten clone image (copy all blocks from parent to child) - """ - with nogil: - ret = rbd_flatten(self.image) - if ret < 0: - raise make_ex(ret, "error flattening %s" % self.name) - - def rebuild_object_map(self): - """ - Rebuilds the object map for the image HEAD or currently set snapshot - """ - cdef librbd_progress_fn_t prog_cb = &no_op_progress_callback - with nogil: - ret = rbd_rebuild_object_map(self.image, prog_cb, NULL) - if ret < 0: - raise make_ex(ret, "error rebuilding object map %s" % self.name) - - def list_children(self): - """ - List children of the currently set snapshot (set via set_snap()). - - :returns: list - a list of (pool name, image name) tuples - """ - cdef: - size_t pools_size = 512, images_size = 512 - char *c_pools = NULL - char *c_images = NULL - try: - while True: - c_pools = realloc_chk(c_pools, pools_size) - c_images = realloc_chk(c_images, images_size) - with nogil: - ret = rbd_list_children(self.image, c_pools, &pools_size, - c_images, &images_size) - if ret >= 0: - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing images') - if ret == 0: - return [] - pools = map(decode_cstr, c_pools[:pools_size - 1].split('\0')) - images = map(decode_cstr, c_images[:images_size - 1].split('\0')) - return list(zip(pools, images)) - finally: - free(c_pools) - free(c_images) - - def list_lockers(self): - """ - List clients that have locked the image and information - about the lock. - - :returns: dict - contains the following keys: - - * ``tag`` - the tag associated with the lock (every - additional locker must use the same tag) - * ``exclusive`` - boolean indicating whether the - lock is exclusive or shared - * ``lockers`` - a list of (client, cookie, address) - tuples - """ - cdef: - size_t clients_size = 512, cookies_size = 512 - size_t addrs_size = 512, tag_size = 512 - int exclusive = 0 - char *c_clients = NULL - char *c_cookies = NULL - char *c_addrs = NULL - char *c_tag = NULL - - try: - while True: - c_clients = realloc_chk(c_clients, clients_size) - c_cookies = realloc_chk(c_cookies, cookies_size) - c_addrs = realloc_chk(c_addrs, addrs_size) - c_tag = realloc_chk(c_tag, tag_size) - with nogil: - ret = rbd_list_lockers(self.image, &exclusive, - c_tag, &tag_size, - c_clients, &clients_size, - c_cookies, &cookies_size, - c_addrs, &addrs_size) - if ret >= 0: - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing images') - if ret == 0: - return [] - clients = map(decode_cstr, c_clients[:clients_size - 1].split('\0')) - cookies = map(decode_cstr, c_cookies[:cookies_size - 1].split('\0')) - addrs = map(decode_cstr, c_addrs[:addrs_size - 1].split('\0')) - return { - 'tag' : decode_cstr(c_tag), - 'exclusive' : exclusive == 1, - 'lockers' : list(zip(clients, cookies, addrs)), - } - finally: - free(c_clients) - free(c_cookies) - free(c_addrs) - free(c_tag) - - def lock_exclusive(self, cookie): - """ - Take an exclusive lock on the image. - - :raises: :class:`ImageBusy` if a different client or cookie locked it - :class:`ImageExists` if the same client and cookie locked it - """ - cookie = cstr(cookie, 'cookie') - cdef char *_cookie = cookie - with nogil: - ret = rbd_lock_exclusive(self.image, _cookie) - if ret < 0: - raise make_ex(ret, 'error acquiring exclusive lock on image') - - def lock_shared(self, cookie, tag): - """ - Take a shared lock on the image. The tag must match - that of the existing lockers, if any. - - :raises: :class:`ImageBusy` if a different client or cookie locked it - :class:`ImageExists` if the same client and cookie locked it - """ - cookie = cstr(cookie, 'cookie') - tag = cstr(tag, 'tag') - cdef: - char *_cookie = cookie - char *_tag = tag - with nogil: - ret = rbd_lock_shared(self.image, _cookie, _tag) - if ret < 0: - raise make_ex(ret, 'error acquiring shared lock on image') - - def unlock(self, cookie): - """ - Release a lock on the image that was locked by this rados client. - """ - cookie = cstr(cookie, 'cookie') - cdef char *_cookie = cookie - with nogil: - ret = rbd_unlock(self.image, _cookie) - if ret < 0: - raise make_ex(ret, 'error unlocking image') - - def break_lock(self, client, cookie): - """ - Release a lock held by another rados client. - """ - client = cstr(client, 'client') - cookie = cstr(cookie, 'cookie') - cdef: - char *_client = client - char *_cookie = cookie - with nogil: - ret = rbd_break_lock(self.image, _client, _cookie) - if ret < 0: - raise make_ex(ret, 'error unlocking image') - - -cdef class SnapIterator(object): - """ - Iterator over snapshot info for an image. - - Yields a dictionary containing information about a snapshot. - - Keys are: - - * ``id`` (int) - numeric identifier of the snapshot - - * ``size`` (int) - size of the image at the time of snapshot (in bytes) - - * ``name`` (str) - name of the snapshot - """ - - cdef rbd_snap_info_t *snaps - cdef int num_snaps - - def __init__(self, Image image): - self.snaps = NULL - self.num_snaps = 10 - while True: - self.snaps = realloc_chk(self.snaps, - self.num_snaps * - sizeof(rbd_snap_info_t)) - with nogil: - ret = rbd_snap_list(image.image, self.snaps, &self.num_snaps) - if ret >= 0: - self.num_snaps = ret - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing snapshots for image %s' % (image.name,)) - - def __iter__(self): - for i in range(self.num_snaps): - yield { - 'id' : self.snaps[i].id, - 'size' : self.snaps[i].size, - 'name' : decode_cstr(self.snaps[i].name), - } - - def __del__(self): - if self.snaps: - rbd_snap_list_end(self.snaps) - free(self.snaps) diff --git a/src/pybind/rbd/Makefile.am b/src/pybind/rbd/Makefile.am new file mode 100644 index 00000000000..affd2de5054 --- /dev/null +++ b/src/pybind/rbd/Makefile.am @@ -0,0 +1,34 @@ +EXTRA_DIST += $(srcdir)/pybind/rbd/setup.py $(srcdir)/pybind/rbd/rbd.pyx + +rbd-pybind-all: librbd.la ${srcdir}/ceph_ver.h + cd $(srcdir)/pybind/rbd; $(PY_DISTUTILS) build \ + --build-base $(shell readlink -f $(builddir))/build \ + --verbose + +rbd-pybind-clean: ${srcdir}/ceph_ver.h + cd $(srcdir)/pybind/rbd; $(PY_DISTUTILS) clean \ + --build-base $(shell readlink -f $(builddir))/build \ + --verbose + +rbd-pybind-install-exec: ${srcdir}/ceph_ver.h + if test "$(DESTDIR)" ; then \ + if lsb_release -si | grep --quiet 'Ubuntu\|Debian\|Devuan' ; then \ + options=--install-layout=deb ; \ + else \ + options=--prefix=/usr ; \ + fi ; \ + root="--root=$(DESTDIR)" ; \ + else \ + options=--prefix=$(prefix) ; \ + fi ; \ + cd $(srcdir)/pybind/rbd; $(PY_DISTUTILS) build \ + --build-base $(shell readlink -f $(builddir))/build \ + install \ + $$options $$root \ + --single-version-externally-managed \ + --record /dev/null \ + --verbose + +LOCAL_ALL += rbd-pybind-all +LOCAL_CLEAN += rbd-pybind-clean +LOCAL_INSTALLEXEC += rbd-pybind-install-exec diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx new file mode 100644 index 00000000000..a03d97578d3 --- /dev/null +++ b/src/pybind/rbd/rbd.pyx @@ -0,0 +1,1442 @@ +# cython: embedsignature=True +""" +This module is a thin wrapper around librbd. + +It currently provides all the synchronous methods of librbd that do +not use callbacks. + +Error codes from librbd are turned into exceptions that subclass +:class:`Error`. Almost all methods may raise :class:`Error` +(the base class of all rbd exceptions), :class:`PermissionError` +and :class:`IOError`, in addition to those documented for the +method. +""" +# Copyright 2011 Josh Durgin +# Copyright 2015 Hector Martin + +from cpython cimport PyObject, ref, exc +from libc cimport errno +from libc.stdint cimport * +from libc.stdlib cimport realloc, free + +from collections import Iterable + +cdef extern from "Python.h": + # These are in cpython/string.pxd, but use "object" types instead of + # PyObject*, which invokes assumptions in cpython that we need to + # legitimately break to implement zero-copy string buffers in Image.read(). + # This is valid use of the Python API and documented as a special case. + PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL + char* PyBytes_AsString(PyObject *string) except NULL + int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1 + +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" + _RBD_FEATURE_STRIPINGV2 "RBD_FEATURE_STRIPINGV2" + _RBD_FEATURE_EXCLUSIVE_LOCK "RBD_FEATURE_EXCLUSIVE_LOCK" + _RBD_FEATURE_OBJECT_MAP "RBD_FEATURE_OBJECT_MAP" + _RBD_FEATURE_FAST_DIFF "RBD_FEATURE_FAST_DIFF" + _RBD_FEATURE_DEEP_FLATTEN "RBD_FEATURE_DEEP_FLATTEN" + _RBD_FEATURE_JOURNALING "RBD_FEATURE_JOURNALING" + + _RBD_FEATURES_INCOMPATIBLE "RBD_FEATURES_INCOMPATIBLE" + _RBD_FEATURES_RW_INCOMPATIBLE "RBD_FEATURES_RW_INCOMPATIBLE" + _RBD_FEATURES_MUTABLE "RBD_FEATURES_MUTABLE" + _RBD_FEATURES_SINGLE_CLIENT "RBD_FEATURES_SINGLE_CLIENT" + _RBD_FEATURES_ALL "RBD_FEATURES_ALL" + + _RBD_FLAG_OBJECT_MAP_INVALID "RBD_FLAG_OBJECT_MAP_INVALID" + _RBD_FLAG_FAST_DIFF_INVALID "RBD_FLAG_FAST_DIFF_INVALID" + + _RBD_IMAGE_OPTION_FORMAT "RBD_IMAGE_OPTION_FORMAT" + _RBD_IMAGE_OPTION_FEATURES "RBD_IMAGE_OPTION_FEATURES" + _RBD_IMAGE_OPTION_ORDER "RBD_IMAGE_OPTION_ORDER" + _RBD_IMAGE_OPTION_STRIPE_UNIT "RBD_IMAGE_OPTION_STRIPE_UNIT" + _RBD_IMAGE_OPTION_STRIPE_COUNT "RBD_IMAGE_OPTION_STRIPE_COUNT" + + RBD_MAX_BLOCK_NAME_SIZE + RBD_MAX_IMAGE_NAME_SIZE + + ctypedef void* rados_ioctx_t + ctypedef void* rbd_image_t + ctypedef void* rbd_image_options_t + + ctypedef struct rbd_image_info_t: + uint64_t size + uint64_t obj_size + uint64_t num_objs + int order + char block_name_prefix[RBD_MAX_BLOCK_NAME_SIZE] + uint64_t parent_pool + char parent_name[RBD_MAX_IMAGE_NAME_SIZE] + + ctypedef struct rbd_snap_info_t: + uint64_t id + uint64_t size + char *name + + void rbd_version(int *major, int *minor, int *extra) + + void rbd_image_options_create(rbd_image_options_t* opts) + void rbd_image_options_destroy(rbd_image_options_t opts) + int rbd_image_options_set_string(rbd_image_options_t opts, int optname, + const char* optval) + int rbd_image_options_set_uint64(rbd_image_options_t opts, int optname, + uint64_t optval) + int rbd_image_options_get_string(rbd_image_options_t opts, int optname, + char* optval, size_t maxlen) + int rbd_image_options_get_uint64(rbd_image_options_t opts, int optname, + uint64_t* optval) + int rbd_image_options_unset(rbd_image_options_t opts, int optname) + void rbd_image_options_clear(rbd_image_options_t opts) + int rbd_image_options_is_empty(rbd_image_options_t opts) + + int rbd_list(rados_ioctx_t io, char *names, size_t *size) + int rbd_create(rados_ioctx_t io, const char *name, uint64_t size, + int *order) + int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size, + rbd_image_options_t opts) + 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_rename(rados_ioctx_t src_io_ctx, const char *srcname, + const char *destname) + int rbd_open(rados_ioctx_t io, const char *name, + rbd_image_t *image, const char *snap_name) + int rbd_open_read_only(rados_ioctx_t io, const char *name, + rbd_image_t *image, const char *snap_name) + int rbd_close(rbd_image_t image) + int rbd_resize(rbd_image_t image, uint64_t size) + int rbd_stat(rbd_image_t image, rbd_image_info_t *info, size_t infosize) + int rbd_get_old_format(rbd_image_t image, uint8_t *old) + int rbd_get_size(rbd_image_t image, uint64_t *size) + int rbd_get_features(rbd_image_t image, uint64_t *features) + int rbd_update_features(rbd_image_t image, uint64_t features, + uint8_t enabled) + int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit) + int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count) + int rbd_get_overlap(rbd_image_t image, uint64_t *overlap) + int rbd_get_parent_info(rbd_image_t image, + char *parent_poolname, size_t ppoolnamelen, + char *parent_name, size_t pnamelen, + char *parent_snapname, size_t psnapnamelen) + int rbd_get_flags(rbd_image_t image, uint64_t *flags) + int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner) + ssize_t rbd_read2(rbd_image_t image, uint64_t ofs, size_t len, + char *buf, int op_flags) + ssize_t rbd_write2(rbd_image_t image, uint64_t ofs, size_t len, + const char *buf, int op_flags) + int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len) + int rbd_copy3(rbd_image_t src, rados_ioctx_t dest_io_ctx, + const char *destname, rbd_image_options_t dest_opts) + int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, + int *max_snaps) + void rbd_snap_list_end(rbd_snap_info_t *snaps) + int rbd_snap_create(rbd_image_t image, const char *snapname) + int rbd_snap_remove(rbd_image_t image, const char *snapname) + int rbd_snap_rollback(rbd_image_t image, const char *snapname) + int rbd_snap_rename(rbd_image_t image, const char *snapname, + const char* dstsnapsname) + int rbd_snap_protect(rbd_image_t image, const char *snap_name) + int rbd_snap_unprotect(rbd_image_t image, const char *snap_name) + int rbd_snap_is_protected(rbd_image_t image, const char *snap_name, + int *is_protected) + int rbd_snap_set(rbd_image_t image, const char *snapname) + int rbd_flatten(rbd_image_t image) + int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb, + void *cbdata) + ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len, + char *images, size_t *images_len) + ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive, + char *tag, size_t *tag_len, + char *clients, size_t *clients_len, + char *cookies, size_t *cookies_len, + char *addrs, size_t *addrs_len) + int rbd_lock_exclusive(rbd_image_t image, const char *cookie) + int rbd_lock_shared(rbd_image_t image, const char *cookie, + const char *tag) + int rbd_unlock(rbd_image_t image, const char *cookie) + int rbd_break_lock(rbd_image_t image, const char *client, + const char *cookie) + + # We use -9000 to propagate Python exceptions. We use except? to make sure + # things still work as intended if -9000 happens to be a valid errno value + # somewhere. + int rbd_diff_iterate2(rbd_image_t image, const char *fromsnapname, + uint64_t ofs, uint64_t len, + uint8_t include_parent, uint8_t whole_object, + int (*cb)(uint64_t, size_t, int, void *) + nogil except? -9000, + void *arg) except? -9000 + + int rbd_flush(rbd_image_t image) + int rbd_invalidate_cache(rbd_image_t image) + + +RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING +RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2 +RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK +RBD_FEATURE_OBJECT_MAP = _RBD_FEATURE_OBJECT_MAP +RBD_FEATURE_FAST_DIFF = _RBD_FEATURE_FAST_DIFF +RBD_FEATURE_DEEP_FLATTEN = _RBD_FEATURE_DEEP_FLATTEN +RBD_FEATURE_JOURNALING = _RBD_FEATURE_JOURNALING + +RBD_FEATURES_INCOMPATIBLE = _RBD_FEATURES_INCOMPATIBLE +RBD_FEATURES_RW_INCOMPATIBLE = _RBD_FEATURES_RW_INCOMPATIBLE +RBD_FEATURES_MUTABLE = _RBD_FEATURES_MUTABLE +RBD_FEATURES_SINGLE_CLIENT = _RBD_FEATURES_SINGLE_CLIENT +RBD_FEATURES_ALL = _RBD_FEATURES_ALL + +RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID + +RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT +RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES +RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER +RBD_IMAGE_OPTION_STRIPE_UNIT = _RBD_IMAGE_OPTION_STRIPE_UNIT +RBD_IMAGE_OPTION_STRIPE_COUNT = _RBD_IMAGE_OPTION_STRIPE_COUNT + + +class Error(Exception): + pass + + +class PermissionError(Error): + pass + + +class ImageNotFound(Error): + pass + + +class ImageExists(Error): + pass + + +class IOError(Error): + pass + + +class NoSpace(Error): + pass + + +class IncompleteWriteError(Error): + pass + + +class InvalidArgument(Error): + pass + + +class LogicError(Error): + pass + + +class ReadOnlyImage(Error): + pass + + +class ImageBusy(Error): + pass + + +class ImageHasSnapshots(Error): + pass + + +class FunctionNotSupported(Error): + pass + + +class ArgumentOutOfRange(Error): + pass + + +class ConnectionShutdown(Error): + pass + + +class Timeout(Error): + pass + + +cdef errno_to_exception = { + errno.EPERM : PermissionError, + errno.ENOENT : ImageNotFound, + errno.EIO : IOError, + errno.ENOSPC : NoSpace, + errno.EEXIST : ImageExists, + errno.EINVAL : InvalidArgument, + errno.EROFS : ReadOnlyImage, + errno.EBUSY : ImageBusy, + errno.ENOTEMPTY : ImageHasSnapshots, + errno.ENOSYS : FunctionNotSupported, + errno.EDOM : ArgumentOutOfRange, + errno.ESHUTDOWN : ConnectionShutdown, + errno.ETIMEDOUT : Timeout, +} + +cdef make_ex(ret, msg): + """ + Translate a librbd return code into an exception. + + :param ret: the return code + :type ret: int + :param msg: the error message to use + :type msg: str + :returns: a subclass of :class:`Error` + """ + ret = abs(ret) + if ret in errno_to_exception: + return errno_to_exception[ret](msg) + else: + return Error(msg + (": error code %d" % ret)) + +cdef rados_ioctx_t convert_ioctx(ioctx) except? NULL: + return ioctx.io.value + +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): + """ + Create a byte string from a Python string + + :param basestring val: Python string + :param str name: Name of the string parameter, for exceptions + :param str encoding: Encoding to use + :param bool opt: If True, None is allowed + :rtype: bytes + :raises: :class:`InvalidArgument` + """ + if opt and val is None: + return None + if isinstance(val, bytes): + return val + elif isinstance(val, unicode): + return val.encode(encoding) + else: + raise InvalidArgument('%s must be a string' % name) + +def decode_cstr(val, encoding="utf-8"): + """ + Decode a byte string into a Python string. + + :param bytes val: byte string + :rtype: unicode or None + """ + if val is None: + return None + + return val.decode(encoding) + + +cdef char* opt_str(s) except? NULL: + if s is None: + return NULL + return s + +cdef void* realloc_chk(void* ptr, size_t size) except NULL: + cdef void *ret = realloc(ptr, size) + if ret == NULL: + raise MemoryError("realloc failed") + return ret + +class RBD(object): + """ + This class wraps librbd CRUD functions. + """ + def version(self): + """ + Get the version number of the ``librbd`` C library. + + :returns: a tuple of ``(major, minor, extra)`` components of the + librbd version + """ + cdef int major = 0 + cdef int minor = 0 + cdef int extra = 0 + rbd_version(&major, &minor, &extra) + return (major, minor, extra) + + def create(self, ioctx, name, size, order=None, old_format=True, + features=0, stripe_unit=0, stripe_count=0): + """ + Create an rbd image. + + :param ioctx: the context in which to create the image + :type ioctx: :class:`rados.Ioctx` + :param name: what the image is called + :type name: str + :param size: how big the image is in bytes + :type size: int + :param order: the image is split into (2**order) byte objects + :type order: int + :param old_format: whether to create an old-style image that + is accessible by old clients, but can't + use more advanced features like layering. + :type old_format: bool + :param features: bitmask of features to enable + :type features: int + :param stripe_unit: stripe unit in bytes (default 0 for object size) + :type stripe_unit: int + :param stripe_count: objects to stripe over before looping + :type stripe_count: int + :raises: :class:`ImageExists` + :raises: :class:`TypeError` + :raises: :class:`InvalidArgument` + :raises: :class:`FunctionNotSupported` + """ + name = cstr(name, 'name') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_name = name + uint64_t _size = size + int _order = 0 + rbd_image_options_t opts + if order is not None: + _order = order + if old_format: + if features != 0 or stripe_unit != 0 or stripe_count != 0: + raise InvalidArgument('format 1 images do not support feature' + ' masks or non-default striping') + with nogil: + ret = rbd_create(_ioctx, _name, _size, &_order) + else: + rbd_image_options_create(&opts) + try: + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FORMAT, + 1 if old_format else 2) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, + features) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, + _order) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, + stripe_unit) + rbd_image_options_set_uint64(opts, + RBD_IMAGE_OPTION_STRIPE_COUNT, + stripe_count) + with nogil: + ret = rbd_create4(_ioctx, _name, _size, opts) + finally: + rbd_image_options_destroy(opts) + if ret < 0: + raise make_ex(ret, 'error creating image') + + def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name, + features=0, order=None, stripe_unit=0, stripe_count=0): + """ + Clone a parent rbd snapshot into a COW sparse child. + + :param p_ioctx: the parent context that represents the parent snap + :type ioctx: :class:`rados.Ioctx` + :param p_name: the parent image name + :type name: str + :param p_snapname: the parent image snapshot name + :type name: str + :param c_ioctx: the child context that represents the new clone + :type ioctx: :class:`rados.Ioctx` + :param c_name: the clone (child) name + :type name: str + :param features: bitmask of features to enable; if set, must include layering + :type features: int + :param order: the image is split into (2**order) byte objects + :type order: int + :param stripe_unit: stripe unit in bytes (default 0 for object size) + :type stripe_unit: int + :param stripe_count: objects to stripe over before looping + :type stripe_count: int + :raises: :class:`TypeError` + :raises: :class:`InvalidArgument` + :raises: :class:`ImageExists` + :raises: :class:`FunctionNotSupported` + :raises: :class:`ArgumentOutOfRange` + """ + p_snapname = cstr(p_snapname, 'p_snapname') + p_name = cstr(p_name, 'p_name') + c_name = cstr(c_name, 'c_name') + cdef: + rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx) + rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx) + char *_p_name = p_name + char *_p_snapname = p_snapname + char *_c_name = c_name + rbd_image_options_t opts + if order is None: + order = 0 + + rbd_image_options_create(&opts) + try: + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, + features) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, + order) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, + stripe_unit) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, + stripe_count) + with nogil: + ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname, + _c_ioctx, _c_name, opts) + finally: + rbd_image_options_destroy(opts) + if ret < 0: + raise make_ex(ret, 'error creating clone') + + def list(self, ioctx): + """ + List image names. + + :param ioctx: determines which RADOS pool is read + :type ioctx: :class:`rados.Ioctx` + :returns: list -- a list of image names + """ + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + size_t size = 512 + char *c_names = NULL + try: + while True: + c_names = realloc_chk(c_names, size) + with nogil: + ret = rbd_list(_ioctx, c_names, &size) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing images') + return [decode_cstr(name) for name in c_names[:ret].split('\0') + if name] + finally: + free(c_names) + + def remove(self, ioctx, name): + """ + Delete an RBD image. This may take a long time, since it does + not return until every object that comprises the image has + been deleted. Note that all snapshots must be deleted before + the image can be removed. If there are snapshots left, + :class:`ImageHasSnapshots` is raised. If the image is still + open, or the watch from a crashed client has not expired, + :class:`ImageBusy` is raised. + + :param ioctx: determines which RADOS pool the image is in + :type ioctx: :class:`rados.Ioctx` + :param name: the name of the image to remove + :type name: str + :raises: :class:`ImageNotFound`, :class:`ImageBusy`, + :class:`ImageHasSnapshots` + """ + name = cstr(name, 'name') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_name = name + with nogil: + ret = rbd_remove(_ioctx, _name) + if ret != 0: + raise make_ex(ret, 'error removing image') + + def rename(self, ioctx, src, dest): + """ + Rename an RBD image. + + :param ioctx: determines which RADOS pool the image is in + :type ioctx: :class:`rados.Ioctx` + :param src: the current name of the image + :type src: str + :param dest: the new name of the image + :type dest: str + :raises: :class:`ImageNotFound`, :class:`ImageExists` + """ + src = cstr(src, 'src') + dest = cstr(dest, 'dest') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_src = src + char *_dest = dest + with nogil: + ret = rbd_rename(_ioctx, _src, _dest) + if ret != 0: + raise make_ex(ret, 'error renaming image') + + +cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \ + except? -9000 with gil: + # Make sure that if we wound up with an exception from a previous callback, + # we stop calling back (just in case librbd ever fails to bail out on the + # first negative return, as older versions did) + if exc.PyErr_Occurred(): + return -9000 + ret = (cb)(offset, length, bool(write)) + if ret is None: + return 0 + return ret + + +cdef class Image(object): + """ + This class represents an RBD image. It is used to perform I/O on + the image and interact with snapshots. + + **Note**: Any method of this class may raise :class:`ImageNotFound` + if the image has been deleted. + """ + cdef rbd_image_t image + cdef bint closed + cdef object name + cdef object ioctx + cdef rados_ioctx_t _ioctx + + def __init__(self, ioctx, name, snapshot=None, read_only=False): + """ + Open the image at the given snapshot. + If a snapshot is specified, the image will be read-only, unless + :func:`Image.set_snap` is called later. + + If read-only mode is used, metadata for the :class:`Image` + object (such as which snapshots exist) may become obsolete. See + the C api for more details. + + To clean up from opening the image, :func:`Image.close` should + be called. For ease of use, this is done automatically when + an :class:`Image` is used as a context manager (see :pep:`343`). + + :param ioctx: determines which RADOS pool the image is in + :type ioctx: :class:`rados.Ioctx` + :param name: the name of the image + :type name: str + :param snapshot: which snapshot to read from + :type snaphshot: str + :param read_only: whether to open the image in read-only mode + :type read_only: bool + """ + name = cstr(name, 'name') + snapshot = cstr(snapshot, 'snapshot', opt=True) + self.closed = True + self.name = name + # Keep around a reference to the ioctx, so it won't get deleted + self.ioctx = ioctx + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_name = name + char *_snapshot = opt_str(snapshot) + if read_only: + with nogil: + ret = rbd_open_read_only(_ioctx, _name, &self.image, _snapshot) + else: + with nogil: + ret = rbd_open(_ioctx, _name, &self.image, _snapshot) + if ret != 0: + raise make_ex(ret, 'error opening image %s at snapshot %s' % (name, snapshot)) + self.closed = False + + def __enter__(self): + return self + + def __exit__(self, type_, value, traceback): + """ + Closes the image. See :func:`close` + """ + self.close() + return False + + def close(self): + """ + Release the resources used by this image object. + + After this is called, this object should not be used. + """ + if not self.closed: + with nogil: + ret = rbd_close(self.image) + if ret < 0: + raise make_ex(ret, 'error while closing image %s' % ( + self.name,)) + self.closed = True + + def __del__(self): + self.close() + + def __repr__(self): + return "rbd.Image(ioctx, %r)" % self.name + + def resize(self, size): + """ + Change the size of the image. + + :param size: the new size of the image + :type size: int + """ + cdef uint64_t _size = size + with nogil: + ret = rbd_resize(self.image, _size) + if ret < 0: + raise make_ex(ret, 'error resizing image %s' % (self.name,)) + + def stat(self): + """ + Get information about the image. Currently parent pool and + parent name are always -1 and ''. + + :returns: dict - contains the following keys: + + * ``size`` (int) - the size of the image in bytes + + * ``obj_size`` (int) - the size of each object that comprises the + image + + * ``num_objs`` (int) - the number of objects in the image + + * ``order`` (int) - log_2(object_size) + + * ``block_name_prefix`` (str) - the prefix of the RADOS objects used + to store the image + + * ``parent_pool`` (int) - deprecated + + * ``parent_name`` (str) - deprecated + + See also :meth:`format` and :meth:`features`. + + """ + cdef rbd_image_info_t info + with nogil: + ret = rbd_stat(self.image, &info, sizeof(info)) + if ret != 0: + raise make_ex(ret, 'error getting info for image %s' % (self.name,)) + return { + 'size' : info.size, + 'obj_size' : info.obj_size, + 'num_objs' : info.num_objs, + 'order' : info.order, + 'block_name_prefix' : decode_cstr(info.block_name_prefix), + 'parent_pool' : info.parent_pool, + 'parent_name' : info.parent_name + } + + def parent_info(self): + """ + Get information about a cloned image's parent (if any) + + :returns: tuple - ``(pool name, image name, snapshot name)`` components + of the parent image + :raises: :class:`ImageNotFound` if the image doesn't have a parent + """ + cdef: + int ret = -errno.ERANGE + size_t size = 8 + char *pool = NULL + char *name = NULL + char *snapname = NULL + try: + while ret == -errno.ERANGE and size <= 4096: + pool = realloc_chk(pool, size) + name = realloc_chk(name, size) + snapname = realloc_chk(snapname, size) + with nogil: + ret = rbd_get_parent_info(self.image, pool, size, name, + size, snapname, size) + if ret == -errno.ERANGE: + size *= 2 + + if ret != 0: + raise make_ex(ret, 'error getting parent info for image %s' % (self.name,)) + return (decode_cstr(pool), decode_cstr(name), decode_cstr(snapname)) + finally: + free(pool) + free(name) + free(snapname) + + def old_format(self): + """ + Find out whether the image uses the old RBD format. + + :returns: bool - whether the image uses the old RBD format + """ + cdef uint8_t old + with nogil: + ret = rbd_get_old_format(self.image, &old) + if ret != 0: + raise make_ex(ret, 'error getting old_format for image' % (self.name)) + return old != 0 + + def size(self): + """ + Get the size of the image. If open to a snapshot, returns the + size of that snapshot. + + :returns: the size of the image in bytes + """ + cdef uint64_t image_size + with nogil: + ret = rbd_get_size(self.image, &image_size) + if ret != 0: + raise make_ex(ret, 'error getting size for image' % (self.name)) + return image_size + + def features(self): + """ + Gets the features bitmask of the image. + + :returns: int - the features bitmask of the image + """ + cdef uint64_t features + with nogil: + ret = rbd_get_features(self.image, &features) + if ret != 0: + raise make_ex(ret, 'error getting features for image' % (self.name)) + return features + + def update_features(self, features, enabled): + """ + Updates the features bitmask of the image by enabling/disabling + a single feature. The feature must support the ability to be + dynamically enabled/disabled. + + :param features: feature bitmask to enable/disable + :type features: int + :param enabled: whether to enable/disable the feature + :type enabled: bool + :raises: :class:`InvalidArgument` + """ + cdef: + uint64_t _features = features + uint8_t _enabled = bool(enabled) + with nogil: + ret = rbd_update_features(self.image, _features, _enabled) + if ret != 0: + raise make_ex(ret, 'error updating features for image %s' % + (self.name)) + + def overlap(self): + """ + Gets the number of overlapping bytes between the image and its parent + image. If open to a snapshot, returns the overlap between the snapshot + and the parent image. + + :returns: int - the overlap in bytes + :raises: :class:`ImageNotFound` if the image doesn't have a parent + """ + cdef uint64_t overlap + with nogil: + ret = rbd_get_overlap(self.image, &overlap) + if ret != 0: + raise make_ex(ret, 'error getting overlap for image' % (self.name)) + return overlap + + def flags(self): + """ + Gets the flags bitmask of the image. + + :returns: int - the flags bitmask of the image + """ + cdef uint64_t flags + with nogil: + ret = rbd_get_flags(self.image, &flags) + if ret != 0: + raise make_ex(ret, 'error getting flags for image' % (self.name)) + return flags + + def is_exclusive_lock_owner(self): + """ + Gets the status of the image exclusive lock. + + :returns: bool - true if the image is exclusively locked + """ + cdef int owner + with nogil: + ret = rbd_is_exclusive_lock_owner(self.image, &owner) + if ret != 0: + raise make_ex(ret, 'error getting lock status for image' % (self.name)) + return owner == 1 + + def copy(self, dest_ioctx, dest_name, features=0, order=None, stripe_unit=0, + stripe_count=0): + """ + Copy the image to another location. + + :param dest_ioctx: determines which pool to copy into + :type dest_ioctx: :class:`rados.Ioctx` + :param dest_name: the name of the copy + :type dest_name: str + :param features: bitmask of features to enable; if set, must include layering + :type features: int + :param order: the image is split into (2**order) byte objects + :type order: int + :param stripe_unit: stripe unit in bytes (default 0 for object size) + :type stripe_unit: int + :param stripe_count: objects to stripe over before looping + :type stripe_count: int + :raises: :class:`TypeError` + :raises: :class:`InvalidArgument` + :raises: :class:`ImageExists` + :raises: :class:`FunctionNotSupported` + :raises: :class:`ArgumentOutOfRange` + """ + if order is None: + order = 0 + dest_name = cstr(dest_name, 'dest_name') + cdef: + rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx) + char *_dest_name = dest_name + rbd_image_options_t opts + + rbd_image_options_create(&opts) + try: + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, + features) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, + order) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, + stripe_unit) + rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, + stripe_count) + with nogil: + ret = rbd_copy3(self.image, _dest_ioctx, _dest_name, opts) + finally: + rbd_image_options_destroy(opts) + if ret < 0: + raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name)) + + def list_snaps(self): + """ + Iterate over the snapshots of an image. + + :returns: :class:`SnapIterator` + """ + return SnapIterator(self) + + def create_snap(self, name): + """ + Create a snapshot of the image. + + :param name: the name of the snapshot + :type name: str + :raises: :class:`ImageExists` + """ + name = cstr(name, 'name') + cdef char *_name = name + with nogil: + ret = rbd_snap_create(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error creating snapshot %s from %s' % (name, self.name)) + + def rename_snap(self, srcname, dstname): + """ + rename a snapshot of the image. + + :param srcname: the src name of the snapshot + :type srcname: str + :param dstname: the dst name of the snapshot + :type dstname: str + :raises: :class:`ImageExists` + """ + srcname = cstr(srcname, 'srcname') + dstname = cstr(dstname, 'dstname') + cdef: + char *_srcname = srcname + char *_dstname = dstname + with nogil: + ret = rbd_snap_rename(self.image, _srcname, _dstname) + if ret != 0: + raise make_ex(ret, 'error renaming snapshot of %s from %s to %s' % (self.name, srcname, dstname)) + + def remove_snap(self, name): + """ + Delete a snapshot of the image. + + :param name: the name of the snapshot + :type name: str + :raises: :class:`IOError`, :class:`ImageBusy` + """ + name = cstr(name, 'name') + cdef char *_name = name + with nogil: + ret = rbd_snap_remove(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error removing snapshot %s from %s' % (name, self.name)) + + def rollback_to_snap(self, name): + """ + Revert the image to its contents at a snapshot. This is a + potentially expensive operation, since it rolls back each + object individually. + + :param name: the snapshot to rollback to + :type name: str + :raises: :class:`IOError` + """ + name = cstr(name, 'name') + cdef char *_name = name + with nogil: + ret = rbd_snap_rollback(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error rolling back image %s to snapshot %s' % (self.name, name)) + + def protect_snap(self, name): + """ + Mark a snapshot as protected. This means it can't be deleted + until it is unprotected. + + :param name: the snapshot to protect + :type name: str + :raises: :class:`IOError`, :class:`ImageNotFound` + """ + name = cstr(name, 'name') + cdef char *_name = name + with nogil: + ret = rbd_snap_protect(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error protecting snapshot %s@%s' % (self.name, name)) + + def unprotect_snap(self, name): + """ + Mark a snapshot unprotected. This allows it to be deleted if + it was protected. + + :param name: the snapshot to unprotect + :type name: str + :raises: :class:`IOError`, :class:`ImageNotFound` + """ + name = cstr(name, 'name') + cdef char *_name = name + with nogil: + ret = rbd_snap_unprotect(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error unprotecting snapshot %s@%s' % (self.name, name)) + + def is_protected_snap(self, name): + """ + Find out whether a snapshot is protected from deletion. + + :param name: the snapshot to check + :type name: str + :returns: bool - whether the snapshot is protected + :raises: :class:`IOError`, :class:`ImageNotFound` + """ + name = cstr(name, 'name') + cdef: + char *_name = name + int is_protected + with nogil: + ret = rbd_snap_is_protected(self.image, _name, &is_protected) + if ret != 0: + raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name)) + return is_protected == 1 + + def set_snap(self, name): + """ + Set the snapshot to read from. Writes will raise ReadOnlyImage + while a snapshot is set. Pass None to unset the snapshot + (reads come from the current image) , and allow writing again. + + :param name: the snapshot to read from, or None to unset the snapshot + :type name: str or None + """ + name = cstr(name, 'name', opt=True) + cdef char *_name = opt_str(name) + with nogil: + ret = rbd_snap_set(self.image, _name) + if ret != 0: + raise make_ex(ret, 'error setting image %s to snapshot %s' % (self.name, name)) + + def read(self, offset, length, fadvise_flags=0): + """ + Read data from the image. Raises :class:`InvalidArgument` if + part of the range specified is outside the image. + + :param offset: the offset to start reading at + :type offset: int + :param length: how many bytes to read + :type length: int + :param fadvise_flags: fadvise flags for this read + :type fadvise_flags: int + :returns: str - the data read + :raises: :class:`InvalidArgument`, :class:`IOError` + """ + + # This usage of the Python API allows us to construct a string + # that librbd directly reads into, avoiding an extra copy. Although + # strings are normally immutable, this usage is explicitly supported + # for freshly created string objects. + cdef: + char *ret_buf + uint64_t _offset = offset + size_t _length = length + int _fadvise_flags = fadvise_flags + PyObject* ret_s = NULL + ret_s = PyBytes_FromStringAndSize(NULL, length) + try: + ret_buf = PyBytes_AsString(ret_s) + with nogil: + ret = rbd_read2(self.image, _offset, _length, ret_buf, + _fadvise_flags) + if ret < 0: + raise make_ex(ret, 'error reading %s %ld~%ld' % (self.name, offset, length)) + + if ret != length: + _PyBytes_Resize(&ret_s, ret) + + return ret_s + finally: + # We DECREF unconditionally: the cast to object above will have + # INCREFed if necessary. This also takes care of exceptions, + # including if _PyString_Resize fails (that will free the string + # itself and set ret_s to NULL, hence XDECREF). + ref.Py_XDECREF(ret_s) + + def diff_iterate(self, offset, length, from_snapshot, iterate_cb, + include_parent = True, whole_object = False): + """ + 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. + + iterate_cb may raise an exception, which will abort the diff and will be + propagated to the caller. + + 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 + :param include_parent: True if full history diff should include parent + :type include_parent: bool + :param whole_object: True if diff extents should cover whole object + :type whole_object: bool + :raises: :class:`InvalidArgument`, :class:`IOError`, + :class:`ImageNotFound` + """ + from_snapshot = cstr(from_snapshot, 'from_snapshot', opt=True) + cdef: + char *_from_snapshot = opt_str(from_snapshot) + uint64_t _offset = offset, _length = length + uint8_t _include_parent = include_parent + uint8_t _whole_object = whole_object + with nogil: + ret = rbd_diff_iterate2(self.image, _from_snapshot, _offset, + _length, _include_parent, _whole_object, + &diff_iterate_cb, iterate_cb) + if ret < 0: + msg = 'error generating diff from snapshot %s' % from_snapshot + raise make_ex(ret, msg) + + def write(self, data, offset, fadvise_flags=0): + """ + Write data to the image. Raises :class:`InvalidArgument` if + part of the write would fall outside the image. + + :param data: the data to be written + :type data: bytes + :param offset: where to start writing data + :type offset: int + :param fadvise_flags: fadvise flags for this write + :type fadvise_flags: int + :returns: int - the number of bytes written + :raises: :class:`IncompleteWriteError`, :class:`LogicError`, + :class:`InvalidArgument`, :class:`IOError` + """ + if not isinstance(data, bytes): + raise TypeError('data must be a byte string') + cdef: + uint64_t _offset = offset, length = len(data) + char *_data = data + int _fadvise_flags = fadvise_flags + with nogil: + ret = rbd_write2(self.image, _offset, length, _data, _fadvise_flags) + + if ret == length: + return ret + elif ret < 0: + raise make_ex(ret, "error writing to %s" % (self.name,)) + elif ret < length: + raise IncompleteWriteError("Wrote only %ld out of %ld bytes" % (ret, length)) + else: + raise LogicError("logic error: rbd_write(%s) \ +returned %d, but %d was the maximum number of bytes it could have \ +written." % (self.name, ret, length)) + + def discard(self, offset, length): + """ + Trim the range from the image. It will be logically filled + with zeroes. + """ + cdef uint64_t _offset = offset, _length = length + with nogil: + ret = rbd_discard(self.image, _offset, _length) + if ret < 0: + msg = 'error discarding region %d~%d' % (offset, length) + raise make_ex(ret, msg) + + def flush(self): + """ + Block until all writes are fully flushed if caching is enabled. + """ + with nogil: + ret = rbd_flush(self.image) + if ret < 0: + raise make_ex(ret, 'error flushing image') + + def invalidate_cache(self): + """ + Drop any cached data for the image. + """ + with nogil: + ret = rbd_invalidate_cache(self.image) + if ret < 0: + raise make_ex(ret, 'error invalidating cache') + + def stripe_unit(self): + """ + Returns the stripe unit used for the image. + """ + cdef uint64_t stripe_unit + with nogil: + ret = rbd_get_stripe_unit(self.image, &stripe_unit) + if ret != 0: + raise make_ex(ret, 'error getting stripe unit for image' % (self.name)) + return stripe_unit + + def stripe_count(self): + """ + Returns the stripe count used for the image. + """ + cdef uint64_t stripe_count + with nogil: + ret = rbd_get_stripe_count(self.image, &stripe_count) + if ret != 0: + raise make_ex(ret, 'error getting stripe count for image' % (self.name)) + return stripe_count + + def flatten(self): + """ + Flatten clone image (copy all blocks from parent to child) + """ + with nogil: + ret = rbd_flatten(self.image) + if ret < 0: + raise make_ex(ret, "error flattening %s" % self.name) + + def rebuild_object_map(self): + """ + Rebuilds the object map for the image HEAD or currently set snapshot + """ + cdef librbd_progress_fn_t prog_cb = &no_op_progress_callback + with nogil: + ret = rbd_rebuild_object_map(self.image, prog_cb, NULL) + if ret < 0: + raise make_ex(ret, "error rebuilding object map %s" % self.name) + + def list_children(self): + """ + List children of the currently set snapshot (set via set_snap()). + + :returns: list - a list of (pool name, image name) tuples + """ + cdef: + size_t pools_size = 512, images_size = 512 + char *c_pools = NULL + char *c_images = NULL + try: + while True: + c_pools = realloc_chk(c_pools, pools_size) + c_images = realloc_chk(c_images, images_size) + with nogil: + ret = rbd_list_children(self.image, c_pools, &pools_size, + c_images, &images_size) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing images') + if ret == 0: + return [] + pools = map(decode_cstr, c_pools[:pools_size - 1].split('\0')) + images = map(decode_cstr, c_images[:images_size - 1].split('\0')) + return list(zip(pools, images)) + finally: + free(c_pools) + free(c_images) + + def list_lockers(self): + """ + List clients that have locked the image and information + about the lock. + + :returns: dict - contains the following keys: + + * ``tag`` - the tag associated with the lock (every + additional locker must use the same tag) + * ``exclusive`` - boolean indicating whether the + lock is exclusive or shared + * ``lockers`` - a list of (client, cookie, address) + tuples + """ + cdef: + size_t clients_size = 512, cookies_size = 512 + size_t addrs_size = 512, tag_size = 512 + int exclusive = 0 + char *c_clients = NULL + char *c_cookies = NULL + char *c_addrs = NULL + char *c_tag = NULL + + try: + while True: + c_clients = realloc_chk(c_clients, clients_size) + c_cookies = realloc_chk(c_cookies, cookies_size) + c_addrs = realloc_chk(c_addrs, addrs_size) + c_tag = realloc_chk(c_tag, tag_size) + with nogil: + ret = rbd_list_lockers(self.image, &exclusive, + c_tag, &tag_size, + c_clients, &clients_size, + c_cookies, &cookies_size, + c_addrs, &addrs_size) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing images') + if ret == 0: + return [] + clients = map(decode_cstr, c_clients[:clients_size - 1].split('\0')) + cookies = map(decode_cstr, c_cookies[:cookies_size - 1].split('\0')) + addrs = map(decode_cstr, c_addrs[:addrs_size - 1].split('\0')) + return { + 'tag' : decode_cstr(c_tag), + 'exclusive' : exclusive == 1, + 'lockers' : list(zip(clients, cookies, addrs)), + } + finally: + free(c_clients) + free(c_cookies) + free(c_addrs) + free(c_tag) + + def lock_exclusive(self, cookie): + """ + Take an exclusive lock on the image. + + :raises: :class:`ImageBusy` if a different client or cookie locked it + :class:`ImageExists` if the same client and cookie locked it + """ + cookie = cstr(cookie, 'cookie') + cdef char *_cookie = cookie + with nogil: + ret = rbd_lock_exclusive(self.image, _cookie) + if ret < 0: + raise make_ex(ret, 'error acquiring exclusive lock on image') + + def lock_shared(self, cookie, tag): + """ + Take a shared lock on the image. The tag must match + that of the existing lockers, if any. + + :raises: :class:`ImageBusy` if a different client or cookie locked it + :class:`ImageExists` if the same client and cookie locked it + """ + cookie = cstr(cookie, 'cookie') + tag = cstr(tag, 'tag') + cdef: + char *_cookie = cookie + char *_tag = tag + with nogil: + ret = rbd_lock_shared(self.image, _cookie, _tag) + if ret < 0: + raise make_ex(ret, 'error acquiring shared lock on image') + + def unlock(self, cookie): + """ + Release a lock on the image that was locked by this rados client. + """ + cookie = cstr(cookie, 'cookie') + cdef char *_cookie = cookie + with nogil: + ret = rbd_unlock(self.image, _cookie) + if ret < 0: + raise make_ex(ret, 'error unlocking image') + + def break_lock(self, client, cookie): + """ + Release a lock held by another rados client. + """ + client = cstr(client, 'client') + cookie = cstr(cookie, 'cookie') + cdef: + char *_client = client + char *_cookie = cookie + with nogil: + ret = rbd_break_lock(self.image, _client, _cookie) + if ret < 0: + raise make_ex(ret, 'error unlocking image') + + +cdef class SnapIterator(object): + """ + Iterator over snapshot info for an image. + + Yields a dictionary containing information about a snapshot. + + Keys are: + + * ``id`` (int) - numeric identifier of the snapshot + + * ``size`` (int) - size of the image at the time of snapshot (in bytes) + + * ``name`` (str) - name of the snapshot + """ + + cdef rbd_snap_info_t *snaps + cdef int num_snaps + + def __init__(self, Image image): + self.snaps = NULL + self.num_snaps = 10 + while True: + self.snaps = realloc_chk(self.snaps, + self.num_snaps * + sizeof(rbd_snap_info_t)) + with nogil: + ret = rbd_snap_list(image.image, self.snaps, &self.num_snaps) + if ret >= 0: + self.num_snaps = ret + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error listing snapshots for image %s' % (image.name,)) + + def __iter__(self): + for i in range(self.num_snaps): + yield { + 'id' : self.snaps[i].id, + 'size' : self.snaps[i].size, + 'name' : decode_cstr(self.snaps[i].name), + } + + def __del__(self): + if self.snaps: + rbd_snap_list_end(self.snaps) + free(self.snaps) diff --git a/src/pybind/rbd/setup.py b/src/pybind/rbd/setup.py new file mode 100755 index 00000000000..1eda454c392 --- /dev/null +++ b/src/pybind/rbd/setup.py @@ -0,0 +1,51 @@ +# Largely taken from +# https://blog.kevin-brown.com/programming/2014/09/24/combining-autotools-and-setuptools.html +import os, sys, os.path + +from setuptools.command.egg_info import egg_info +from distutils.core import setup +from distutils.extension import Extension +from Cython.Build import cythonize + +def get_version(): + try: + for line in open(os.path.join(os.path.dirname(__file__), "..", "ceph_ver.h")): + if "CEPH_GIT_NICE_VER" in line: + return line.split()[2][1:-1] + else: + return "0" + except IOError: + return "0" + +class EggInfoCommand(egg_info): + def finalize_options(self): + egg_info.finalize_options(self) + if "build" in self.distribution.command_obj: + build_command = self.distribution.command_obj["build"] + self.egg_base = build_command.build_base + self.egg_info = os.path.join(self.egg_base, os.path.basename(self.egg_info)) + +# Disable cythonification if we're not really building anything +if (len(sys.argv) >= 2 and + any(i in sys.argv[1:] for i in ('--help', 'clean', 'egg_info', '--version') + )): + def cythonize(x, **kwargs): + return x + +setup( + name = 'rbd', + version = get_version(), + description = "Python libraries for the Ceph librbd library", + long_description = ( + "This package contains Python libraries for interacting with Ceph's " + "RBD block device library."), + ext_modules = cythonize([ + Extension("rbd", + ["rbd.pyx"], + libraries=["rbd"] + ) + ], build_dir=os.environ.get("CYTHON_BUILD_DIR", None)), + cmdclass={ + "egg_info": EggInfoCommand, + }, +) diff --git a/src/pybind/setup.py b/src/pybind/setup.py deleted file mode 100755 index 1eda454c392..00000000000 --- a/src/pybind/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -# Largely taken from -# https://blog.kevin-brown.com/programming/2014/09/24/combining-autotools-and-setuptools.html -import os, sys, os.path - -from setuptools.command.egg_info import egg_info -from distutils.core import setup -from distutils.extension import Extension -from Cython.Build import cythonize - -def get_version(): - try: - for line in open(os.path.join(os.path.dirname(__file__), "..", "ceph_ver.h")): - if "CEPH_GIT_NICE_VER" in line: - return line.split()[2][1:-1] - else: - return "0" - except IOError: - return "0" - -class EggInfoCommand(egg_info): - def finalize_options(self): - egg_info.finalize_options(self) - if "build" in self.distribution.command_obj: - build_command = self.distribution.command_obj["build"] - self.egg_base = build_command.build_base - self.egg_info = os.path.join(self.egg_base, os.path.basename(self.egg_info)) - -# Disable cythonification if we're not really building anything -if (len(sys.argv) >= 2 and - any(i in sys.argv[1:] for i in ('--help', 'clean', 'egg_info', '--version') - )): - def cythonize(x, **kwargs): - return x - -setup( - name = 'rbd', - version = get_version(), - description = "Python libraries for the Ceph librbd library", - long_description = ( - "This package contains Python libraries for interacting with Ceph's " - "RBD block device library."), - ext_modules = cythonize([ - Extension("rbd", - ["rbd.pyx"], - libraries=["rbd"] - ) - ], build_dir=os.environ.get("CYTHON_BUILD_DIR", None)), - cmdclass={ - "egg_info": EggInfoCommand, - }, -)