X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fpybind%2Frbd%2Frbd.pyx;fp=src%2Fceph%2Fsrc%2Fpybind%2Frbd%2Frbd.pyx;h=0000000000000000000000000000000000000000;hb=7da45d65be36d36b880cc55c5036e96c24b53f00;hp=1d6376b7faf1d7d255a40839a75fc92ef9570348;hpb=691462d09d0987b47e112d6ee8740375df3c51b2;p=stor4nfv.git diff --git a/src/ceph/src/pybind/rbd/rbd.pyx b/src/ceph/src/pybind/rbd/rbd.pyx deleted file mode 100644 index 1d6376b..0000000 --- a/src/ceph/src/pybind/rbd/rbd.pyx +++ /dev/null @@ -1,2959 +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 - -import cython -import sys - -from cpython cimport PyObject, ref, exc -from libc cimport errno -from libc.stdint cimport * -from libc.stdlib cimport realloc, free -from libc.string cimport strdup - -from collections import Iterable -from datetime import datetime - -cimport rados - - -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 - -cdef extern from "time.h": - ctypedef long int time_t - cdef struct timespec: - time_t tv_sec - long tv_nsec - -cdef extern from "limits.h": - cdef uint64_t INT64_MAX - -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_FEATURE_DATA_POOL "RBD_FEATURE_DATA_POOL" - - _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_IMAGE_OPTION_DATA_POOL "RBD_IMAGE_OPTION_DATA_POOL" - - 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 void *rbd_completion_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 - - ctypedef enum rbd_mirror_mode_t: - _RBD_MIRROR_MODE_DISABLED "RBD_MIRROR_MODE_DISABLED" - _RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE" - _RBD_MIRROR_MODE_POOL "RBD_MIRROR_MODE_POOL" - - ctypedef struct rbd_mirror_peer_t: - char *uuid - char *cluster_name - char *client_name - - ctypedef enum rbd_mirror_image_state_t: - _RBD_MIRROR_IMAGE_DISABLING "RBD_MIRROR_IMAGE_DISABLING" - _RBD_MIRROR_IMAGE_ENABLED "RBD_MIRROR_IMAGE_ENABLED" - _RBD_MIRROR_IMAGE_DISABLED "RBD_MIRROR_IMAGE_DISABLED" - - ctypedef struct rbd_mirror_image_info_t: - char *global_id - rbd_mirror_image_state_t state - bint primary - - ctypedef enum rbd_mirror_image_status_state_t: - _MIRROR_IMAGE_STATUS_STATE_UNKNOWN "MIRROR_IMAGE_STATUS_STATE_UNKNOWN" - _MIRROR_IMAGE_STATUS_STATE_ERROR "MIRROR_IMAGE_STATUS_STATE_ERROR" - _MIRROR_IMAGE_STATUS_STATE_SYNCING "MIRROR_IMAGE_STATUS_STATE_SYNCING" - _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY" - _MIRROR_IMAGE_STATUS_STATE_REPLAYING "MIRROR_IMAGE_STATUS_STATE_REPLAYING" - _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY" - _MIRROR_IMAGE_STATUS_STATE_STOPPED "MIRROR_IMAGE_STATUS_STATE_STOPPED" - - ctypedef struct rbd_mirror_image_status_t: - char *name - rbd_mirror_image_info_t info - rbd_mirror_image_status_state_t state - char *description - time_t last_update - bint up - - ctypedef enum rbd_lock_mode_t: - _RBD_LOCK_MODE_EXCLUSIVE "RBD_LOCK_MODE_EXCLUSIVE" - _RBD_LOCK_MODE_SHARED "RBD_LOCK_MODE_SHARED" - - ctypedef enum rbd_trash_image_source_t: - _RBD_TRASH_IMAGE_SOURCE_USER "RBD_TRASH_IMAGE_SOURCE_USER", - _RBD_TRASH_IMAGE_SOURCE_MIRRORING "RBD_TRASH_IMAGE_SOURCE_MIRRORING" - - ctypedef struct rbd_trash_image_info_t: - char *id - char *name - rbd_trash_image_source_t source - time_t deletion_time - time_t deferment_end_time - - ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg) - ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr) - - void rbd_version(int *major, int *minor, int *extra) - - 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_trash_move(rados_ioctx_t io, const char *name, uint64_t delay) - int rbd_trash_get(rados_ioctx_t io, const char *id, - rbd_trash_image_info_t *info) - void rbd_trash_get_cleanup(rbd_trash_image_info_t *info) - int rbd_trash_list(rados_ioctx_t io, rbd_trash_image_info_t *trash_entries, - size_t *num_entries) - void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries, - size_t num_entries) - int rbd_trash_remove(rados_ioctx_t io, const char *id, int force) - int rbd_trash_restore(rados_ioctx_t io, const char *id, const char *name) - - int rbd_mirror_mode_get(rados_ioctx_t io, rbd_mirror_mode_t *mirror_mode) - int rbd_mirror_mode_set(rados_ioctx_t io, rbd_mirror_mode_t mirror_mode) - int rbd_mirror_peer_add(rados_ioctx_t io, char *uuid, - size_t uuid_max_length, const char *cluster_name, - const char *client_name) - int rbd_mirror_peer_remove(rados_ioctx_t io, const char *uuid) - int rbd_mirror_peer_list(rados_ioctx_t io_ctx, rbd_mirror_peer_t *peers, - int *max_peers) - void rbd_mirror_peer_list_cleanup(rbd_mirror_peer_t *peers, int max_peers) - int rbd_mirror_peer_set_client(rados_ioctx_t io, const char *uuid, - const char *client_name) - int rbd_mirror_peer_set_cluster(rados_ioctx_t io_ctx, const char *uuid, - const char *cluster_name) - int rbd_mirror_image_status_list(rados_ioctx_t io, const char *start_id, - size_t max, char **image_ids, - rbd_mirror_image_status_t *images, - size_t *len) - void rbd_mirror_image_status_list_cleanup(char **image_ids, - rbd_mirror_image_status_t *images, - size_t len) - int rbd_mirror_image_status_summary(rados_ioctx_t io, - rbd_mirror_image_status_state_t *states, - int *counts, size_t *maxlen) - - 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_create_timestamp(rbd_image_t image, timespec *timestamp) - int rbd_get_overlap(rbd_image_t image, uint64_t *overlap) - int rbd_get_id(rbd_image_t image, char *id, size_t id_len) - int rbd_get_block_name_prefix(rbd_image_t image, char *prefix, - size_t prefix_len) - int rbd_get_parent_info2(rbd_image_t image, - char *parent_poolname, size_t ppoolnamelen, - char *parent_name, size_t pnamelen, - char *parent_id, size_t pidlen, - char *parent_snapname, size_t psnapnamelen) - int rbd_get_flags(rbd_image_t image, uint64_t *flags) - 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_remove2(rbd_image_t image, const char *snapname, uint32_t flags, - librbd_progress_fn_t cb, void *cbdata) - 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_get_limit(rbd_image_t image, uint64_t *limit) - int rbd_snap_set_limit(rbd_image_t image, uint64_t limit) - int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, timespec *timestamp) - 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) - - int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner) - int rbd_lock_acquire(rbd_image_t image, rbd_lock_mode_t lock_mode) - int rbd_lock_release(rbd_image_t image) - int rbd_lock_get_owners(rbd_image_t image, rbd_lock_mode_t *lock_mode, - char **lock_owners, size_t *max_lock_owners) - void rbd_lock_get_owners_cleanup(char **lock_owners, - size_t lock_owner_count) - int rbd_lock_break(rbd_image_t image, rbd_lock_mode_t lock_mode, - char *lock_owner) - - # 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) - - int rbd_mirror_image_enable(rbd_image_t image) - int rbd_mirror_image_disable(rbd_image_t image, bint force) - int rbd_mirror_image_promote(rbd_image_t image, bint force) - int rbd_mirror_image_demote(rbd_image_t image) - int rbd_mirror_image_resync(rbd_image_t image) - int rbd_mirror_image_get_info(rbd_image_t image, - rbd_mirror_image_info_t *mirror_image_info, - size_t info_size) - int rbd_mirror_image_get_status(rbd_image_t image, - rbd_mirror_image_status_t *mirror_image_status, - size_t status_size) - - int rbd_aio_write2(rbd_image_t image, uint64_t off, size_t len, - const char *buf, rbd_completion_t c, int op_flags) - int rbd_aio_read2(rbd_image_t image, uint64_t off, size_t len, - char *buf, rbd_completion_t c, int op_flags) - int rbd_aio_discard(rbd_image_t image, uint64_t off, uint64_t len, - rbd_completion_t c) - - int rbd_aio_create_completion(void *cb_arg, rbd_callback_t complete_cb, - rbd_completion_t *c) - int rbd_aio_is_complete(rbd_completion_t c) - int rbd_aio_wait_for_complete(rbd_completion_t c) - ssize_t rbd_aio_get_return_value(rbd_completion_t c) - void rbd_aio_release(rbd_completion_t c) - int rbd_aio_flush(rbd_image_t image, rbd_completion_t c) - - int rbd_metadata_get(rbd_image_t image, const char *key, char *value, - size_t *val_len) - int rbd_metadata_set(rbd_image_t image, const char *key, const char *value) - int rbd_metadata_remove(rbd_image_t image, const char *key) - int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max, - char *keys, size_t *key_len, char *values, - size_t *vals_len) - -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_FEATURE_DATA_POOL = _RBD_FEATURE_DATA_POOL - -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_MIRROR_MODE_DISABLED = _RBD_MIRROR_MODE_DISABLED -RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE -RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL - -RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING -RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED -RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED - -MIRROR_IMAGE_STATUS_STATE_UNKNOWN = _MIRROR_IMAGE_STATUS_STATE_UNKNOWN -MIRROR_IMAGE_STATUS_STATE_ERROR = _MIRROR_IMAGE_STATUS_STATE_ERROR -MIRROR_IMAGE_STATUS_STATE_SYNCING = _MIRROR_IMAGE_STATUS_STATE_SYNCING -MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY -MIRROR_IMAGE_STATUS_STATE_REPLAYING = _MIRROR_IMAGE_STATUS_STATE_REPLAYING -MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY -MIRROR_IMAGE_STATUS_STATE_STOPPED = _MIRROR_IMAGE_STATUS_STATE_STOPPED - -RBD_LOCK_MODE_EXCLUSIVE = _RBD_LOCK_MODE_EXCLUSIVE -RBD_LOCK_MODE_SHARED = _RBD_LOCK_MODE_SHARED - -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_IMAGE_OPTION_DATA_POOL = _RBD_IMAGE_OPTION_DATA_POOL - -class Error(Exception): - pass - - -class OSError(Error): - """ `OSError` class, derived from `Error` """ - def __init__(self, message, errno=None): - super(OSError, self).__init__(message) - self.errno = errno - - def __str__(self): - msg = super(OSError, self).__str__() - if self.errno is None: - return msg - return '[errno {0}] {1}'.format(self.errno, msg) - - def __reduce__(self): - return (self.__class__, (self.message, self.errno)) - -class PermissionError(OSError): - pass - - -class ImageNotFound(OSError): - pass - - -class ImageExists(OSError): - pass - - -class IOError(OSError): - pass - - -class NoSpace(OSError): - pass - - -class IncompleteWriteError(OSError): - pass - - -class InvalidArgument(OSError): - pass - - -class LogicError(Error): - pass - - -class ReadOnlyImage(OSError): - pass - - -class ImageBusy(OSError): - pass - - -class ImageHasSnapshots(OSError): - pass - - -class FunctionNotSupported(OSError): - pass - - -class ArgumentOutOfRange(OSError): - pass - - -class ConnectionShutdown(OSError): - pass - - -class Timeout(OSError): - pass - -class DiskQuotaExceeded(OSError): - 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, - errno.EDQUOT : DiskQuotaExceeded, -} - -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, errno=ret) - else: - return OSError(msg, errno=ret) - - -cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL: - return ioctx.io - -cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr) nogil: - 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 - -cdef class Completion - -cdef void __aio_complete_cb(rbd_completion_t completion, void *args) with gil: - """ - Callback to oncomplete() for asynchronous operations - """ - cdef Completion cb = args - cb._complete() - - -cdef class Completion(object): - """completion object""" - - cdef: - object image - object oncomplete - rbd_completion_t rbd_comp - PyObject* buf - bint persisted - object exc_info - - def __cinit__(self, image, object oncomplete): - self.oncomplete = oncomplete - self.image = image - self.persisted = False - - def is_complete(self): - """ - Has an asynchronous operation completed? - - This does not imply that the callback has finished. - - :returns: True if the operation is completed - """ - with nogil: - ret = rbd_aio_is_complete(self.rbd_comp) - return ret == 1 - - def wait_for_complete_and_cb(self): - """ - Wait for an asynchronous operation to complete - - This method waits for the callback to execute, if one was provided. - It will also re-raise any exceptions raised by the callback. You - should call this to "reap" asynchronous completions and ensure that - any exceptions in the callbacks are handled, as an exception internal - to this module may have occurred. - """ - with nogil: - rbd_aio_wait_for_complete(self.rbd_comp) - - if self.exc_info: - raise self.exc_info[0], self.exc_info[1], self.exc_info[2] - - def get_return_value(self): - """ - Get the return value of an asychronous operation - - The return value is set when the operation is complete. - - :returns: int - return value of the operation - """ - with nogil: - ret = rbd_aio_get_return_value(self.rbd_comp) - return ret - - def __dealloc__(self): - """ - Release a completion - - This is automatically called when the completion object is freed. - """ - ref.Py_XDECREF(self.buf) - self.buf = NULL - if self.rbd_comp != NULL: - with nogil: - rbd_aio_release(self.rbd_comp) - self.rbd_comp = NULL - - cdef void _complete(self): - try: - self.__unpersist() - if self.oncomplete: - self.oncomplete(self) - # In the event that something raises an exception during the next 2 - # lines of code, we will not be able to catch it, and this may result - # in the app not noticing a failed callback. However, this should only - # happen in extreme circumstances (OOM, etc.). KeyboardInterrupt - # should not be a problem because the callback thread from librbd - # ought to have SIGINT blocked. - except: - self.exc_info = sys.exc_info() - - cdef __persist(self): - if self.oncomplete is not None and not self.persisted: - # Increment our own reference count to make sure the completion - # is not freed until the callback is called. The completion is - # allowed to be freed if there is no callback. - ref.Py_INCREF(self) - self.persisted = True - - cdef __unpersist(self): - if self.persisted: - ref.Py_DECREF(self) - self.persisted = False - - -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=None, stripe_unit=None, stripe_count=None, - data_pool=None): - """ - 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 None to let librbd decide) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :param data_pool: optional separate pool for data blocks - :type data_pool: str - :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 or - ((stripe_unit is not None) and stripe_unit != 0) or - ((stripe_count is not None) and stripe_count != 0) or - data_pool): - raise InvalidArgument('format 1 images do not support feature ' - 'masks, non-default striping, nor data ' - 'pool') - 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) - if features is not None: - rbd_image_options_set_uint64(opts, - RBD_IMAGE_OPTION_FEATURES, - features) - if order is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - _order) - if stripe_unit is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - if stripe_count is not None: - rbd_image_options_set_uint64(opts, - RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - if data_pool is not None: - rbd_image_options_set_string(opts, - RBD_IMAGE_OPTION_DATA_POOL, - data_pool) - 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=None, order=None, stripe_unit=None, stripe_count=None, - data_pool=None): - """ - 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 None to let librbd decide) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :param data_pool: optional separate pool for data blocks - :type data_pool: str - :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 - - rbd_image_options_create(&opts) - try: - if features is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, - features) - if order is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - order) - if stripe_unit is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - if stripe_count is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - if data_pool is not None: - rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL, - data_pool) - 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(b'\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') - - def trash_move(self, ioctx, name, delay=0): - """ - Move an RBD image to the trash. - :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 - :param delay: time delay in seconds before the image can be deleted - from trash - :type delay: int - :raises: :class:`ImageNotFound` - """ - name = cstr(name, 'name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_name = name - uint64_t _delay = delay - with nogil: - ret = rbd_trash_move(_ioctx, _name, _delay) - if ret != 0: - raise make_ex(ret, 'error moving image to trash') - - def trash_remove(self, ioctx, image_id, force=False): - """ - Delete an RBD image from trash. If image deferment time has not - expired :class:`PermissionError` is raised. - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param image_id: the id of the image to remove - :type image_id: str - :param force: force remove even if deferment time has not expired - :type force: bool - :raises: :class:`ImageNotFound`, :class:`PermissionError` - """ - image_id = cstr(image_id, 'image_id') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_image_id = image_id - int _force = force - with nogil: - ret = rbd_trash_remove(_ioctx, _image_id, _force) - if ret != 0: - raise make_ex(ret, 'error deleting image from trash') - - def trash_get(self, ioctx, image_id): - """ - Retrieve RBD image info from trash - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param image_id: the id of the image to restore - :type image_id: str - :returns: dict - contains the following keys: - - * ``id`` (str) - image id - - * ``name`` (str) - image name - - * ``source`` (str) - source of deletion - - * ``deletion_time`` (datetime) - time of deletion - - * ``deferment_end_time`` (datetime) - time that an image is allowed - to be removed from trash - - :raises: :class:`ImageNotFound` - """ - image_id = cstr(image_id, 'image_id') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_image_id = image_id - rbd_trash_image_info_t c_info - with nogil: - ret = rbd_trash_get(_ioctx, _image_id, &c_info) - if ret != 0: - raise make_ex(ret, 'error restoring image from trash') - - __source_string = ['USER', 'MIRRORING'] - info = { - 'id' : decode_cstr(c_info.id), - 'name' : decode_cstr(c_info.name), - 'source' : __source_string[c_info.source], - 'deletion_time' : datetime.fromtimestamp(c_info.deletion_time), - 'deferment_end_time' : datetime.fromtimestamp(c_info.deferment_end_time) - } - rbd_trash_get_cleanup(&c_info) - return info - - def trash_list(self, ioctx): - """ - List all entries from trash. - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :returns: :class:`TrashIterator` - """ - return TrashIterator(ioctx) - - def trash_restore(self, ioctx, image_id, name): - """ - Restore an RBD image from trash. - :param ioctx: determines which RADOS pool the image is in - :type ioctx: :class:`rados.Ioctx` - :param image_id: the id of the image to restore - :type image_id: str - :param name: the new name of the restored image - :type name: str - :raises: :class:`ImageNotFound` - """ - image_id = cstr(image_id, 'image_id') - name = cstr(name, 'name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_image_id = image_id - char *_name = name - with nogil: - ret = rbd_trash_restore(_ioctx, _image_id, _name) - if ret != 0: - raise make_ex(ret, 'error restoring image from trash') - - def mirror_mode_get(self, ioctx): - """ - Get pool mirror mode. - - :param ioctx: determines which RADOS pool is read - :type ioctx: :class:`rados.Ioctx` - :returns: int - pool mirror mode - """ - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - rbd_mirror_mode_t mirror_mode - with nogil: - ret = rbd_mirror_mode_get(_ioctx, &mirror_mode) - if ret != 0: - raise make_ex(ret, 'error getting mirror mode') - return mirror_mode - - def mirror_mode_set(self, ioctx, mirror_mode): - """ - Set pool mirror mode. - - :param ioctx: determines which RADOS pool is written - :type ioctx: :class:`rados.Ioctx` - :param mirror_mode: mirror mode to set - :type mirror_mode: int - """ - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - rbd_mirror_mode_t _mirror_mode = mirror_mode - with nogil: - ret = rbd_mirror_mode_set(_ioctx, _mirror_mode) - if ret != 0: - raise make_ex(ret, 'error setting mirror mode') - - def mirror_peer_add(self, ioctx, cluster_name, client_name): - """ - Add mirror peer. - - :param ioctx: determines which RADOS pool is used - :type ioctx: :class:`rados.Ioctx` - :param cluster_name: mirror peer cluster name - :type cluster_name: str - :param client_name: mirror peer client name - :type client_name: str - :returns: str - peer uuid - """ - cluster_name = cstr(cluster_name, 'cluster_name') - client_name = cstr(client_name, 'client_name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_uuid = NULL - size_t _uuid_max_length = 512 - char *_cluster_name = cluster_name - char *_client_name = client_name - try: - _uuid = realloc_chk(_uuid, _uuid_max_length) - ret = rbd_mirror_peer_add(_ioctx, _uuid, _uuid_max_length, - _cluster_name, _client_name) - if ret != 0: - raise make_ex(ret, 'error adding mirror peer') - return decode_cstr(_uuid) - finally: - free(_uuid) - - def mirror_peer_remove(self, ioctx, uuid): - """ - Remove mirror peer. - - :param ioctx: determines which RADOS pool is used - :type ioctx: :class:`rados.Ioctx` - :param uuid: peer uuid - :type uuid: str - """ - uuid = cstr(uuid, 'uuid') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_uuid = uuid - with nogil: - ret = rbd_mirror_peer_remove(_ioctx, _uuid) - if ret != 0: - raise make_ex(ret, 'error removing mirror peer') - - def mirror_peer_list(self, ioctx): - """ - Iterate over the peers of a pool. - - :param ioctx: determines which RADOS pool is read - :type ioctx: :class:`rados.Ioctx` - :returns: :class:`MirrorPeerIterator` - """ - return MirrorPeerIterator(ioctx) - - def mirror_peer_set_client(self, ioctx, uuid, client_name): - """ - Set mirror peer client name - - :param ioctx: determines which RADOS pool is written - :type ioctx: :class:`rados.Ioctx` - :param uuid: uuid of the mirror peer - :type uuid: str - :param client_name: client name of the mirror peer to set - :type client_name: str - """ - uuid = cstr(uuid, 'uuid') - client_name = cstr(client_name, 'client_name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_uuid = uuid - char *_client_name = client_name - with nogil: - ret = rbd_mirror_peer_set_client(_ioctx, _uuid, _client_name) - if ret != 0: - raise make_ex(ret, 'error setting mirror peer client') - - def mirror_peer_set_cluster(self, ioctx, uuid, cluster_name): - """ - Set mirror peer cluster name - - :param ioctx: determines which RADOS pool is written - :type ioctx: :class:`rados.Ioctx` - :param uuid: uuid of the mirror peer - :type uuid: str - :param cluster_name: cluster name of the mirror peer to set - :type cluster_name: str - """ - uuid = cstr(uuid, 'uuid') - cluster_name = cstr(cluster_name, 'cluster_name') - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - char *_uuid = uuid - char *_cluster_name = cluster_name - with nogil: - ret = rbd_mirror_peer_set_cluster(_ioctx, _uuid, _cluster_name) - if ret != 0: - raise make_ex(ret, 'error setting mirror peer cluster') - - def mirror_image_status_list(self, ioctx): - """ - Iterate over the mirror image statuses of a pool. - - :param ioctx: determines which RADOS pool is read - :type ioctx: :class:`rados.Ioctx` - :returns: :class:`MirrorImageStatus` - """ - return MirrorImageStatusIterator(ioctx) - - def mirror_image_status_summary(self, ioctx): - """ - Get mirror image status summary of a pool. - - :param ioctx: determines which RADOS pool is read - :type ioctx: :class:`rados.Ioctx` - :returns: list - a list of (state, count) tuples - """ - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - rbd_mirror_image_status_state_t *states = NULL - int *counts = NULL - size_t maxlen = 32 - try: - states = realloc_chk(states, - sizeof(rbd_mirror_image_status_state_t) * maxlen) - counts = realloc_chk(counts, sizeof(int) * maxlen) - with nogil: - ret = rbd_mirror_image_status_summary(_ioctx, states, counts, - &maxlen) - if ret < 0: - raise make_ex(ret, 'error getting mirror image status summary') - return [(states[i], counts[i]) for i in range(maxlen)] - finally: - free(states) - free(counts) - -cdef class MirrorPeerIterator(object): - """ - Iterator over mirror peer info for a pool. - - Yields a dictionary containing information about a peer. - - Keys are: - - * ``uuid`` (str) - uuid of the peer - - * ``cluster_name`` (str) - cluster name of the peer - - * ``client_name`` (str) - client name of the peer - """ - - cdef: - rbd_mirror_peer_t *peers - int num_peers - - def __init__(self, ioctx): - cdef: - rados_ioctx_t _ioctx = convert_ioctx(ioctx) - self.peers = NULL - self.num_peers = 10 - while True: - self.peers = realloc_chk( - self.peers, self.num_peers * sizeof(rbd_mirror_peer_t)) - with nogil: - ret = rbd_mirror_peer_list(_ioctx, self.peers, &self.num_peers) - if ret < 0: - if ret == -errno.ERANGE: - continue - self.num_peers = 0 - raise make_ex(ret, 'error listing peers') - break - - def __iter__(self): - for i in range(self.num_peers): - yield { - 'uuid' : decode_cstr(self.peers[i].uuid), - 'cluster_name' : decode_cstr(self.peers[i].cluster_name), - 'client_name' : decode_cstr(self.peers[i].client_name), - } - - def __dealloc__(self): - if self.peers: - rbd_mirror_peer_list_cleanup(self.peers, self.num_peers) - free(self.peers) - -cdef class MirrorImageStatusIterator(object): - """ - Iterator over mirror image status for a pool. - - Yields a dictionary containing mirror status of an image. - - Keys are: - - * ``name`` (str) - mirror image name - - * `info` (dict) - mirror image info - - * `state` (int) - mirror state - - * `description` (str) - status description - - * `last_update` (datetime) - last status update time - - * ``up`` (bool) - is mirroring agent up - """ - - cdef: - rados_ioctx_t ioctx - size_t max_read - char *last_read - char **image_ids - rbd_mirror_image_status_t *images - size_t size - - def __init__(self, ioctx): - self.ioctx = convert_ioctx(ioctx) - self.max_read = 1024 - self.last_read = strdup("") - self.image_ids = realloc_chk(NULL, - sizeof(char *) * self.max_read) - self.images = realloc_chk(NULL, - sizeof(rbd_mirror_image_status_t) * self.max_read) - self.size = 0 - self.get_next_chunk() - - def __iter__(self): - while self.size > 0: - for i in range(self.size): - yield { - 'name' : decode_cstr(self.images[i].name), - 'info' : { - 'global_id' : decode_cstr(self.images[i].info.global_id), - 'state' : self.images[i].info.state, - }, - 'state' : self.images[i].state, - 'description' : decode_cstr(self.images[i].description), - 'last_update' : datetime.fromtimestamp(self.images[i].last_update), - 'up' : self.images[i].up, - } - if self.size < self.max_read: - break - self.get_next_chunk() - - def __dealloc__(self): - rbd_mirror_image_status_list_cleanup(self.image_ids, self.images, - self.size) - if self.last_read: - free(self.last_read) - if self.image_ids: - free(self.image_ids) - if self.images: - free(self.images) - - def get_next_chunk(self): - if self.size > 0: - rbd_mirror_image_status_list_cleanup(self.image_ids, self.images, - self.size) - self.size = 0 - with nogil: - ret = rbd_mirror_image_status_list(self.ioctx, self.last_read, - self.max_read, self.image_ids, - self.images, &self.size) - if ret < 0: - raise make_ex(ret, 'error listing mirror images status') - if self.size > 0: - free(self.last_read) - last_read = decode_cstr(self.image_ids[self.size - 1]) - self.last_read = strdup(last_read) - else: - free(self.last_read) - self.last_read = strdup("") - -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 __get_completion(self, oncomplete): - """ - Constructs a completion to use with asynchronous operations - - :param oncomplete: callback for the completion - - :raises: :class:`Error` - :returns: completion object - """ - - completion_obj = Completion(self, oncomplete) - - cdef: - rbd_completion_t completion - PyObject* p_completion_obj= completion_obj - - with nogil: - ret = rbd_aio_create_completion(p_completion_obj, __aio_complete_cb, - &completion) - if ret < 0: - raise make_ex(ret, "error getting a completion") - - completion_obj.rbd_comp = completion - return completion_obj - - 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: - self.closed = True - with nogil: - ret = rbd_close(self.image) - if ret < 0: - raise make_ex(ret, 'error while closing image %s' % ( - self.name,)) - - def __dealloc__(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 id(self): - """ - Get the RBD v2 internal image id - - :returns: str - image id - """ - cdef: - int ret = -errno.ERANGE - size_t size = 32 - char *image_id = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - image_id = realloc_chk(image_id, size) - with nogil: - ret = rbd_get_id(self.image, image_id, size) - if ret == -errno.ERANGE: - size *= 2 - - if ret != 0: - raise make_ex(ret, 'error getting id for image %s' % (self.name,)) - return decode_cstr(image_id) - finally: - free(image_id) - - def block_name_prefix(self): - """ - Get the RBD block name prefix - - :returns: str - block name prefix - """ - cdef: - int ret = -errno.ERANGE - size_t size = 32 - char *prefix = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - prefix = realloc_chk(prefix, size) - with nogil: - ret = rbd_get_block_name_prefix(self.image, prefix, size) - if ret == -errno.ERANGE: - size *= 2 - - if ret != 0: - raise make_ex(ret, 'error getting block name prefix for image %s' % (self.name,)) - return decode_cstr(prefix) - finally: - free(prefix) - - 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_info2(self.image, pool, size, name, - size, NULL, 0, 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 parent_id(self): - """ - Get image id of a cloned image's parent (if any) - - :returns: str - the parent id - :raises: :class:`ImageNotFound` if the image doesn't have a parent - """ - cdef: - int ret = -errno.ERANGE - size_t size = 32 - char *parent_id = NULL - try: - while ret == -errno.ERANGE and size <= 4096: - parent_id = realloc_chk(parent_id, size) - with nogil: - ret = rbd_get_parent_info2(self.image, NULL, 0, NULL, 0, - parent_id, size, NULL, 0) - if ret == -errno.ERANGE: - size *= 2 - - if ret != 0: - raise make_ex(ret, 'error getting parent id for image %s' % (self.name,)) - return decode_cstr(parent_id) - finally: - free(parent_id) - - 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 %s' % (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 %s' % (self.name)) - return image_size - - def features(self): - """ - Get 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 %s' % (self.name)) - return features - - def update_features(self, features, enabled): - """ - Update 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): - """ - Get 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 %s' % (self.name)) - return overlap - - def flags(self): - """ - Get 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 %s' % (self.name)) - return flags - - def is_exclusive_lock_owner(self): - """ - Get 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 %s' % (self.name)) - return owner == 1 - - def copy(self, dest_ioctx, dest_name, features=None, order=None, - stripe_unit=None, stripe_count=None, data_pool=None): - """ - 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 None to let librbd decide) - :type stripe_unit: int - :param stripe_count: objects to stripe over before looping - :type stripe_count: int - :param data_pool: optional separate pool for data blocks - :type data_pool: str - :raises: :class:`TypeError` - :raises: :class:`InvalidArgument` - :raises: :class:`ImageExists` - :raises: :class:`FunctionNotSupported` - :raises: :class:`ArgumentOutOfRange` - """ - 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: - if features is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES, - features) - if order is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER, - order) - if stripe_unit is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT, - stripe_unit) - if stripe_count is not None: - rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT, - stripe_count) - if data_pool is not None: - rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL, - data_pool) - 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 remove_snap2(self, name, flags): - """ - Delete a snapshot of the image. - - :param name: the name of the snapshot - :param flags: the flags for removal - :type name: str - :raises: :class:`IOError`, :class:`ImageBusy` - """ - name = cstr(name, 'name') - cdef: - char *_name = name - uint32_t _flags = flags - librbd_progress_fn_t prog_cb = &no_op_progress_callback - with nogil: - ret = rbd_snap_remove2(self.image, _name, _flags, prog_cb, NULL) - if ret != 0: - raise make_ex(ret, 'error removing snapshot %s from %s with flags %llx' % (name, self.name, flags)) - - 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 get_snap_limit(self): - """ - Get the snapshot limit for an image. - """ - - cdef: - uint64_t limit - with nogil: - ret = rbd_snap_get_limit(self.image, &limit) - if ret != 0: - raise make_ex(ret, 'error getting snapshot limit for %s' % self.name) - return limit - - def set_snap_limit(self, limit): - """ - Set the snapshot limit for an image. - - :param limit: the new limit to set - """ - - cdef: - uint64_t _limit = limit - with nogil: - ret = rbd_snap_set_limit(self.image, _limit) - if ret != 0: - raise make_ex(ret, 'error setting snapshot limit for %s' % self.name) - return ret - - def get_snap_timestamp(self, snap_id): - """ - Get the snapshot timestamp for an image. - :param snap_id: the snapshot id of a snap shot - """ - cdef: - timespec timestamp - uint64_t _snap_id = snap_id - with nogil: - ret = rbd_snap_get_timestamp(self.image, _snap_id, ×tamp) - if ret != 0: - raise make_ex(ret, 'error getting snapshot timestamp for image: %s, snap_id: %d' % (self.name, snap_id)) - return datetime.fromtimestamp(timestamp.tv_sec) - - def remove_snap_limit(self): - """ - Remove the snapshot limit for an image, essentially setting - the limit to the maximum size allowed by the implementation. - """ - with nogil: - ret = rbd_snap_set_limit(self.image, UINT64_MAX) - if ret != 0: - raise make_ex(ret, 'error removing snapshot limit for %s' % self.name) - return ret - - 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): - """ - Return 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 %s' % (self.name)) - return stripe_unit - - def stripe_count(self): - """ - Return 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 %s' % (self.name)) - return stripe_count - - def create_timestamp(self): - """ - Return the create timestamp for the image. - """ - cdef: - timespec timestamp - with nogil: - ret = rbd_get_create_timestamp(self.image, ×tamp) - if ret != 0: - raise make_ex(ret, 'error getting create timestamp for image: %s' % (self.name)) - return datetime.fromtimestamp(timestamp.tv_sec) - - 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): - """ - Rebuild 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(b'\0')) - images = map(decode_cstr, c_images[:images_size - 1].split(b'\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(b'\0')) - cookies = map(decode_cstr, c_cookies[:cookies_size - 1].split(b'\0')) - addrs = map(decode_cstr, c_addrs[:addrs_size - 1].split(b'\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_acquire(self, lock_mode): - """ - Acquire a managed lock on the image. - - :param lock_mode: lock mode to set - :type lock_mode: int - :raises: :class:`ImageBusy` if the lock could not be acquired - """ - cdef: - rbd_lock_mode_t _lock_mode = lock_mode - with nogil: - ret = rbd_lock_acquire(self.image, _lock_mode) - if ret < 0: - raise make_ex(ret, 'error acquiring lock on image') - - def lock_release(self): - """ - Release a managed lock on the image that was previously acquired. - """ - with nogil: - ret = rbd_lock_release(self.image) - if ret < 0: - raise make_ex(ret, 'error releasing lock on image') - - def lock_get_owners(self): - """ - Iterate over the lock owners of an image. - - :returns: :class:`LockOwnerIterator` - """ - return LockOwnerIterator(self) - - def lock_break(self, lock_mode, lock_owner): - """ - Break the image lock held by a another client. - - :param lock_owner: the owner of the lock to break - :type lock_owner: str - """ - lock_owner = cstr(lock_owner, 'lock_owner') - cdef: - rbd_lock_mode_t _lock_mode = lock_mode - char *_lock_owner = lock_owner - with nogil: - ret = rbd_lock_break(self.image, _lock_mode, _lock_owner) - if ret < 0: - raise make_ex(ret, 'error breaking lock on image') - - 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') - - def mirror_image_enable(self): - """ - Enable mirroring for the image. - """ - with nogil: - ret = rbd_mirror_image_enable(self.image) - if ret < 0: - raise make_ex(ret, 'error enabling mirroring for image %s' - % (self.name,)) - - def mirror_image_disable(self, force): - """ - Disable mirroring for the image. - - :param force: force disabling - :type force: bool - """ - cdef bint c_force = force - with nogil: - ret = rbd_mirror_image_disable(self.image, c_force) - if ret < 0: - raise make_ex(ret, 'error disabling mirroring for image %s' % - (self.name,)) - - def mirror_image_promote(self, force): - """ - Promote the image to primary for mirroring. - - :param force: force promoting - :type force: bool - """ - cdef bint c_force = force - with nogil: - ret = rbd_mirror_image_promote(self.image, c_force) - if ret < 0: - raise make_ex(ret, 'error promoting image %s to primary' % - (self.name,)) - - def mirror_image_demote(self): - """ - Demote the image to secondary for mirroring. - """ - with nogil: - ret = rbd_mirror_image_demote(self.image) - if ret < 0: - raise make_ex(ret, 'error demoting image %s to secondary' % - (self.name,)) - - def mirror_image_resync(self): - """ - Flag the image to resync. - """ - with nogil: - ret = rbd_mirror_image_resync(self.image) - if ret < 0: - raise make_ex(ret, 'error to resync image %s' % (self.name,)) - - def mirror_image_get_info(self): - """ - Get mirror info for the image. - - :returns: dict - contains the following keys: - - * ``global_id`` (str) - image global id - - * ``state`` (int) - mirror state - - * ``primary`` (bool) - is image primary - """ - cdef rbd_mirror_image_info_t c_info - with nogil: - ret = rbd_mirror_image_get_info(self.image, &c_info, sizeof(c_info)) - if ret != 0: - raise make_ex(ret, 'error getting mirror info for image %s' % - (self.name,)) - info = { - 'global_id' : decode_cstr(c_info.global_id), - 'state' : int(c_info.state), - 'primary' : c_info.primary, - } - free(c_info.global_id) - return info - - def mirror_image_get_status(self): - """ - Get mirror status for the image. - - :returns: dict - contains the following keys: - - * ``name`` (str) - mirror image name - - * `info` (dict) - mirror image info - - * ``state`` (int) - status mirror state - - * ``description`` (str) - status description - - * ``last_update`` (datetime) - last status update time - - * ``up`` (bool) - is mirroring agent up - """ - cdef rbd_mirror_image_status_t c_status - with nogil: - ret = rbd_mirror_image_get_status(self.image, &c_status, - sizeof(c_status)) - if ret != 0: - raise make_ex(ret, 'error getting mirror status for image %s' % - (self.name,)) - status = { - 'name' : decode_cstr(c_status.name), - 'info' : { - 'global_id' : decode_cstr(c_status.info.global_id), - 'state' : int(c_status.info.state), - 'primary' : c_status.info.primary, - }, - 'state' : c_status.state, - 'description' : decode_cstr(c_status.description), - 'last_update' : datetime.fromtimestamp(c_status.last_update), - 'up' : c_status.up, - } - free(c_status.name) - free(c_status.info.global_id) - free(c_status.description) - return status - - def aio_read(self, offset, length, oncomplete, fadvise_flags=0): - """ - Asynchronously read data from the image - - Raises :class:`InvalidArgument` if part of the range specified is - outside the image. - - oncomplete will be called with the returned read value as - well as the completion: - - oncomplete(completion, data_read) - - :param offset: the offset to start reading at - :type offset: int - :param length: how many bytes to read - :type length: int - :param oncomplete: what to do when the read is complete - :type oncomplete: completion - :param fadvise_flags: fadvise flags for this read - :type fadvise_flags: int - :returns: :class:`Completion` - the completion object - :raises: :class:`InvalidArgument`, :class:`IOError` - """ - - cdef: - char *ret_buf - uint64_t _offset = offset - size_t _length = length - int _fadvise_flags = fadvise_flags - Completion completion - - def oncomplete_(completion_v): - cdef Completion _completion_v = completion_v - return_value = _completion_v.get_return_value() - if return_value > 0 and return_value != length: - _PyBytes_Resize(&_completion_v.buf, return_value) - return oncomplete(_completion_v, _completion_v.buf if return_value >= 0 else None) - - completion = self.__get_completion(oncomplete_) - completion.buf = PyBytes_FromStringAndSize(NULL, length) - ret_buf = PyBytes_AsString(completion.buf) - try: - completion.__persist() - with nogil: - ret = rbd_aio_read2(self.image, _offset, _length, ret_buf, - completion.rbd_comp, _fadvise_flags) - if ret < 0: - raise make_ex(ret, 'error reading %s %ld~%ld' % - (self.name, offset, length)) - except: - completion.__unpersist() - raise - - return completion - - def aio_write(self, data, offset, oncomplete, fadvise_flags=0): - """ - Asynchronously write data to the image - - Raises :class:`InvalidArgument` if part of the write would fall outside - the image. - - oncomplete will be called with the completion: - - oncomplete(completion) - - :param data: the data to be written - :type data: bytes - :param offset: the offset to start writing at - :type offset: int - :param oncomplete: what to do when the write is complete - :type oncomplete: completion - :param fadvise_flags: fadvise flags for this write - :type fadvise_flags: int - :returns: :class:`Completion` - the completion object - :raises: :class:`InvalidArgument`, :class:`IOError` - """ - - cdef: - uint64_t _offset = offset - char *_data = data - size_t _length = len(data) - int _fadvise_flags = fadvise_flags - Completion completion - - completion = self.__get_completion(oncomplete) - try: - completion.__persist() - with nogil: - ret = rbd_aio_write2(self.image, _offset, _length, _data, - completion.rbd_comp, _fadvise_flags) - if ret < 0: - raise make_ex(ret, 'error writing %s %ld~%ld' % - (self.name, offset, _length)) - except: - completion.__unpersist() - raise - - return completion - - def aio_discard(self, offset, length, oncomplete): - """ - Asynchronously trim the range from the image. It will be logically - filled with zeroes. - """ - - cdef: - uint64_t _offset = offset - size_t _length = length - Completion completion - - completion = self.__get_completion(oncomplete) - try: - completion.__persist() - with nogil: - ret = rbd_aio_discard(self.image, _offset, _length, - completion.rbd_comp) - if ret < 0: - raise make_ex(ret, 'error discarding %s %ld~%ld' % - (self.name, offset, _length)) - except: - completion.__unpersist() - raise - - return completion - - def aio_flush(self, oncomplete): - """ - Asyncronously wait until all writes are fully flushed if caching is - enabled. - """ - - cdef Completion completion = self.__get_completion(oncomplete) - try: - completion.__persist() - with nogil: - ret = rbd_aio_flush(self.image, completion.rbd_comp) - if ret < 0: - raise make_ex(ret, 'error flushing') - except: - completion.__unpersist() - raise - - return completion - - def metadata_get(self, key): - """ - Get image metadata for the given key. - - :param key: metadata key - :type key: str - :returns: str - image id - """ - key = cstr(key, 'key') - cdef: - char *_key = key - size_t size = 4096 - char *value = NULL - int ret - try: - while True: - value = realloc_chk(value, size) - with nogil: - ret = rbd_metadata_get(self.image, _key, value, &size) - if ret != -errno.ERANGE: - break - if ret != 0: - raise make_ex(ret, 'error getting metadata %s for image %s' % - (self.key, self.name,)) - return decode_cstr(value) - finally: - free(value) - - def metadata_set(self, key, value): - """ - Set image metadata for the given key. - - :param key: metadata key - :type key: str - :param value: metadata value - :type value: str - """ - key = cstr(key, 'key') - value = cstr(value, 'value') - cdef: - char *_key = key - char *_value = value - with nogil: - ret = rbd_metadata_set(self.image, _key, _value) - - if ret != 0: - raise make_ex(ret, 'error setting metadata %s for image %s' % - (self.key, self.name,)) - - - def metadata_remove(self, key): - """ - Remove image metadata for the given key. - - :param key: metadata key - :type key: str - """ - key = cstr(key, 'key') - cdef: - char *_key = key - with nogil: - ret = rbd_metadata_remove(self.image, _key) - - if ret != 0: - raise make_ex(ret, 'error removing metadata %s for image %s' % - (self.key, self.name,)) - - def metadata_list(self): - """ - List image metadata. - - :returns: :class:`MetadataIterator` - """ - return MetadataIterator(self) - -cdef class LockOwnerIterator(object): - """ - Iterator over managed lock owners for an image - - Yields a dictionary containing information about the image's lock - - Keys are: - - * ``mode`` (int) - active lock mode - - * ``owner`` (str) - lock owner name - """ - - cdef: - rbd_lock_mode_t lock_mode - char **lock_owners - size_t num_lock_owners - object image - - def __init__(self, Image image): - self.image = image - self.lock_owners = NULL - self.num_lock_owners = 8 - while True: - self.lock_owners = realloc_chk(self.lock_owners, - self.num_lock_owners * - sizeof(char*)) - with nogil: - ret = rbd_lock_get_owners(image.image, &self.lock_mode, - self.lock_owners, - &self.num_lock_owners) - if ret >= 0: - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing lock owners for image %s' % (image.name,)) - - def __iter__(self): - for i in range(self.num_lock_owners): - yield { - 'mode' : int(self.lock_mode), - 'owner' : decode_cstr(self.lock_owners[i]), - } - - def __dealloc__(self): - if self.lock_owners: - rbd_lock_get_owners_cleanup(self.lock_owners, self.num_lock_owners) - free(self.lock_owners) - -cdef class MetadataIterator(object): - """ - Iterator over metadata list for an image. - - Yields ``(key, value)`` tuple. - - * ``key`` (str) - metadata key - * ``value`` (str) - metadata value - """ - - cdef: - object image_name - rbd_image_t image - char *last_read - uint64_t max_read - object next_chunk - - def __init__(self, Image image): - self.image_name = image.name - self.image = image.image - self.last_read = strdup("") - self.max_read = 32 - self.get_next_chunk() - - def __iter__(self): - while len(self.next_chunk) > 0: - for pair in self.next_chunk: - yield pair - if len(self.next_chunk) < self.max_read: - break - self.get_next_chunk() - - def __dealloc__(self): - if self.last_read: - free(self.last_read) - - def get_next_chunk(self): - cdef: - char *c_keys = NULL - size_t keys_size = 4096 - char *c_vals = NULL - size_t vals_size = 4096 - try: - while True: - c_keys = realloc_chk(c_keys, keys_size) - c_vals = realloc_chk(c_vals, vals_size) - with nogil: - ret = rbd_metadata_list(self.image, self.last_read, - self.max_read, c_keys, &keys_size, - c_vals, &vals_size) - if ret >= 0: - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing metadata for image %s' % - (self.image_name,)) - keys = [decode_cstr(key) for key in - c_keys[:keys_size].split(b'\0') if key] - vals = [decode_cstr(val) for val in - c_vals[:vals_size].split(b'\0') if val] - if len(keys) > 0: - free(self.last_read) - self.last_read = strdup(keys[-1]) - self.next_chunk = zip(keys, vals) - finally: - free(c_keys) - free(c_vals) - -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 - cdef object image - - def __init__(self, Image image): - 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 __dealloc__(self): - if self.snaps: - rbd_snap_list_end(self.snaps) - free(self.snaps) - -cdef class TrashIterator(object): - """ - Iterator over trash entries. - - Yields a dictionary containing trash info of an image. - - Keys are: - - * `id` (str) - image id - - * `name` (str) - image name - - * `source` (str) - source of deletion - - * `deletion_time` (datetime) - time of deletion - - * `deferment_end_time` (datetime) - time that an image is allowed to be - removed from trash - """ - - cdef: - rados_ioctx_t ioctx - size_t num_entries - rbd_trash_image_info_t *entries - - def __init__(self, ioctx): - self.ioctx = convert_ioctx(ioctx) - self.num_entries = 1024 - self.entries = NULL - while True: - self.entries = realloc_chk(self.entries, - self.num_entries * - sizeof(rbd_trash_image_info_t)) - with nogil: - ret = rbd_trash_list(self.ioctx, self.entries, &self.num_entries) - if ret >= 0: - self.num_entries = ret - break - elif ret != -errno.ERANGE: - raise make_ex(ret, 'error listing trash entries') - - __source_string = ['USER', 'MIRRORING'] - - def __iter__(self): - for i in range(self.num_entries): - yield { - 'id' : decode_cstr(self.entries[i].id), - 'name' : decode_cstr(self.entries[i].name), - 'source' : TrashIterator.__source_string[self.entries[i].source], - 'deletion_time' : datetime.fromtimestamp(self.entries[i].deletion_time), - 'deferment_end_time' : datetime.fromtimestamp(self.entries[i].deferment_end_time) - } - - def __dealloc__(self): - rbd_trash_list_cleanup(self.entries, self.num_entries) - if self.entries: - free(self.entries) -