1 # cython: embedsignature=True
3 This module is a thin wrapper around librbd.
5 It currently provides all the synchronous methods of librbd that do
8 Error codes from librbd are turned into exceptions that subclass
9 :class:`Error`. Almost all methods may raise :class:`Error`
10 (the base class of all rbd exceptions), :class:`PermissionError`
11 and :class:`IOError`, in addition to those documented for the
14 # Copyright 2011 Josh Durgin
15 # Copyright 2015 Hector Martin <marcan@marcan.st>
21 from cpython cimport PyObject, ref, exc
22 from libc cimport errno
23 from libc.stdint cimport *
24 from libc.stdlib cimport malloc, realloc, free
25 from libc.string cimport strdup, memset
28 from collections.abc import Iterable
30 from collections import Iterable
31 from datetime import datetime
33 from itertools import chain
37 include "mock_rbd.pxi"
43 cdef extern from "Python.h":
44 # These are in cpython/string.pxd, but use "object" types instead of
45 # PyObject*, which invokes assumptions in cpython that we need to
46 # legitimately break to implement zero-copy string buffers in Image.read().
47 # This is valid use of the Python API and documented as a special case.
48 PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL
49 char* PyBytes_AsString(PyObject *string) except NULL
50 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
52 cdef extern from "<errno.h>" nogil:
54 _ECANCELED "ECANCELED"
57 ECANCELED = _ECANCELED
59 RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING
60 RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2
61 RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK
62 RBD_FEATURE_OBJECT_MAP = _RBD_FEATURE_OBJECT_MAP
63 RBD_FEATURE_FAST_DIFF = _RBD_FEATURE_FAST_DIFF
64 RBD_FEATURE_DEEP_FLATTEN = _RBD_FEATURE_DEEP_FLATTEN
65 RBD_FEATURE_JOURNALING = _RBD_FEATURE_JOURNALING
66 RBD_FEATURE_DATA_POOL = _RBD_FEATURE_DATA_POOL
67 RBD_FEATURE_OPERATIONS = _RBD_FEATURE_OPERATIONS
68 RBD_FEATURE_MIGRATING = _RBD_FEATURE_MIGRATING
69 RBD_FEATURE_NON_PRIMARY = _RBD_FEATURE_NON_PRIMARY
71 RBD_FEATURES_INCOMPATIBLE = _RBD_FEATURES_INCOMPATIBLE
72 RBD_FEATURES_RW_INCOMPATIBLE = _RBD_FEATURES_RW_INCOMPATIBLE
73 RBD_FEATURES_MUTABLE = _RBD_FEATURES_MUTABLE
74 RBD_FEATURES_SINGLE_CLIENT = _RBD_FEATURES_SINGLE_CLIENT
75 RBD_FEATURES_ALL = _RBD_FEATURES_ALL
77 RBD_OPERATION_FEATURE_CLONE_PARENT = _RBD_OPERATION_FEATURE_CLONE_PARENT
78 RBD_OPERATION_FEATURE_CLONE_CHILD = _RBD_OPERATION_FEATURE_CLONE_CHILD
79 RBD_OPERATION_FEATURE_GROUP = _RBD_OPERATION_FEATURE_GROUP
80 RBD_OPERATION_FEATURE_SNAP_TRASH = _RBD_OPERATION_FEATURE_SNAP_TRASH
82 RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID
83 RBD_FLAG_FAST_DIFF_INVALID = _RBD_FLAG_FAST_DIFF_INVALID
85 RBD_MIRROR_MODE_DISABLED = _RBD_MIRROR_MODE_DISABLED
86 RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE
87 RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL
89 RBD_MIRROR_PEER_DIRECTION_RX = _RBD_MIRROR_PEER_DIRECTION_RX
90 RBD_MIRROR_PEER_DIRECTION_TX = _RBD_MIRROR_PEER_DIRECTION_TX
91 RBD_MIRROR_PEER_DIRECTION_RX_TX = _RBD_MIRROR_PEER_DIRECTION_RX_TX
93 RBD_MIRROR_IMAGE_MODE_JOURNAL = _RBD_MIRROR_IMAGE_MODE_JOURNAL
94 RBD_MIRROR_IMAGE_MODE_SNAPSHOT = _RBD_MIRROR_IMAGE_MODE_SNAPSHOT
96 RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING
97 RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED
98 RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED
100 MIRROR_IMAGE_STATUS_STATE_UNKNOWN = _MIRROR_IMAGE_STATUS_STATE_UNKNOWN
101 MIRROR_IMAGE_STATUS_STATE_ERROR = _MIRROR_IMAGE_STATUS_STATE_ERROR
102 MIRROR_IMAGE_STATUS_STATE_SYNCING = _MIRROR_IMAGE_STATUS_STATE_SYNCING
103 MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
104 MIRROR_IMAGE_STATUS_STATE_REPLAYING = _MIRROR_IMAGE_STATUS_STATE_REPLAYING
105 MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
106 MIRROR_IMAGE_STATUS_STATE_STOPPED = _MIRROR_IMAGE_STATUS_STATE_STOPPED
108 RBD_LOCK_MODE_EXCLUSIVE = _RBD_LOCK_MODE_EXCLUSIVE
109 RBD_LOCK_MODE_SHARED = _RBD_LOCK_MODE_SHARED
111 RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT
112 RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES
113 RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER
114 RBD_IMAGE_OPTION_STRIPE_UNIT = _RBD_IMAGE_OPTION_STRIPE_UNIT
115 RBD_IMAGE_OPTION_STRIPE_COUNT = _RBD_IMAGE_OPTION_STRIPE_COUNT
116 RBD_IMAGE_OPTION_DATA_POOL = _RBD_IMAGE_OPTION_DATA_POOL
118 RBD_SNAP_NAMESPACE_TYPE_USER = _RBD_SNAP_NAMESPACE_TYPE_USER
119 RBD_SNAP_NAMESPACE_TYPE_GROUP = _RBD_SNAP_NAMESPACE_TYPE_GROUP
120 RBD_SNAP_NAMESPACE_TYPE_TRASH = _RBD_SNAP_NAMESPACE_TYPE_TRASH
121 RBD_SNAP_NAMESPACE_TYPE_MIRROR = _RBD_SNAP_NAMESPACE_TYPE_MIRROR
123 RBD_SNAP_MIRROR_STATE_PRIMARY = _RBD_SNAP_MIRROR_STATE_PRIMARY
124 RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED = _RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED
125 RBD_SNAP_MIRROR_STATE_NON_PRIMARY = _RBD_SNAP_MIRROR_STATE_NON_PRIMARY
126 RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED = _RBD_SNAP_MIRROR_STATE_NON_PRIMARY_DEMOTED
128 RBD_GROUP_IMAGE_STATE_ATTACHED = _RBD_GROUP_IMAGE_STATE_ATTACHED
129 RBD_GROUP_IMAGE_STATE_INCOMPLETE = _RBD_GROUP_IMAGE_STATE_INCOMPLETE
131 RBD_GROUP_SNAP_STATE_INCOMPLETE = _RBD_GROUP_SNAP_STATE_INCOMPLETE
132 RBD_GROUP_SNAP_STATE_COMPLETE = _RBD_GROUP_SNAP_STATE_COMPLETE
134 RBD_IMAGE_MIGRATION_STATE_UNKNOWN = _RBD_IMAGE_MIGRATION_STATE_UNKNOWN
135 RBD_IMAGE_MIGRATION_STATE_ERROR = _RBD_IMAGE_MIGRATION_STATE_ERROR
136 RBD_IMAGE_MIGRATION_STATE_PREPARING = _RBD_IMAGE_MIGRATION_STATE_PREPARING
137 RBD_IMAGE_MIGRATION_STATE_PREPARED = _RBD_IMAGE_MIGRATION_STATE_PREPARED
138 RBD_IMAGE_MIGRATION_STATE_EXECUTING = _RBD_IMAGE_MIGRATION_STATE_EXECUTING
139 RBD_IMAGE_MIGRATION_STATE_EXECUTED = _RBD_IMAGE_MIGRATION_STATE_EXECUTED
140 RBD_IMAGE_MIGRATION_STATE_ABORTING = _RBD_IMAGE_MIGRATION_STATE_ABORTING
142 RBD_CONFIG_SOURCE_CONFIG = _RBD_CONFIG_SOURCE_CONFIG
143 RBD_CONFIG_SOURCE_POOL = _RBD_CONFIG_SOURCE_POOL
144 RBD_CONFIG_SOURCE_IMAGE = _RBD_CONFIG_SOURCE_IMAGE
146 RBD_POOL_STAT_OPTION_IMAGES = _RBD_POOL_STAT_OPTION_IMAGES
147 RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES
148 RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES
149 RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS = _RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS
150 RBD_POOL_STAT_OPTION_TRASH_IMAGES = _RBD_POOL_STAT_OPTION_TRASH_IMAGES
151 RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES
152 RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES = _RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES
153 RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS = _RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS
155 RBD_SNAP_CREATE_SKIP_QUIESCE = _RBD_SNAP_CREATE_SKIP_QUIESCE
156 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR = _RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
158 RBD_SNAP_REMOVE_UNPROTECT = _RBD_SNAP_REMOVE_UNPROTECT
159 RBD_SNAP_REMOVE_FLATTEN = _RBD_SNAP_REMOVE_FLATTEN
160 RBD_SNAP_REMOVE_FORCE = _RBD_SNAP_REMOVE_FORCE
162 RBD_ENCRYPTION_FORMAT_LUKS1 = _RBD_ENCRYPTION_FORMAT_LUKS1
163 RBD_ENCRYPTION_FORMAT_LUKS2 = _RBD_ENCRYPTION_FORMAT_LUKS2
164 RBD_ENCRYPTION_FORMAT_LUKS = _RBD_ENCRYPTION_FORMAT_LUKS
165 RBD_ENCRYPTION_ALGORITHM_AES128 = _RBD_ENCRYPTION_ALGORITHM_AES128
166 RBD_ENCRYPTION_ALGORITHM_AES256 = _RBD_ENCRYPTION_ALGORITHM_AES256
168 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION = _RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
170 class Error(Exception):
174 class OSError(Error):
175 """ `OSError` class, derived from `Error` """
176 def __init__(self, message, errno=None):
177 super(OSError, self).__init__(message)
181 msg = super(OSError, self).__str__()
182 if self.errno is None:
184 return '[errno {0}] {1}'.format(self.errno, msg)
186 def __reduce__(self):
187 return (self.__class__, (self.message, self.errno))
189 class PermissionError(OSError):
190 def __init__(self, message, errno=None):
191 super(PermissionError, self).__init__(
192 "RBD permission error (%s)" % message, errno)
195 class ImageNotFound(OSError):
196 def __init__(self, message, errno=None):
197 super(ImageNotFound, self).__init__(
198 "RBD image not found (%s)" % message, errno)
201 class ObjectNotFound(OSError):
202 def __init__(self, message, errno=None):
203 super(ObjectNotFound, self).__init__(
204 "RBD object not found (%s)" % message, errno)
207 class ImageExists(OSError):
208 def __init__(self, message, errno=None):
209 super(ImageExists, self).__init__(
210 "RBD image already exists (%s)" % message, errno)
213 class ObjectExists(OSError):
214 def __init__(self, message, errno=None):
215 super(ObjectExists, self).__init__(
216 "RBD object already exists (%s)" % message, errno)
219 class IOError(OSError):
220 def __init__(self, message, errno=None):
221 super(IOError, self).__init__(
222 "RBD I/O error (%s)" % message, errno)
225 class NoSpace(OSError):
226 def __init__(self, message, errno=None):
227 super(NoSpace, self).__init__(
228 "RBD insufficient space available (%s)" % message, errno)
231 class IncompleteWriteError(OSError):
232 def __init__(self, message, errno=None):
233 super(IncompleteWriteError, self).__init__(
234 "RBD incomplete write (%s)" % message, errno)
237 class InvalidArgument(OSError):
238 def __init__(self, message, errno=None):
239 super(InvalidArgument, self).__init__(
240 "RBD invalid argument (%s)" % message, errno)
243 class LogicError(OSError):
244 def __init__(self, message, errno=None):
245 super(LogicError, self).__init__(
246 "RBD logic error (%s)" % message, errno)
249 class ReadOnlyImage(OSError):
250 def __init__(self, message, errno=None):
251 super(ReadOnlyImage, self).__init__(
252 "RBD read-only image (%s)" % message, errno)
255 class ImageBusy(OSError):
256 def __init__(self, message, errno=None):
257 super(ImageBusy, self).__init__(
258 "RBD image is busy (%s)" % message, errno)
261 class ImageHasSnapshots(OSError):
262 def __init__(self, message, errno=None):
263 super(ImageHasSnapshots, self).__init__(
264 "RBD image has snapshots (%s)" % message, errno)
267 class FunctionNotSupported(OSError):
268 def __init__(self, message, errno=None):
269 super(FunctionNotSupported, self).__init__(
270 "RBD function not supported (%s)" % message, errno)
273 class ArgumentOutOfRange(OSError):
274 def __init__(self, message, errno=None):
275 super(ArgumentOutOfRange, self).__init__(
276 "RBD arguments out of range (%s)" % message, errno)
279 class ConnectionShutdown(OSError):
280 def __init__(self, message, errno=None):
281 super(ConnectionShutdown, self).__init__(
282 "RBD connection was shutdown (%s)" % message, errno)
285 class Timeout(OSError):
286 def __init__(self, message, errno=None):
287 super(Timeout, self).__init__(
288 "RBD operation timeout (%s)" % message, errno)
291 class DiskQuotaExceeded(OSError):
292 def __init__(self, message, errno=None):
293 super(DiskQuotaExceeded, self).__init__(
294 "RBD disk quota exceeded (%s)" % message, errno)
296 class OperationNotSupported(OSError):
297 def __init__(self, message, errno=None):
298 super(OperationNotSupported, self).__init__(
299 "RBD operation not supported (%s)" % message, errno)
301 class OperationCanceled(OSError):
302 def __init__(self, message, errno=None):
303 super(OperationCanceled, self).__init__(
304 "RBD operation canceled (%s)" % message, errno)
306 cdef errno_to_exception = {
307 errno.EPERM : PermissionError,
308 errno.ENOENT : ImageNotFound,
310 errno.ENOSPC : NoSpace,
311 errno.EEXIST : ImageExists,
312 errno.EINVAL : InvalidArgument,
313 errno.EROFS : ReadOnlyImage,
314 errno.EBUSY : ImageBusy,
315 errno.ENOTEMPTY : ImageHasSnapshots,
316 errno.ENOSYS : FunctionNotSupported,
317 errno.EDOM : ArgumentOutOfRange,
318 errno.ESHUTDOWN : ConnectionShutdown,
319 errno.ETIMEDOUT : Timeout,
320 errno.EDQUOT : DiskQuotaExceeded,
321 errno.EOPNOTSUPP : OperationNotSupported,
322 ECANCELED : OperationCanceled,
325 cdef group_errno_to_exception = {
326 errno.EPERM : PermissionError,
327 errno.ENOENT : ObjectNotFound,
329 errno.ENOSPC : NoSpace,
330 errno.EEXIST : ObjectExists,
331 errno.EINVAL : InvalidArgument,
332 errno.EROFS : ReadOnlyImage,
333 errno.EBUSY : ImageBusy,
334 errno.ENOTEMPTY : ImageHasSnapshots,
335 errno.ENOSYS : FunctionNotSupported,
336 errno.EDOM : ArgumentOutOfRange,
337 errno.ESHUTDOWN : ConnectionShutdown,
338 errno.ETIMEDOUT : Timeout,
339 errno.EDQUOT : DiskQuotaExceeded,
340 errno.EOPNOTSUPP : OperationNotSupported,
341 ECANCELED : OperationCanceled,
344 cdef make_ex(ret, msg, exception_map=errno_to_exception):
346 Translate a librbd return code into an exception.
348 :param ret: the return code
350 :param msg: the error message to use
352 :returns: a subclass of :class:`Error`
355 if ret in exception_map:
356 return exception_map[ret](msg, errno=ret)
358 return OSError(msg, errno=ret)
362 cdef rados_t convert_rados(rados) nogil:
365 cdef rados_ioctx_t convert_ioctx(ioctx) nogil:
366 return <rados_ioctx_t>0
368 cdef rados_t convert_rados(rados.Rados rados) except? NULL:
369 return <rados_t>rados.cluster
371 cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL:
372 return <rados_ioctx_t>ioctx.io
374 cdef int progress_callback(uint64_t offset, uint64_t total, void* ptr) with gil:
375 return (<object>ptr)(offset, total)
377 cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr):
380 def cstr(val, name, encoding="utf-8", opt=False):
382 Create a byte string from a Python string
384 :param basestring val: Python string
385 :param str name: Name of the string parameter, for exceptions
386 :param str encoding: Encoding to use
387 :param bool opt: If True, None is allowed
389 :raises: :class:`InvalidArgument`
391 if opt and val is None:
393 if isinstance(val, bytes):
395 elif isinstance(val, str):
396 return val.encode(encoding)
398 raise InvalidArgument('%s must be a string' % name)
400 def decode_cstr(val, encoding="utf-8"):
402 Decode a byte string into a Python string.
404 :param bytes val: byte string
410 return val.decode(encoding)
413 cdef char* opt_str(s) except? NULL:
418 cdef void* realloc_chk(void* ptr, size_t size) except NULL:
419 cdef void *ret = realloc(ptr, size)
421 raise MemoryError("realloc failed")
424 RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST = decode_cstr(_RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST)
425 RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY = decode_cstr(_RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY)
427 cdef class Completion
429 cdef void __aio_complete_cb(rbd_completion_t completion, void *args) with gil:
431 Callback to oncomplete() for asynchronous operations
433 cdef Completion cb = <Completion>args
437 cdef class Completion(object):
438 """completion object"""
443 rbd_completion_t rbd_comp
448 def __cinit__(self, image, object oncomplete):
449 self.oncomplete = oncomplete
451 self.persisted = False
453 def is_complete(self):
455 Has an asynchronous operation completed?
457 This does not imply that the callback has finished.
459 :returns: True if the operation is completed
462 ret = rbd_aio_is_complete(self.rbd_comp)
465 def wait_for_complete_and_cb(self):
467 Wait for an asynchronous operation to complete
469 This method waits for the callback to execute, if one was provided.
470 It will also re-raise any exceptions raised by the callback. You
471 should call this to "reap" asynchronous completions and ensure that
472 any exceptions in the callbacks are handled, as an exception internal
473 to this module may have occurred.
476 rbd_aio_wait_for_complete(self.rbd_comp)
479 raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
481 def get_return_value(self):
483 Get the return value of an asychronous operation
485 The return value is set when the operation is complete.
487 :returns: int - return value of the operation
490 ret = rbd_aio_get_return_value(self.rbd_comp)
493 def __dealloc__(self):
497 This is automatically called when the completion object is freed.
499 ref.Py_XDECREF(self.buf)
501 if self.rbd_comp != NULL:
503 rbd_aio_release(self.rbd_comp)
506 cdef void _complete(self):
510 self.oncomplete(self)
511 # In the event that something raises an exception during the next 2
512 # lines of code, we will not be able to catch it, and this may result
513 # in the app not noticing a failed callback. However, this should only
514 # happen in extreme circumstances (OOM, etc.). KeyboardInterrupt
515 # should not be a problem because the callback thread from librbd
516 # ought to have SIGINT blocked.
518 self.exc_info = sys.exc_info()
520 cdef __persist(self):
521 if self.oncomplete is not None and not self.persisted:
522 # Increment our own reference count to make sure the completion
523 # is not freed until the callback is called. The completion is
524 # allowed to be freed if there is no callback.
526 self.persisted = True
528 cdef __unpersist(self):
531 self.persisted = False
536 This class wraps librbd CRUD functions.
540 Get the version number of the ``librbd`` C library.
542 :returns: a tuple of ``(major, minor, extra)`` components of the
548 rbd_version(&major, &minor, &extra)
549 return (major, minor, extra)
551 def create(self, ioctx, name, size, order=None, old_format=False,
552 features=None, stripe_unit=None, stripe_count=None,
557 :param ioctx: the context in which to create the image
558 :type ioctx: :class:`rados.Ioctx`
559 :param name: what the image is called
561 :param size: how big the image is in bytes
563 :param order: the image is split into (2**order) byte objects
565 :param old_format: whether to create an old-style image that
566 is accessible by old clients, but can't
567 use more advanced features like layering.
568 :type old_format: bool
569 :param features: bitmask of features to enable
571 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
572 :type stripe_unit: int
573 :param stripe_count: objects to stripe over before looping
574 :type stripe_count: int
575 :param data_pool: optional separate pool for data blocks
577 :raises: :class:`ImageExists`
578 :raises: :class:`TypeError`
579 :raises: :class:`InvalidArgument`
580 :raises: :class:`FunctionNotSupported`
582 name = cstr(name, 'name')
583 data_pool = cstr(data_pool, 'data_pool', opt=True)
585 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
587 uint64_t _size = size
589 rbd_image_options_t opts
590 if order is not None:
594 ((stripe_unit is not None) and stripe_unit != 0) or
595 ((stripe_count is not None) and stripe_count != 0) or
597 raise InvalidArgument('format 1 images do not support feature '
598 'masks, non-default striping, nor data '
601 ret = rbd_create(_ioctx, _name, _size, &_order)
603 rbd_image_options_create(&opts)
605 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FORMAT,
606 1 if old_format else 2)
607 if features is not None:
608 rbd_image_options_set_uint64(opts,
609 RBD_IMAGE_OPTION_FEATURES,
611 if order is not None:
612 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
614 if stripe_unit is not None:
615 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
617 if stripe_count is not None:
618 rbd_image_options_set_uint64(opts,
619 RBD_IMAGE_OPTION_STRIPE_COUNT,
621 if data_pool is not None:
622 rbd_image_options_set_string(opts,
623 RBD_IMAGE_OPTION_DATA_POOL,
626 ret = rbd_create4(_ioctx, _name, _size, opts)
628 rbd_image_options_destroy(opts)
630 raise make_ex(ret, 'error creating image')
632 def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name,
633 features=None, order=None, stripe_unit=None, stripe_count=None,
636 Clone a parent rbd snapshot into a COW sparse child.
638 :param p_ioctx: the parent context that represents the parent snap
639 :type ioctx: :class:`rados.Ioctx`
640 :param p_name: the parent image name
642 :param p_snapname: the parent image snapshot name
644 :param c_ioctx: the child context that represents the new clone
645 :type ioctx: :class:`rados.Ioctx`
646 :param c_name: the clone (child) name
648 :param features: bitmask of features to enable; if set, must include layering
650 :param order: the image is split into (2**order) byte objects
652 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
653 :type stripe_unit: int
654 :param stripe_count: objects to stripe over before looping
655 :type stripe_count: int
656 :param data_pool: optional separate pool for data blocks
658 :raises: :class:`TypeError`
659 :raises: :class:`InvalidArgument`
660 :raises: :class:`ImageExists`
661 :raises: :class:`FunctionNotSupported`
662 :raises: :class:`ArgumentOutOfRange`
664 p_snapname = cstr(p_snapname, 'p_snapname')
665 p_name = cstr(p_name, 'p_name')
666 c_name = cstr(c_name, 'c_name')
667 data_pool = cstr(data_pool, 'data_pool', opt=True)
669 rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx)
670 rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx)
671 char *_p_name = p_name
672 char *_p_snapname = p_snapname
673 char *_c_name = c_name
674 rbd_image_options_t opts
676 rbd_image_options_create(&opts)
678 if features is not None:
679 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
681 if order is not None:
682 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
684 if stripe_unit is not None:
685 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
687 if stripe_count is not None:
688 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
690 if data_pool is not None:
691 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
694 ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname,
695 _c_ioctx, _c_name, opts)
697 rbd_image_options_destroy(opts)
699 raise make_ex(ret, 'error creating clone')
701 def list(self, ioctx):
705 :param ioctx: determines which RADOS pool is read
706 :type ioctx: :class:`rados.Ioctx`
707 :returns: list -- a list of image names
710 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
715 c_names = <char *>realloc_chk(c_names, size)
717 ret = rbd_list(_ioctx, c_names, &size)
720 elif ret != -errno.ERANGE:
721 raise make_ex(ret, 'error listing images')
722 return [decode_cstr(name) for name in c_names[:ret].split(b'\0')
727 def list2(self, ioctx):
729 Iterate over the images in the pool.
731 :param ioctx: determines which RADOS pool the image is in
732 :type ioctx: :class:`rados.Ioctx`
733 :returns: :class:`ImageIterator`
735 return ImageIterator(ioctx)
737 def remove(self, ioctx, name, on_progress=None):
739 Delete an RBD image. This may take a long time, since it does
740 not return until every object that comprises the image has
741 been deleted. Note that all snapshots must be deleted before
742 the image can be removed. If there are snapshots left,
743 :class:`ImageHasSnapshots` is raised. If the image is still
744 open, or the watch from a crashed client has not expired,
745 :class:`ImageBusy` is raised.
747 :param ioctx: determines which RADOS pool the image is in
748 :type ioctx: :class:`rados.Ioctx`
749 :param name: the name of the image to remove
751 :param on_progress: optional progress callback function
752 :type on_progress: callback function
753 :raises: :class:`ImageNotFound`, :class:`ImageBusy`,
754 :class:`ImageHasSnapshots`
756 name = cstr(name, 'name')
758 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
760 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
761 void *_prog_arg = NULL
763 _prog_cb = &progress_callback
764 _prog_arg = <void *>on_progress
766 ret = rbd_remove_with_progress(_ioctx, _name, _prog_cb, _prog_arg)
768 raise make_ex(ret, 'error removing image')
770 def rename(self, ioctx, src, dest):
774 :param ioctx: determines which RADOS pool the image is in
775 :type ioctx: :class:`rados.Ioctx`
776 :param src: the current name of the image
778 :param dest: the new name of the image
780 :raises: :class:`ImageNotFound`, :class:`ImageExists`
782 src = cstr(src, 'src')
783 dest = cstr(dest, 'dest')
785 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
789 ret = rbd_rename(_ioctx, _src, _dest)
791 raise make_ex(ret, 'error renaming image')
793 def trash_move(self, ioctx, name, delay=0):
795 Move an RBD image to the trash.
797 :param ioctx: determines which RADOS pool the image is in
798 :type ioctx: :class:`rados.Ioctx`
799 :param name: the name of the image to remove
801 :param delay: time delay in seconds before the image can be deleted
804 :raises: :class:`ImageNotFound`
806 name = cstr(name, 'name')
808 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
810 uint64_t _delay = delay
812 ret = rbd_trash_move(_ioctx, _name, _delay)
814 raise make_ex(ret, 'error moving image to trash')
816 def trash_purge(self, ioctx, expire_ts=None, threshold=-1):
818 Delete RBD images from trash in bulk.
820 By default it removes images with deferment end time less than now.
822 The timestamp is configurable, e.g. delete images that have expired a
825 If the threshold is used it deletes images until X% pool usage is met.
827 :param ioctx: determines which RADOS pool the image is in
828 :type ioctx: :class:`rados.Ioctx`
829 :param expire_ts: timestamp for images to be considered as expired (UTC)
830 :type expire_ts: datetime
831 :param threshold: percentage of pool usage to be met (0 to 1)
832 :type threshold: float
835 expire_epoch_ts = time.mktime(expire_ts.timetuple())
840 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
841 time_t _expire_ts = expire_epoch_ts
842 float _threshold = threshold
844 ret = rbd_trash_purge(_ioctx, _expire_ts, _threshold)
846 raise make_ex(ret, 'error purging images from trash')
848 def trash_remove(self, ioctx, image_id, force=False, on_progress=None):
850 Delete an RBD image from trash. If image deferment time has not
851 expired :class:`PermissionError` is raised.
853 :param ioctx: determines which RADOS pool the image is in
854 :type ioctx: :class:`rados.Ioctx`
855 :param image_id: the id of the image to remove
857 :param force: force remove even if deferment time has not expired
859 :param on_progress: optional progress callback function
860 :type on_progress: callback function
861 :raises: :class:`ImageNotFound`, :class:`PermissionError`
863 image_id = cstr(image_id, 'image_id')
865 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
866 char *_image_id = image_id
868 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
869 void *_prog_arg = NULL
871 _prog_cb = &progress_callback
872 _prog_arg = <void *>on_progress
874 ret = rbd_trash_remove_with_progress(_ioctx, _image_id, _force,
877 raise make_ex(ret, 'error deleting image from trash')
879 def trash_get(self, ioctx, image_id):
881 Retrieve RBD image info from trash.
883 :param ioctx: determines which RADOS pool the image is in
884 :type ioctx: :class:`rados.Ioctx`
885 :param image_id: the id of the image to restore
887 :returns: dict - contains the following keys:
889 * ``id`` (str) - image id
891 * ``name`` (str) - image name
893 * ``source`` (str) - source of deletion
895 * ``deletion_time`` (datetime) - time of deletion
897 * ``deferment_end_time`` (datetime) - time that an image is allowed
898 to be removed from trash
900 :raises: :class:`ImageNotFound`
902 image_id = cstr(image_id, 'image_id')
904 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
905 char *_image_id = image_id
906 rbd_trash_image_info_t c_info
908 ret = rbd_trash_get(_ioctx, _image_id, &c_info)
910 raise make_ex(ret, 'error retrieving image from trash')
912 __source_string = ['USER', 'MIRRORING', 'MIGRATION', 'REMOVING']
914 'id' : decode_cstr(c_info.id),
915 'name' : decode_cstr(c_info.name),
916 'source' : __source_string[c_info.source],
917 'deletion_time' : datetime.utcfromtimestamp(c_info.deletion_time),
918 'deferment_end_time' : datetime.utcfromtimestamp(c_info.deferment_end_time)
920 rbd_trash_get_cleanup(&c_info)
923 def trash_list(self, ioctx):
925 List all entries from trash.
927 :param ioctx: determines which RADOS pool the image is in
928 :type ioctx: :class:`rados.Ioctx`
929 :returns: :class:`TrashIterator`
931 return TrashIterator(ioctx)
933 def trash_restore(self, ioctx, image_id, name):
935 Restore an RBD image from trash.
937 :param ioctx: determines which RADOS pool the image is in
938 :type ioctx: :class:`rados.Ioctx`
939 :param image_id: the id of the image to restore
941 :param name: the new name of the restored image
943 :raises: :class:`ImageNotFound`
945 image_id = cstr(image_id, 'image_id')
946 name = cstr(name, 'name')
948 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
949 char *_image_id = image_id
952 ret = rbd_trash_restore(_ioctx, _image_id, _name)
954 raise make_ex(ret, 'error restoring image from trash')
956 def migration_prepare(self, ioctx, image_name, dest_ioctx, dest_image_name,
957 features=None, order=None, stripe_unit=None, stripe_count=None,
960 Prepare an RBD image migration.
962 :param ioctx: determines which RADOS pool the image is in
963 :type ioctx: :class:`rados.Ioctx`
964 :param image_name: the current name of the image
966 :param dest_ioctx: determines which pool to migration into
967 :type dest_ioctx: :class:`rados.Ioctx`
968 :param dest_image_name: the name of the destination image (may be the same image)
969 :type dest_image_name: str
970 :param features: bitmask of features to enable; if set, must include layering
972 :param order: the image is split into (2**order) byte objects
974 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
975 :type stripe_unit: int
976 :param stripe_count: objects to stripe over before looping
977 :type stripe_count: int
978 :param data_pool: optional separate pool for data blocks
980 :raises: :class:`TypeError`
981 :raises: :class:`InvalidArgument`
982 :raises: :class:`ImageExists`
983 :raises: :class:`FunctionNotSupported`
984 :raises: :class:`ArgumentOutOfRange`
986 image_name = cstr(image_name, 'image_name')
987 dest_image_name = cstr(dest_image_name, 'dest_image_name')
989 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
990 char *_image_name = image_name
991 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
992 char *_dest_image_name = dest_image_name
993 rbd_image_options_t opts
995 rbd_image_options_create(&opts)
997 if features is not None:
998 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
1000 if order is not None:
1001 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
1003 if stripe_unit is not None:
1004 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
1006 if stripe_count is not None:
1007 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
1009 if data_pool is not None:
1010 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
1013 ret = rbd_migration_prepare(_ioctx, _image_name, _dest_ioctx,
1014 _dest_image_name, opts)
1016 rbd_image_options_destroy(opts)
1018 raise make_ex(ret, 'error migrating image %s' % (image_name))
1020 def migration_prepare_import(self, source_spec, dest_ioctx, dest_image_name,
1021 features=None, order=None, stripe_unit=None,
1022 stripe_count=None, data_pool=None):
1024 Prepare an RBD image migration.
1026 :param source_spec: JSON-encoded source-spec
1027 :type source_spec: str
1028 :param dest_ioctx: determines which pool to migration into
1029 :type dest_ioctx: :class:`rados.Ioctx`
1030 :param dest_image_name: the name of the destination image (may be the same image)
1031 :type dest_image_name: str
1032 :param features: bitmask of features to enable; if set, must include layering
1034 :param order: the image is split into (2**order) byte objects
1036 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
1037 :type stripe_unit: int
1038 :param stripe_count: objects to stripe over before looping
1039 :type stripe_count: int
1040 :param data_pool: optional separate pool for data blocks
1041 :type data_pool: str
1042 :raises: :class:`TypeError`
1043 :raises: :class:`InvalidArgument`
1044 :raises: :class:`ImageExists`
1045 :raises: :class:`FunctionNotSupported`
1046 :raises: :class:`ArgumentOutOfRange`
1048 source_spec = cstr(source_spec, 'source_spec')
1049 dest_image_name = cstr(dest_image_name, 'dest_image_name')
1051 char *_source_spec = source_spec
1052 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
1053 char *_dest_image_name = dest_image_name
1054 rbd_image_options_t opts
1056 rbd_image_options_create(&opts)
1058 if features is not None:
1059 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
1061 if order is not None:
1062 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
1064 if stripe_unit is not None:
1065 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
1067 if stripe_count is not None:
1068 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
1070 if data_pool is not None:
1071 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
1074 ret = rbd_migration_prepare_import(_source_spec, _dest_ioctx,
1075 _dest_image_name, opts)
1077 rbd_image_options_destroy(opts)
1079 raise make_ex(ret, 'error migrating image %s' % (source_spec))
1081 def migration_execute(self, ioctx, image_name, on_progress=None):
1083 Execute a prepared RBD image migration.
1085 :param ioctx: determines which RADOS pool the image is in
1086 :type ioctx: :class:`rados.Ioctx`
1087 :param image_name: the name of the image
1088 :type image_name: str
1089 :param on_progress: optional progress callback function
1090 :type on_progress: callback function
1091 :raises: :class:`ImageNotFound`
1093 image_name = cstr(image_name, 'image_name')
1095 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1096 char *_image_name = image_name
1097 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
1098 void *_prog_arg = NULL
1100 _prog_cb = &progress_callback
1101 _prog_arg = <void *>on_progress
1103 ret = rbd_migration_execute_with_progress(_ioctx, _image_name,
1104 _prog_cb, _prog_arg)
1106 raise make_ex(ret, 'error aborting migration')
1108 def migration_commit(self, ioctx, image_name, on_progress=None):
1110 Commit an executed RBD image migration.
1112 :param ioctx: determines which RADOS pool the image is in
1113 :type ioctx: :class:`rados.Ioctx`
1114 :param image_name: the name of the image
1115 :type image_name: str
1116 :param on_progress: optional progress callback function
1117 :type on_progress: callback function
1118 :raises: :class:`ImageNotFound`
1120 image_name = cstr(image_name, 'image_name')
1122 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1123 char *_image_name = image_name
1124 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
1125 void *_prog_arg = NULL
1127 _prog_cb = &progress_callback
1128 _prog_arg = <void *>on_progress
1130 ret = rbd_migration_commit_with_progress(_ioctx, _image_name,
1131 _prog_cb, _prog_arg)
1133 raise make_ex(ret, 'error aborting migration')
1135 def migration_abort(self, ioctx, image_name, on_progress=None):
1137 Cancel a previously started but interrupted migration.
1139 :param ioctx: determines which RADOS pool the image is in
1140 :type ioctx: :class:`rados.Ioctx`
1141 :param image_name: the name of the image
1142 :type image_name: str
1143 :param on_progress: optional progress callback function
1144 :type on_progress: callback function
1145 :raises: :class:`ImageNotFound`
1147 image_name = cstr(image_name, 'image_name')
1149 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1150 char *_image_name = image_name
1151 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
1152 void *_prog_arg = NULL
1154 _prog_cb = &progress_callback
1155 _prog_arg = <void *>on_progress
1157 ret = rbd_migration_abort_with_progress(_ioctx, _image_name,
1158 _prog_cb, _prog_arg)
1160 raise make_ex(ret, 'error aborting migration')
1162 def migration_status(self, ioctx, image_name):
1164 Return RBD image migration status.
1166 :param ioctx: determines which RADOS pool the image is in
1167 :type ioctx: :class:`rados.Ioctx`
1168 :param image_name: the name of the image
1169 :type image_name: str
1170 :returns: dict - contains the following keys:
1172 * ``source_pool_id`` (int) - source image pool id
1174 * ``source_pool_namespace`` (str) - source image pool namespace
1176 * ``source_image_name`` (str) - source image name
1178 * ``source_image_id`` (str) - source image id
1180 * ``dest_pool_id`` (int) - destination image pool id
1182 * ``dest_pool_namespace`` (str) - destination image pool namespace
1184 * ``dest_image_name`` (str) - destination image name
1186 * ``dest_image_id`` (str) - destination image id
1188 * ``state`` (int) - current migration state
1190 * ``state_description`` (str) - migration state description
1192 :raises: :class:`ImageNotFound`
1194 image_name = cstr(image_name, 'image_name')
1196 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1197 char *_image_name = image_name
1198 rbd_image_migration_status_t c_status
1200 ret = rbd_migration_status(_ioctx, _image_name, &c_status,
1203 raise make_ex(ret, 'error getting migration status')
1206 'source_pool_id' : c_status.source_pool_id,
1207 'source_pool_namespace' : decode_cstr(c_status.source_pool_namespace),
1208 'source_image_name' : decode_cstr(c_status.source_image_name),
1209 'source_image_id' : decode_cstr(c_status.source_image_id),
1210 'dest_pool_id' : c_status.source_pool_id,
1211 'dest_pool_namespace' : decode_cstr(c_status.dest_pool_namespace),
1212 'dest_image_name' : decode_cstr(c_status.dest_image_name),
1213 'dest_image_id' : decode_cstr(c_status.dest_image_id),
1214 'state' : c_status.state,
1215 'state_description' : decode_cstr(c_status.state_description)
1218 rbd_migration_status_cleanup(&c_status)
1222 def mirror_site_name_get(self, rados):
1224 Get the local cluster's friendly site name
1226 :param rados: cluster connection
1227 :type rados: :class: rados.Rados
1228 :returns: str - local site name
1231 rados_t _rados = convert_rados(rados)
1232 char *_site_name = NULL
1233 size_t _max_size = 512
1236 _site_name = <char *>realloc_chk(_site_name, _max_size)
1238 ret = rbd_mirror_site_name_get(_rados, _site_name,
1242 elif ret != -errno.ERANGE:
1243 raise make_ex(ret, 'error getting site name')
1244 return decode_cstr(_site_name)
1248 def mirror_site_name_set(self, rados, site_name):
1250 Set the local cluster's friendly site name
1252 :param rados: cluster connection
1253 :type rados: :class: rados.Rados
1254 :param site_name: friendly site name
1257 site_name = cstr(site_name, 'site_name')
1259 rados_t _rados = convert_rados(rados)
1260 char *_site_name = site_name
1262 ret = rbd_mirror_site_name_set(_rados, _site_name)
1264 raise make_ex(ret, 'error setting mirror site name')
1266 def mirror_mode_get(self, ioctx):
1268 Get pool mirror mode.
1270 :param ioctx: determines which RADOS pool is read
1271 :type ioctx: :class:`rados.Ioctx`
1272 :returns: int - pool mirror mode
1275 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1276 rbd_mirror_mode_t mirror_mode
1278 ret = rbd_mirror_mode_get(_ioctx, &mirror_mode)
1280 raise make_ex(ret, 'error getting mirror mode')
1283 def mirror_mode_set(self, ioctx, mirror_mode):
1285 Set pool mirror mode.
1287 :param ioctx: determines which RADOS pool is written
1288 :type ioctx: :class:`rados.Ioctx`
1289 :param mirror_mode: mirror mode to set
1290 :type mirror_mode: int
1293 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1294 rbd_mirror_mode_t _mirror_mode = mirror_mode
1296 ret = rbd_mirror_mode_set(_ioctx, _mirror_mode)
1298 raise make_ex(ret, 'error setting mirror mode')
1300 def mirror_uuid_get(self, ioctx):
1302 Get pool mirror uuid
1304 :param ioctx: determines which RADOS pool is read
1305 :type ioctx: :class:`rados.Ioctx`
1306 :returns: ste - pool mirror uuid
1309 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1311 size_t _max_size = 512
1314 _uuid = <char *>realloc_chk(_uuid, _max_size)
1316 ret = rbd_mirror_uuid_get(_ioctx, _uuid, &_max_size)
1319 elif ret != -errno.ERANGE:
1320 raise make_ex(ret, 'error retrieving mirror uuid')
1321 return decode_cstr(_uuid)
1325 def mirror_peer_bootstrap_create(self, ioctx):
1327 Creates a new RBD mirroring bootstrap token for an
1330 :param ioctx: determines which RADOS pool is written
1331 :type ioctx: :class:`rados.Ioctx`
1332 :returns: str - bootstrap token
1335 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1337 size_t _max_size = 512
1340 _token = <char *>realloc_chk(_token, _max_size)
1342 ret = rbd_mirror_peer_bootstrap_create(_ioctx, _token,
1346 elif ret != -errno.ERANGE:
1347 raise make_ex(ret, 'error creating bootstrap token')
1348 return decode_cstr(_token)
1352 def mirror_peer_bootstrap_import(self, ioctx, direction, token):
1354 Import a bootstrap token from an external cluster to
1355 auto-configure the mirror peer.
1357 :param ioctx: determines which RADOS pool is written
1358 :type ioctx: :class:`rados.Ioctx`
1359 :param direction: mirror peer direction
1360 :type direction: int
1361 :param token: bootstrap token
1364 token = cstr(token, 'token')
1366 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1367 rbd_mirror_peer_direction_t _direction = direction
1368 char *_token = token
1370 ret = rbd_mirror_peer_bootstrap_import(_ioctx, _direction, _token)
1372 raise make_ex(ret, 'error importing bootstrap token')
1374 def mirror_peer_add(self, ioctx, site_name, client_name,
1375 direction=RBD_MIRROR_PEER_DIRECTION_RX_TX):
1379 :param ioctx: determines which RADOS pool is used
1380 :type ioctx: :class:`rados.Ioctx`
1381 :param site_name: mirror peer site name
1382 :type site_name: str
1383 :param client_name: mirror peer client name
1384 :type client_name: str
1385 :param direction: the direction of the mirroring
1386 :type direction: int
1387 :returns: str - peer uuid
1389 site_name = cstr(site_name, 'site_name')
1390 client_name = cstr(client_name, 'client_name')
1392 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1394 size_t _uuid_max_length = 512
1395 rbd_mirror_peer_direction_t _direction = direction
1396 char *_site_name = site_name
1397 char *_client_name = client_name
1399 _uuid = <char *>realloc_chk(_uuid, _uuid_max_length)
1401 ret = rbd_mirror_peer_site_add(_ioctx, _uuid, _uuid_max_length,
1402 _direction, _site_name,
1405 raise make_ex(ret, 'error adding mirror peer')
1406 return decode_cstr(_uuid)
1410 def mirror_peer_remove(self, ioctx, uuid):
1414 :param ioctx: determines which RADOS pool is used
1415 :type ioctx: :class:`rados.Ioctx`
1416 :param uuid: peer uuid
1419 uuid = cstr(uuid, 'uuid')
1421 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1424 ret = rbd_mirror_peer_site_remove(_ioctx, _uuid)
1426 raise make_ex(ret, 'error removing mirror peer')
1428 def mirror_peer_list(self, ioctx):
1430 Iterate over the peers of a pool.
1432 :param ioctx: determines which RADOS pool is read
1433 :type ioctx: :class:`rados.Ioctx`
1434 :returns: :class:`MirrorPeerIterator`
1436 return MirrorPeerIterator(ioctx)
1438 def mirror_peer_set_client(self, ioctx, uuid, client_name):
1440 Set mirror peer client name
1442 :param ioctx: determines which RADOS pool is written
1443 :type ioctx: :class:`rados.Ioctx`
1444 :param uuid: uuid of the mirror peer
1446 :param client_name: client name of the mirror peer to set
1447 :type client_name: str
1449 uuid = cstr(uuid, 'uuid')
1450 client_name = cstr(client_name, 'client_name')
1452 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1454 char *_client_name = client_name
1456 ret = rbd_mirror_peer_site_set_client_name(_ioctx, _uuid,
1459 raise make_ex(ret, 'error setting mirror peer client name')
1461 def mirror_peer_set_name(self, ioctx, uuid, site_name):
1463 Set mirror peer site name
1465 :param ioctx: determines which RADOS pool is written
1466 :type ioctx: :class:`rados.Ioctx`
1467 :param uuid: uuid of the mirror peer
1469 :param site_name: site name of the mirror peer to set
1470 :type site_name: str
1472 uuid = cstr(uuid, 'uuid')
1473 site_name = cstr(site_name, 'site_name')
1475 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1477 char *_site_name = site_name
1479 ret = rbd_mirror_peer_site_set_name(_ioctx, _uuid, _site_name)
1481 raise make_ex(ret, 'error setting mirror peer site name')
1483 def mirror_peer_set_cluster(self, ioctx, uuid, cluster_name):
1484 self.mirror_peer_set_name(ioctx, uuid, cluster_name)
1486 def mirror_peer_get_attributes(self, ioctx, uuid):
1488 Get optional mirror peer attributes
1490 :param ioctx: determines which RADOS pool is written
1491 :type ioctx: :class:`rados.Ioctx`
1492 :param uuid: uuid of the mirror peer
1495 :returns: dict - contains the following keys:
1497 * ``mon_host`` (str) - monitor addresses
1499 * ``key`` (str) - CephX key
1501 uuid = cstr(uuid, 'uuid')
1503 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1507 size_t _keys_size = 512
1508 size_t _vals_size = 512
1512 _keys = <char *>realloc_chk(_keys, _keys_size)
1513 _vals = <char *>realloc_chk(_vals, _vals_size)
1515 ret = rbd_mirror_peer_site_get_attributes(
1516 _ioctx, _uuid, _keys, &_keys_size, _vals, &_vals_size,
1520 elif ret != -errno.ERANGE:
1521 raise make_ex(ret, 'error getting mirror peer attributes')
1522 keys = [decode_cstr(x) for x in _keys[:_keys_size].split(b'\0')[:-1]]
1523 vals = [decode_cstr(x) for x in _vals[:_vals_size].split(b'\0')[:-1]]
1524 return dict(zip(keys, vals))
1529 def mirror_peer_set_attributes(self, ioctx, uuid, attributes):
1531 Set optional mirror peer attributes
1533 :param ioctx: determines which RADOS pool is written
1534 :type ioctx: :class:`rados.Ioctx`
1535 :param uuid: uuid of the mirror peer
1537 :param attributes: 'mon_host' and 'key' attributes
1538 :type attributes: dict
1540 uuid = cstr(uuid, 'uuid')
1541 keys_str = b'\0'.join([cstr(x[0], 'key') for x in attributes.items()])
1542 vals_str = b'\0'.join([cstr(x[1], 'val') for x in attributes.items()])
1544 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1546 char *_keys = keys_str
1547 char *_vals = vals_str
1548 size_t _count = len(attributes)
1551 ret = rbd_mirror_peer_site_set_attributes(_ioctx, _uuid, _keys,
1554 raise make_ex(ret, 'error setting mirror peer attributes')
1556 def mirror_image_status_list(self, ioctx):
1558 Iterate over the mirror image statuses of a pool.
1560 :param ioctx: determines which RADOS pool is read
1561 :type ioctx: :class:`rados.Ioctx`
1562 :returns: :class:`MirrorImageStatusIterator`
1564 return MirrorImageStatusIterator(ioctx)
1566 def mirror_image_status_summary(self, ioctx):
1568 Get mirror image status summary of a pool.
1570 :param ioctx: determines which RADOS pool is read
1571 :type ioctx: :class:`rados.Ioctx`
1572 :returns: list - a list of (state, count) tuples
1575 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1576 rbd_mirror_image_status_state_t *states = NULL
1580 states = <rbd_mirror_image_status_state_t *>realloc_chk(states,
1581 sizeof(rbd_mirror_image_status_state_t) * maxlen)
1582 counts = <int *>realloc_chk(counts, sizeof(int) * maxlen)
1584 ret = rbd_mirror_image_status_summary(_ioctx, states, counts,
1587 raise make_ex(ret, 'error getting mirror image status summary')
1588 return [(states[i], counts[i]) for i in range(maxlen)]
1593 def mirror_image_instance_id_list(self, ioctx):
1595 Iterate over the mirror image instance ids of a pool.
1597 :param ioctx: determines which RADOS pool is read
1598 :type ioctx: :class:`rados.Ioctx`
1599 :returns: :class:`MirrorImageInstanceIdIterator`
1601 return MirrorImageInstanceIdIterator(ioctx)
1603 def mirror_image_info_list(self, ioctx, mode_filter=None):
1605 Iterate over the mirror image instance ids of a pool.
1607 :param ioctx: determines which RADOS pool is read
1608 :param mode_filter: list images in this image mirror mode
1609 :type ioctx: :class:`rados.Ioctx`
1610 :returns: :class:`MirrorImageInfoIterator`
1612 return MirrorImageInfoIterator(ioctx, mode_filter)
1614 def pool_metadata_get(self, ioctx, key):
1616 Get pool metadata for the given key.
1618 :param ioctx: determines which RADOS pool is read
1619 :type ioctx: :class:`rados.Ioctx`
1620 :param key: metadata key
1622 :returns: str - metadata value
1624 key = cstr(key, 'key')
1626 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1633 value = <char *>realloc_chk(value, size)
1635 ret = rbd_pool_metadata_get(_ioctx, _key, value, &size)
1636 if ret != -errno.ERANGE:
1638 if ret == -errno.ENOENT:
1639 raise KeyError('no metadata %s' % (key))
1641 raise make_ex(ret, 'error getting metadata %s' % (key))
1642 return decode_cstr(value)
1646 def pool_metadata_set(self, ioctx, key, value):
1648 Set pool metadata for the given key.
1650 :param ioctx: determines which RADOS pool is read
1651 :type ioctx: :class:`rados.Ioctx`
1652 :param key: metadata key
1654 :param value: metadata value
1657 key = cstr(key, 'key')
1658 value = cstr(value, 'value')
1660 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1662 char *_value = value
1664 ret = rbd_pool_metadata_set(_ioctx, _key, _value)
1667 raise make_ex(ret, 'error setting metadata %s' % (key))
1669 def pool_metadata_remove(self, ioctx, key):
1671 Remove pool metadata for the given key.
1673 :param ioctx: determines which RADOS pool is read
1674 :type ioctx: :class:`rados.Ioctx`
1675 :param key: metadata key
1677 :returns: str - metadata value
1679 key = cstr(key, 'key')
1681 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1684 ret = rbd_pool_metadata_remove(_ioctx, _key)
1686 if ret == -errno.ENOENT:
1687 raise KeyError('no metadata %s' % (key))
1689 raise make_ex(ret, 'error removing metadata %s' % (key))
1691 def pool_metadata_list(self, ioctx):
1695 :returns: :class:`PoolMetadataIterator`
1697 return PoolMetadataIterator(ioctx)
1699 def config_list(self, ioctx):
1701 List pool-level config overrides.
1703 :returns: :class:`ConfigPoolIterator`
1705 return ConfigPoolIterator(ioctx)
1707 def config_get(self, ioctx, key):
1709 Get a pool-level configuration override.
1711 :param ioctx: determines which RADOS pool is read
1712 :type ioctx: :class:`rados.Ioctx`
1715 :returns: str - value
1717 conf_key = 'conf_' + key
1718 conf_key = cstr(conf_key, 'key')
1720 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1721 char *_key = conf_key
1727 value = <char *>realloc_chk(value, size)
1729 ret = rbd_pool_metadata_get(_ioctx, _key, value, &size)
1730 if ret != -errno.ERANGE:
1732 if ret == -errno.ENOENT:
1733 raise KeyError('no config %s for pool %s' % (key, ioctx.get_pool_name()))
1735 raise make_ex(ret, 'error getting config %s for pool %s' %
1736 (key, ioctx.get_pool_name()))
1737 return decode_cstr(value)
1741 def config_set(self, ioctx, key, value):
1743 Get a pool-level configuration override.
1745 :param ioctx: determines which RADOS pool is read
1746 :type ioctx: :class:`rados.Ioctx`
1752 conf_key = 'conf_' + key
1753 conf_key = cstr(conf_key, 'key')
1754 value = cstr(value, 'value')
1756 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1757 char *_key = conf_key
1758 char *_value = value
1760 ret = rbd_pool_metadata_set(_ioctx, _key, _value)
1763 raise make_ex(ret, 'error setting config %s for pool %s' %
1764 (key, ioctx.get_pool_name()))
1766 def config_remove(self, ioctx, key):
1768 Remove a pool-level configuration override.
1770 :param ioctx: determines which RADOS pool is read
1771 :type ioctx: :class:`rados.Ioctx`
1774 :returns: str - value
1776 conf_key = 'conf_' + key
1777 conf_key = cstr(conf_key, 'key')
1779 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1780 char *_key = conf_key
1782 ret = rbd_pool_metadata_remove(_ioctx, _key)
1784 if ret == -errno.ENOENT:
1785 raise KeyError('no config %s for pool %s' %
1786 (key, ioctx.get_pool_name()))
1788 raise make_ex(ret, 'error removing config %s for pool %s' %
1789 (key, ioctx.get_pool_name()))
1791 def group_create(self, ioctx, name):
1795 :param ioctx: determines which RADOS pool is used
1796 :type ioctx: :class:`rados.Ioctx`
1797 :param name: the name of the group
1799 :raises: :class:`ObjectExists`
1800 :raises: :class:`InvalidArgument`
1801 :raises: :class:`FunctionNotSupported`
1803 name = cstr(name, 'name')
1806 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1808 ret = rbd_group_create(_ioctx, _name)
1810 raise make_ex(ret, 'error creating group %s' % name, group_errno_to_exception)
1812 def group_remove(self, ioctx, name):
1814 Delete an RBD group. This may take a long time, since it does
1815 not return until every image in the group has been removed
1818 :param ioctx: determines which RADOS pool the group is in
1819 :type ioctx: :class:`rados.Ioctx`
1820 :param name: the name of the group to remove
1822 :raises: :class:`ObjectNotFound`
1823 :raises: :class:`InvalidArgument`
1824 :raises: :class:`FunctionNotSupported`
1826 name = cstr(name, 'name')
1828 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1831 ret = rbd_group_remove(_ioctx, _name)
1833 raise make_ex(ret, 'error removing group', group_errno_to_exception)
1835 def group_list(self, ioctx):
1839 :param ioctx: determines which RADOS pool is read
1840 :type ioctx: :class:`rados.Ioctx`
1841 :returns: list -- a list of groups names
1842 :raises: :class:`FunctionNotSupported`
1845 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1847 char *c_names = NULL
1850 c_names = <char *>realloc_chk(c_names, size)
1852 ret = rbd_group_list(_ioctx, c_names, &size)
1855 elif ret != -errno.ERANGE:
1856 raise make_ex(ret, 'error listing groups', group_errno_to_exception)
1857 return [decode_cstr(name) for name in c_names[:ret].split(b'\0')
1862 def group_rename(self, ioctx, src, dest):
1864 Rename an RBD group.
1866 :param ioctx: determines which RADOS pool the group is in
1867 :type ioctx: :class:`rados.Ioctx`
1868 :param src: the current name of the group
1870 :param dest: the new name of the group
1872 :raises: :class:`ObjectExists`
1873 :raises: :class:`ObjectNotFound`
1874 :raises: :class:`InvalidArgument`
1875 :raises: :class:`FunctionNotSupported`
1877 src = cstr(src, 'src')
1878 dest = cstr(dest, 'dest')
1880 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1884 ret = rbd_group_rename(_ioctx, _src, _dest)
1886 raise make_ex(ret, 'error renaming group')
1888 def namespace_create(self, ioctx, name):
1890 Create an RBD namespace within a pool
1892 :param ioctx: determines which RADOS pool
1893 :type ioctx: :class:`rados.Ioctx`
1894 :param name: namespace name
1897 name = cstr(name, 'name')
1899 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1900 const char *_name = name
1902 ret = rbd_namespace_create(_ioctx, _name)
1904 raise make_ex(ret, 'error creating namespace')
1906 def namespace_remove(self, ioctx, name):
1908 Remove an RBD namespace from a pool
1910 :param ioctx: determines which RADOS pool
1911 :type ioctx: :class:`rados.Ioctx`
1912 :param name: namespace name
1915 name = cstr(name, 'name')
1917 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1918 const char *_name = name
1920 ret = rbd_namespace_remove(_ioctx, _name)
1922 raise make_ex(ret, 'error removing namespace')
1924 def namespace_exists(self, ioctx, name):
1926 Verifies if a namespace exists within a pool
1928 :param ioctx: determines which RADOS pool
1929 :type ioctx: :class:`rados.Ioctx`
1930 :param name: namespace name
1932 :returns: bool - true if namespace exists
1934 name = cstr(name, 'name')
1936 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1937 const char *_name = name
1938 bint _exists = False
1940 ret = rbd_namespace_exists(_ioctx, _name, &_exists)
1942 raise make_ex(ret, 'error verifying namespace')
1943 return bool(_exists != 0)
1945 def namespace_list(self, ioctx):
1947 List all namespaces within a pool
1949 :param ioctx: determines which RADOS pool
1950 :type ioctx: :class:`rados.Ioctx`
1951 :returns: list - collection of namespace names
1954 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1959 _names = <char *>realloc_chk(_names, _size)
1961 ret = rbd_namespace_list(_ioctx, _names, &_size)
1964 elif ret != -errno.ERANGE:
1965 raise make_ex(ret, 'error listing namespaces')
1966 return [decode_cstr(name) for name in _names[:_size].split(b'\0')
1971 def pool_init(self, ioctx, force):
1973 Initialize an RBD pool
1974 :param ioctx: determines which RADOS pool
1975 :type ioctx: :class:`rados.Ioctx`
1976 :param force: force init
1980 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1983 ret = rbd_pool_init(_ioctx, _force)
1985 raise make_ex(ret, 'error initializing pool')
1987 def pool_stats_get(self, ioctx):
1989 Return RBD pool stats
1991 :param ioctx: determines which RADOS pool
1992 :type ioctx: :class:`rados.Ioctx`
1993 :returns: dict - contains the following keys:
1995 * ``image_count`` (int) - image count
1997 * ``image_provisioned_bytes`` (int) - image total HEAD provisioned bytes
1999 * ``image_max_provisioned_bytes`` (int) - image total max provisioned bytes
2001 * ``image_snap_count`` (int) - image snap count
2003 * ``trash_count`` (int) - trash image count
2005 * ``trash_provisioned_bytes`` (int) - trash total HEAD provisioned bytes
2007 * ``trash_max_provisioned_bytes`` (int) - trash total max provisioned bytes
2009 * ``trash_snap_count`` (int) - trash snap count
2013 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2014 uint64_t _image_count = 0
2015 uint64_t _image_provisioned_bytes = 0
2016 uint64_t _image_max_provisioned_bytes = 0
2017 uint64_t _image_snap_count = 0
2018 uint64_t _trash_count = 0
2019 uint64_t _trash_provisioned_bytes = 0
2020 uint64_t _trash_max_provisioned_bytes = 0
2021 uint64_t _trash_snap_count = 0
2022 rbd_pool_stats_t _stats
2024 rbd_pool_stats_create(&_stats)
2025 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGES,
2027 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_PROVISIONED_BYTES,
2028 &_image_provisioned_bytes)
2029 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_MAX_PROVISIONED_BYTES,
2030 &_image_max_provisioned_bytes)
2031 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGE_SNAPSHOTS,
2033 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_IMAGES,
2035 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_PROVISIONED_BYTES,
2036 &_trash_provisioned_bytes)
2037 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_MAX_PROVISIONED_BYTES,
2038 &_trash_max_provisioned_bytes)
2039 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_SNAPSHOTS,
2043 ret = rbd_pool_stats_get(_ioctx, _stats)
2045 raise make_ex(ret, 'error retrieving pool stats')
2047 return {'image_count': _image_count,
2048 'image_provisioned_bytes': _image_provisioned_bytes,
2049 'image_max_provisioned_bytes': _image_max_provisioned_bytes,
2050 'image_snap_count': _image_snap_count,
2051 'trash_count': _trash_count,
2052 'trash_provisioned_bytes': _trash_provisioned_bytes,
2053 'trash_max_provisioned_bytes': _trash_max_provisioned_bytes,
2054 'trash_snap_count': _trash_snap_count}
2056 rbd_pool_stats_destroy(_stats)
2058 def features_to_string(self, features):
2060 Convert features bitmask to str.
2062 :param features: feature bitmask
2064 :returns: str - the features str of the image
2065 :raises: :class:`InvalidArgument`
2068 int ret = -errno.ERANGE
2069 uint64_t _features = features
2071 char *str_features = NULL
2073 while ret == -errno.ERANGE:
2074 str_features = <char *>realloc_chk(str_features, size)
2076 ret = rbd_features_to_string(_features, str_features, &size)
2079 raise make_ex(ret, 'error converting features bitmask to str')
2080 return decode_cstr(str_features)
2084 def features_from_string(self, str_features):
2086 Get features bitmask from str, if str_features is empty, it will return
2087 RBD_FEATURES_DEFAULT.
2089 :param str_features: feature str
2090 :type str_features: str
2091 :returns: int - the features bitmask of the image
2092 :raises: :class:`InvalidArgument`
2094 str_features = cstr(str_features, 'str_features')
2096 const char *_str_features = str_features
2099 ret = rbd_features_from_string(_str_features, &features)
2101 raise make_ex(ret, 'error getting features bitmask from str')
2104 def aio_open_image(self, oncomplete, ioctx, name=None, snapshot=None,
2105 read_only=False, image_id=None):
2107 Asynchronously open the image at the given snapshot.
2108 Specify either name or id, otherwise :class:`InvalidArgument` is raised.
2110 oncomplete will be called with the created Image object as
2111 well as the completion:
2113 oncomplete(completion, image)
2115 If a snapshot is specified, the image will be read-only, unless
2116 :func:`Image.set_snap` is called later.
2118 If read-only mode is used, metadata for the :class:`Image`
2119 object (such as which snapshots exist) may become obsolete. See
2120 the C api for more details.
2122 To clean up from opening the image, :func:`Image.close` or
2123 :func:`Image.aio_close` should be called.
2125 :param oncomplete: what to do when open is complete
2126 :type oncomplete: completion
2127 :param ioctx: determines which RADOS pool the image is in
2128 :type ioctx: :class:`rados.Ioctx`
2129 :param name: the name of the image
2131 :param snapshot: which snapshot to read from
2132 :type snaphshot: str
2133 :param read_only: whether to open the image in read-only mode
2134 :type read_only: bool
2135 :param image_id: the id of the image
2137 :returns: :class:`Completion` - the completion object
2140 image = Image(ioctx, name, snapshot, read_only, image_id, oncomplete)
2141 comp, image._open_completion = image._open_completion, None
2144 cdef class MirrorPeerIterator(object):
2146 Iterator over mirror peer info for a pool.
2148 Yields a dictionary containing information about a peer.
2152 * ``uuid`` (str) - uuid of the peer
2154 * ``direction`` (int) - direction enum
2156 * ``site_name`` (str) - cluster name of the peer
2158 * ``mirror_uuid`` (str) - mirror uuid of the peer
2160 * ``client_name`` (str) - client name of the peer
2164 rbd_mirror_peer_site_t *peers
2167 def __init__(self, ioctx):
2169 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2173 self.peers = <rbd_mirror_peer_site_t *>realloc_chk(
2174 self.peers, self.num_peers * sizeof(rbd_mirror_peer_site_t))
2176 ret = rbd_mirror_peer_site_list(_ioctx, self.peers,
2179 if ret == -errno.ERANGE:
2182 raise make_ex(ret, 'error listing peers')
2186 for i in range(self.num_peers):
2188 'uuid' : decode_cstr(self.peers[i].uuid),
2189 'direction' : int(self.peers[i].direction),
2190 'site_name' : decode_cstr(self.peers[i].site_name),
2191 'cluster_name' : decode_cstr(self.peers[i].site_name),
2192 'mirror_uuid' : decode_cstr(self.peers[i].mirror_uuid),
2193 'client_name' : decode_cstr(self.peers[i].client_name),
2196 def __dealloc__(self):
2198 rbd_mirror_peer_site_list_cleanup(self.peers, self.num_peers)
2201 cdef class MirrorImageStatusIterator(object):
2203 Iterator over mirror image status for a pool.
2205 Yields a dictionary containing mirror status of an image.
2209 * ``name`` (str) - mirror image name
2211 * ``id`` (str) - mirror image id
2213 * ``info`` (dict) - mirror image info
2215 * ``state`` (int) - status mirror state
2217 * ``description`` (str) - status description
2219 * ``last_update`` (datetime) - last status update time
2221 * ``up`` (bool) - is mirroring agent up
2223 * ``remote_statuses`` (array) -
2225 * ``mirror uuid`` (str) - remote mirror uuid
2227 * ``state`` (int) - status mirror state
2229 * ``description`` (str) - status description
2231 * ``last_update`` (datetime) - last status update time
2233 * ``up`` (bool) - is mirroring agent up
2241 rbd_mirror_image_site_status_t *s_status
2242 rbd_mirror_image_global_status_t *images
2245 def __init__(self, ioctx):
2246 self.ioctx = convert_ioctx(ioctx)
2247 self.max_read = 1024
2248 self.last_read = strdup("")
2249 self.image_ids = <char **>realloc_chk(NULL,
2250 sizeof(char *) * self.max_read)
2251 self.images = <rbd_mirror_image_global_status_t *>realloc_chk(NULL,
2252 sizeof(rbd_mirror_image_global_status_t) * self.max_read)
2254 self.get_next_chunk()
2258 while self.size > 0:
2259 for i in range(self.size):
2263 for x in range(self.images[i].site_statuses_count):
2264 s_status = &self.images[i].site_statuses[x]
2266 'state' : s_status.state,
2267 'description' : decode_cstr(s_status.description),
2268 'last_update' : datetime.utcfromtimestamp(s_status.last_update),
2271 mirror_uuid = decode_cstr(s_status.mirror_uuid)
2272 if mirror_uuid == '':
2273 local_status = site_status
2275 site_status['mirror_uuid'] = mirror_uuid
2276 site_statuses.append(site_status)
2279 'name' : decode_cstr(self.images[i].name),
2280 'id' : decode_cstr(self.image_ids[i]),
2282 'global_id' : decode_cstr(self.images[i].info.global_id),
2283 'state' : self.images[i].info.state,
2284 # primary isn't added here because it is unknown (always
2285 # false, see XXX in Mirror::image_global_status_list())
2287 'remote_statuses': site_statuses,
2290 status.update(local_status)
2292 if self.size < self.max_read:
2294 self.get_next_chunk()
2296 def __dealloc__(self):
2297 rbd_mirror_image_global_status_list_cleanup(self.image_ids, self.images,
2300 free(self.last_read)
2302 free(self.image_ids)
2306 def get_next_chunk(self):
2308 rbd_mirror_image_global_status_list_cleanup(self.image_ids,
2313 ret = rbd_mirror_image_global_status_list(self.ioctx,
2317 self.images, &self.size)
2319 raise make_ex(ret, 'error listing mirror images status')
2321 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2322 free(self.last_read)
2323 self.last_read = strdup(last_read)
2325 free(self.last_read)
2326 self.last_read = strdup("")
2328 cdef class MirrorImageInstanceIdIterator(object):
2330 Iterator over mirror image instance id for a pool.
2332 Yields ``(image_id, instance_id)`` tuple.
2343 def __init__(self, ioctx):
2344 self.ioctx = convert_ioctx(ioctx)
2345 self.max_read = 1024
2346 self.last_read = strdup("")
2347 self.image_ids = <char **>realloc_chk(NULL,
2348 sizeof(char *) * self.max_read)
2349 self.instance_ids = <char **>realloc_chk(NULL,
2350 sizeof(char *) * self.max_read)
2352 self.get_next_chunk()
2355 while self.size > 0:
2356 for i in range(self.size):
2357 yield (decode_cstr(self.image_ids[i]),
2358 decode_cstr(self.instance_ids[i]))
2359 if self.size < self.max_read:
2361 self.get_next_chunk()
2363 def __dealloc__(self):
2364 rbd_mirror_image_instance_id_list_cleanup(self.image_ids,
2365 self.instance_ids, self.size)
2367 free(self.last_read)
2369 free(self.image_ids)
2370 if self.instance_ids:
2371 free(self.instance_ids)
2373 def get_next_chunk(self):
2375 rbd_mirror_image_instance_id_list_cleanup(self.image_ids,
2380 ret = rbd_mirror_image_instance_id_list(self.ioctx, self.last_read,
2386 raise make_ex(ret, 'error listing mirror images instance ids')
2388 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2389 free(self.last_read)
2390 self.last_read = strdup(last_read)
2392 free(self.last_read)
2393 self.last_read = strdup("")
2395 cdef class MirrorImageInfoIterator(object):
2397 Iterator over mirror image info for a pool.
2399 Yields ``(image_id, info)`` tuple.
2404 rbd_mirror_image_mode_t mode_filter
2405 rbd_mirror_image_mode_t *mode_filter_ptr
2409 rbd_mirror_image_info_t *info_entries
2410 rbd_mirror_image_mode_t *mode_entries
2413 def __init__(self, ioctx, mode_filter):
2414 self.ioctx = convert_ioctx(ioctx)
2415 if mode_filter is not None:
2416 self.mode_filter = mode_filter
2417 self.mode_filter_ptr = &self.mode_filter
2419 self.mode_filter_ptr = NULL
2420 self.max_read = 1024
2421 self.last_read = strdup("")
2422 self.image_ids = <char **>realloc_chk(NULL,
2423 sizeof(char *) * self.max_read)
2424 self.info_entries = <rbd_mirror_image_info_t *>realloc_chk(NULL,
2425 sizeof(rbd_mirror_image_info_t) * self.max_read)
2426 self.mode_entries = <rbd_mirror_image_mode_t *>realloc_chk(NULL,
2427 sizeof(rbd_mirror_image_mode_t) * self.max_read)
2429 self.get_next_chunk()
2432 while self.size > 0:
2433 for i in range(self.size):
2434 yield (decode_cstr(self.image_ids[i]),
2436 'mode' : int(self.mode_entries[i]),
2437 'global_id' : decode_cstr(self.info_entries[i].global_id),
2438 'state' : int(self.info_entries[i].state),
2439 'primary' : self.info_entries[i].primary,
2441 if self.size < self.max_read:
2443 self.get_next_chunk()
2445 def __dealloc__(self):
2446 rbd_mirror_image_info_list_cleanup(self.image_ids, self.info_entries,
2449 free(self.last_read)
2451 free(self.image_ids)
2452 if self.info_entries:
2453 free(self.info_entries)
2454 if self.mode_entries:
2455 free(self.mode_entries)
2457 def get_next_chunk(self):
2459 rbd_mirror_image_info_list_cleanup(self.image_ids,
2460 self.info_entries, self.size)
2463 ret = rbd_mirror_image_info_list(self.ioctx, self.mode_filter_ptr,
2464 self.last_read, self.max_read,
2465 self.image_ids, self.mode_entries,
2466 self.info_entries, &self.size)
2468 raise make_ex(ret, 'error listing mirror image info')
2470 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2471 free(self.last_read)
2472 self.last_read = strdup(last_read)
2474 free(self.last_read)
2475 self.last_read = strdup("")
2477 cdef class PoolMetadataIterator(object):
2479 Iterator over pool metadata list.
2481 Yields ``(key, value)`` tuple.
2483 * ``key`` (str) - metadata key
2484 * ``value`` (str) - metadata value
2493 def __init__(self, ioctx):
2494 self.ioctx = convert_ioctx(ioctx)
2495 self.last_read = strdup("")
2497 self.get_next_chunk()
2500 while len(self.next_chunk) > 0:
2501 for pair in self.next_chunk:
2503 if len(self.next_chunk) < self.max_read:
2505 self.get_next_chunk()
2507 def __dealloc__(self):
2509 free(self.last_read)
2511 def get_next_chunk(self):
2514 size_t keys_size = 4096
2516 size_t vals_size = 4096
2519 c_keys = <char *>realloc_chk(c_keys, keys_size)
2520 c_vals = <char *>realloc_chk(c_vals, vals_size)
2522 ret = rbd_pool_metadata_list(self.ioctx, self.last_read,
2523 self.max_read, c_keys,
2524 &keys_size, c_vals, &vals_size)
2527 elif ret != -errno.ERANGE:
2528 raise make_ex(ret, 'error listing metadata')
2529 keys = [decode_cstr(key) for key in
2530 c_keys[:keys_size].split(b'\0') if key]
2531 vals = [decode_cstr(val) for val in
2532 c_vals[:vals_size].split(b'\0') if val]
2534 last_read = cstr(keys[-1], 'last_read')
2535 free(self.last_read)
2536 self.last_read = strdup(last_read)
2537 self.next_chunk = list(zip(keys, vals))
2542 cdef class ConfigPoolIterator(object):
2544 Iterator over pool-level overrides for a pool.
2546 Yields a dictionary containing information about an override.
2550 * ``name`` (str) - override name
2552 * ``value`` (str) - override value
2554 * ``source`` (str) - override source
2558 rbd_config_option_t *options
2561 def __init__(self, ioctx):
2563 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2565 self.num_options = 32
2567 self.options = <rbd_config_option_t *>realloc_chk(
2568 self.options, self.num_options * sizeof(rbd_config_option_t))
2570 ret = rbd_config_pool_list(_ioctx, self.options, &self.num_options)
2572 if ret == -errno.ERANGE:
2574 self.num_options = 0
2575 raise make_ex(ret, 'error listing config options')
2579 for i in range(self.num_options):
2581 'name' : decode_cstr(self.options[i].name),
2582 'value' : decode_cstr(self.options[i].value),
2583 'source' : self.options[i].source,
2586 def __dealloc__(self):
2588 rbd_config_pool_list_cleanup(self.options, self.num_options)
2591 cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \
2592 except? -9000 with gil:
2593 # Make sure that if we wound up with an exception from a previous callback,
2594 # we stop calling back (just in case librbd ever fails to bail out on the
2595 # first negative return, as older versions did)
2596 if exc.PyErr_Occurred():
2598 ret = (<object>cb)(offset, length, bool(write))
2603 cdef class Group(object):
2605 This class represents an RBD group. It is used to interact with
2606 snapshots and images members.
2613 cdef rados_ioctx_t _ioctx
2615 def __init__(self, ioctx, name):
2616 name = cstr(name, 'name')
2619 self._ioctx = convert_ioctx(ioctx)
2622 def __enter__(self):
2625 def __exit__(self, type_, value, traceback):
2628 def add_image(self, image_ioctx, image_name):
2630 Add an image to a group.
2632 :param image_ioctx: determines which RADOS pool the image belongs to.
2633 :type ioctx: :class:`rados.Ioctx`
2634 :param name: the name of the image to add
2637 :raises: :class:`ObjectNotFound`
2638 :raises: :class:`ObjectExists`
2639 :raises: :class:`InvalidArgument`
2640 :raises: :class:`FunctionNotSupported`
2642 image_name = cstr(image_name, 'image_name')
2644 rados_ioctx_t _image_ioctx = convert_ioctx(image_ioctx)
2645 char *_image_name = image_name
2647 ret = rbd_group_image_add(self._ioctx, self._name, _image_ioctx, _image_name)
2649 raise make_ex(ret, 'error adding image to group', group_errno_to_exception)
2651 def remove_image(self, image_ioctx, image_name):
2653 Remove an image from a group.
2655 :param image_ioctx: determines which RADOS pool the image belongs to.
2656 :type ioctx: :class:`rados.Ioctx`
2657 :param name: the name of the image to remove
2660 :raises: :class:`ObjectNotFound`
2661 :raises: :class:`InvalidArgument`
2662 :raises: :class:`FunctionNotSupported`
2664 image_name = cstr(image_name, 'image_name')
2666 rados_ioctx_t _image_ioctx = convert_ioctx(image_ioctx)
2667 char *_image_name = image_name
2669 ret = rbd_group_image_remove(self._ioctx, self._name, _image_ioctx, _image_name)
2671 raise make_ex(ret, 'error removing image from group', group_errno_to_exception)
2674 def list_images(self):
2676 Iterate over the images of a group.
2678 :returns: :class:`GroupImageIterator`
2680 return GroupImageIterator(self)
2682 def create_snap(self, snap_name, flags=0):
2684 Create a snapshot for the group.
2686 :param snap_name: the name of the snapshot to create
2687 :param flags: create snapshot flags
2690 :raises: :class:`ObjectNotFound`
2691 :raises: :class:`ObjectExists`
2692 :raises: :class:`InvalidArgument`
2693 :raises: :class:`FunctionNotSupported`
2695 snap_name = cstr(snap_name, 'snap_name')
2697 char *_snap_name = snap_name
2698 uint32_t _flags = flags
2700 ret = rbd_group_snap_create2(self._ioctx, self._name, _snap_name,
2703 raise make_ex(ret, 'error creating group snapshot', group_errno_to_exception)
2705 def remove_snap(self, snap_name):
2707 Remove a snapshot from the group.
2709 :param snap_name: the name of the snapshot to remove
2712 :raises: :class:`ObjectNotFound`
2713 :raises: :class:`InvalidArgument`
2714 :raises: :class:`FunctionNotSupported`
2716 snap_name = cstr(snap_name, 'snap_name')
2718 char *_snap_name = snap_name
2720 ret = rbd_group_snap_remove(self._ioctx, self._name, _snap_name)
2722 raise make_ex(ret, 'error removing group snapshot', group_errno_to_exception)
2724 def rename_snap(self, old_snap_name, new_snap_name):
2726 Rename group's snapshot.
2728 :raises: :class:`ObjectNotFound`
2729 :raises: :class:`ObjectExists`
2730 :raises: :class:`InvalidArgument`
2731 :raises: :class:`FunctionNotSupported`
2734 old_snap_name = cstr(old_snap_name, 'old_snap_name')
2735 new_snap_name = cstr(new_snap_name, 'new_snap_name')
2737 char *_old_snap_name = old_snap_name
2738 char *_new_snap_name = new_snap_name
2740 ret = rbd_group_snap_rename(self._ioctx, self._name, _old_snap_name,
2743 raise make_ex(ret, 'error renaming group snapshot',
2744 group_errno_to_exception)
2746 def list_snaps(self):
2748 Iterate over the images of a group.
2750 :returns: :class:`GroupSnapIterator`
2752 return GroupSnapIterator(self)
2754 def rollback_to_snap(self, name):
2756 Rollback group to snapshot.
2758 :param name: the group snapshot to rollback to
2760 :raises: :class:`ObjectNotFound`
2761 :raises: :class:`IOError`
2763 name = cstr(name, 'name')
2764 cdef char *_name = name
2766 ret = rbd_group_snap_rollback(self._ioctx, self._name, _name)
2768 raise make_ex(ret, 'error rolling back group to snapshot', group_errno_to_exception)
2770 def requires_not_closed(f):
2771 def wrapper(self, *args, **kwargs):
2772 self.require_not_closed()
2773 return f(self, *args, **kwargs)
2777 cdef class Image(object):
2779 This class represents an RBD image. It is used to perform I/O on
2780 the image and interact with snapshots.
2782 **Note**: Any method of this class may raise :class:`ImageNotFound`
2783 if the image has been deleted.
2785 cdef rbd_image_t image
2789 cdef rados_ioctx_t _ioctx
2790 cdef Completion _open_completion
2792 def __init__(self, ioctx, name=None, snapshot=None,
2793 read_only=False, image_id=None, _oncomplete=None):
2795 Open the image at the given snapshot.
2796 Specify either name or id, otherwise :class:`InvalidArgument` is raised.
2798 If a snapshot is specified, the image will be read-only, unless
2799 :func:`Image.set_snap` is called later.
2801 If read-only mode is used, metadata for the :class:`Image`
2802 object (such as which snapshots exist) may become obsolete. See
2803 the C api for more details.
2805 To clean up from opening the image, :func:`Image.close` should
2806 be called. For ease of use, this is done automatically when
2807 an :class:`Image` is used as a context manager (see :pep:`343`).
2809 :param ioctx: determines which RADOS pool the image is in
2810 :type ioctx: :class:`rados.Ioctx`
2811 :param name: the name of the image
2813 :param snapshot: which snapshot to read from
2814 :type snaphshot: str
2815 :param read_only: whether to open the image in read-only mode
2816 :type read_only: bool
2817 :param image_id: the id of the image
2820 name = cstr(name, 'name', opt=True)
2821 image_id = cstr(image_id, 'image_id', opt=True)
2822 snapshot = cstr(snapshot, 'snapshot', opt=True)
2824 if name is not None and image_id is not None:
2825 raise InvalidArgument("only need to specify image name or image id")
2826 elif name is None and image_id is None:
2827 raise InvalidArgument("image name or image id was not specified")
2828 elif name is not None:
2831 self.name = image_id
2832 # Keep around a reference to the ioctx, so it won't get deleted
2835 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2836 char *_name = opt_str(name)
2837 char *_image_id = opt_str(image_id)
2838 char *_snapshot = opt_str(snapshot)
2839 cdef Completion completion
2842 def oncomplete(completion_v):
2843 cdef Completion _completion_v = completion_v
2844 return_value = _completion_v.get_return_value()
2845 if return_value == 0:
2848 self.name = self.get_name()
2849 return _oncomplete(_completion_v, self)
2851 completion = self.__get_completion(oncomplete)
2853 completion.__persist()
2856 if name is not None:
2857 ret = rbd_aio_open_read_only(
2858 _ioctx, _name, &self.image, _snapshot,
2859 completion.rbd_comp)
2861 ret = rbd_aio_open_by_id_read_only(
2862 _ioctx, _image_id, &self.image, _snapshot,
2863 completion.rbd_comp)
2866 if name is not None:
2868 _ioctx, _name, &self.image, _snapshot,
2869 completion.rbd_comp)
2871 ret = rbd_aio_open_by_id(
2872 _ioctx, _image_id, &self.image, _snapshot,
2873 completion.rbd_comp)
2875 raise make_ex(ret, 'error opening image %s at snapshot %s' %
2876 (self.name, snapshot))
2878 completion.__unpersist()
2881 self._open_completion = completion
2886 if name is not None:
2887 ret = rbd_open_read_only(_ioctx, _name, &self.image, _snapshot)
2889 ret = rbd_open_by_id_read_only(_ioctx, _image_id, &self.image, _snapshot)
2892 if name is not None:
2893 ret = rbd_open(_ioctx, _name, &self.image, _snapshot)
2895 ret = rbd_open_by_id(_ioctx, _image_id, &self.image, _snapshot)
2897 raise make_ex(ret, 'error opening image %s at snapshot %s' % (self.name, snapshot))
2900 self.name = self.get_name()
2902 def __enter__(self):
2905 def __exit__(self, type_, value, traceback):
2907 Closes the image. See :func:`close`
2912 def __get_completion(self, oncomplete):
2914 Constructs a completion to use with asynchronous operations
2916 :param oncomplete: callback for the completion
2918 :raises: :class:`Error`
2919 :returns: completion object
2922 completion_obj = Completion(self, oncomplete)
2925 rbd_completion_t completion
2926 PyObject* p_completion_obj= <PyObject*>completion_obj
2929 ret = rbd_aio_create_completion(p_completion_obj, __aio_complete_cb,
2932 raise make_ex(ret, "error getting a completion")
2934 completion_obj.rbd_comp = completion
2935 return completion_obj
2937 def require_not_closed(self):
2939 Checks if the Image is not closed
2941 :raises: :class:`InvalidArgument`
2944 raise InvalidArgument("image is closed")
2948 Release the resources used by this image object.
2950 After this is called, this object should not be used.
2955 ret = rbd_close(self.image)
2957 raise make_ex(ret, 'error while closing image %s' % (
2960 @requires_not_closed
2961 def aio_close(self, oncomplete):
2963 Asynchronously close the image.
2965 After this is called, this object should not be used.
2967 :param oncomplete: what to do when close is complete
2968 :type oncomplete: completion
2969 :returns: :class:`Completion` - the completion object
2971 cdef Completion completion = self.__get_completion(oncomplete)
2974 completion.__persist()
2976 ret = rbd_aio_close(self.image, completion.rbd_comp)
2978 raise make_ex(ret, 'error while closing image %s' %
2981 completion.__unpersist()
2985 def __dealloc__(self):
2989 return "rbd.Image(ioctx, %r)" % self.name
2991 @requires_not_closed
2992 def resize(self, size, allow_shrink=True):
2994 Change the size of the image, allow shrink.
2996 :param size: the new size of the image
2998 :param allow_shrink: permit shrinking
2999 :type allow_shrink: bool
3001 old_size = self.size()
3002 if old_size == size:
3004 if not allow_shrink and old_size > size:
3005 raise InvalidArgument("error allow_shrink is False but old_size > new_size")
3007 uint64_t _size = size
3008 bint _allow_shrink = allow_shrink
3009 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3011 ret = rbd_resize2(self.image, _size, _allow_shrink, prog_cb, NULL)
3013 raise make_ex(ret, 'error resizing image %s' % self.name)
3015 @requires_not_closed
3018 Get information about the image. Currently parent pool and
3019 parent name are always -1 and ''.
3021 :returns: dict - contains the following keys:
3023 * ``size`` (int) - the size of the image in bytes
3025 * ``obj_size`` (int) - the size of each object that comprises the
3028 * ``num_objs`` (int) - the number of objects in the image
3030 * ``order`` (int) - log_2(object_size)
3032 * ``block_name_prefix`` (str) - the prefix of the RADOS objects used
3035 * ``parent_pool`` (int) - deprecated
3037 * ``parent_name`` (str) - deprecated
3039 See also :meth:`format` and :meth:`features`.
3042 cdef rbd_image_info_t info
3044 ret = rbd_stat(self.image, &info, sizeof(info))
3046 raise make_ex(ret, 'error getting info for image %s' % self.name)
3049 'obj_size' : info.obj_size,
3050 'num_objs' : info.num_objs,
3051 'order' : info.order,
3052 'block_name_prefix' : decode_cstr(info.block_name_prefix),
3053 'parent_pool' : info.parent_pool,
3054 'parent_name' : info.parent_name
3057 @requires_not_closed
3060 Get the RBD image name
3062 :returns: str - image name
3065 int ret = -errno.ERANGE
3067 char *image_name = NULL
3069 while ret == -errno.ERANGE:
3070 image_name = <char *>realloc_chk(image_name, size)
3072 ret = rbd_get_name(self.image, image_name, &size)
3075 raise make_ex(ret, 'error getting name for image %s' % self.name)
3076 return decode_cstr(image_name)
3080 @requires_not_closed
3083 Get the RBD v2 internal image id
3085 :returns: str - image id
3088 int ret = -errno.ERANGE
3090 char *image_id = NULL
3092 while ret == -errno.ERANGE and size <= 4096:
3093 image_id = <char *>realloc_chk(image_id, size)
3095 ret = rbd_get_id(self.image, image_id, size)
3096 if ret == -errno.ERANGE:
3100 raise make_ex(ret, 'error getting id for image %s' % self.name)
3101 return decode_cstr(image_id)
3105 @requires_not_closed
3106 def block_name_prefix(self):
3108 Get the RBD block name prefix
3110 :returns: str - block name prefix
3113 int ret = -errno.ERANGE
3117 while ret == -errno.ERANGE and size <= 4096:
3118 prefix = <char *>realloc_chk(prefix, size)
3120 ret = rbd_get_block_name_prefix(self.image, prefix, size)
3121 if ret == -errno.ERANGE:
3125 raise make_ex(ret, 'error getting block name prefix for image %s' % self.name)
3126 return decode_cstr(prefix)
3130 @requires_not_closed
3131 def data_pool_id(self):
3133 Get the pool id of the pool where the data of this RBD image is stored.
3135 :returns: int - the pool id
3138 ret = rbd_get_data_pool_id(self.image)
3140 raise make_ex(ret, 'error getting data pool id for image %s' % self.name)
3143 @requires_not_closed
3144 def get_parent_image_spec(self):
3146 Get spec of the cloned image's parent
3148 :returns: dict - contains the following keys:
3149 * ``pool_name`` (str) - parent pool name
3150 * ``pool_namespace`` (str) - parent pool namespace
3151 * ``image_name`` (str) - parent image name
3152 * ``snap_name`` (str) - parent snapshot name
3154 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3157 rbd_linked_image_spec_t parent_spec
3158 rbd_snap_spec_t snap_spec
3160 ret = rbd_get_parent(self.image, &parent_spec, &snap_spec)
3162 raise make_ex(ret, 'error getting parent info for image %s' % self.name)
3164 result = {'pool_name': decode_cstr(parent_spec.pool_name),
3165 'pool_namespace': decode_cstr(parent_spec.pool_namespace),
3166 'image_name': decode_cstr(parent_spec.image_name),
3167 'snap_name': decode_cstr(snap_spec.name)}
3169 rbd_linked_image_spec_cleanup(&parent_spec)
3170 rbd_snap_spec_cleanup(&snap_spec)
3173 @requires_not_closed
3174 def parent_info(self):
3176 Deprecated. Use `get_parent_image_spec` instead.
3178 Get information about a cloned image's parent (if any)
3180 :returns: tuple - ``(pool name, image name, snapshot name)`` components
3182 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3184 parent = self.get_parent_image_spec()
3185 return (parent['pool_name'], parent['image_name'], parent['snap_name'])
3187 @requires_not_closed
3188 def parent_id(self):
3190 Get image id of a cloned image's parent (if any)
3192 :returns: str - the parent id
3193 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3196 rbd_linked_image_spec_t parent_spec
3197 rbd_snap_spec_t snap_spec
3199 ret = rbd_get_parent(self.image, &parent_spec, &snap_spec)
3201 raise make_ex(ret, 'error getting parent info for image %s' % self.name)
3203 result = decode_cstr(parent_spec.image_id)
3205 rbd_linked_image_spec_cleanup(&parent_spec)
3206 rbd_snap_spec_cleanup(&snap_spec)
3209 @requires_not_closed
3210 def migration_source_spec(self):
3212 Get migration source spec (if any)
3215 :raises: :class:`ImageNotFound` if the image is not migration destination
3222 spec = <char *>realloc_chk(spec, size)
3224 ret = rbd_get_migration_source_spec(self.image, spec, &size)
3227 elif ret != -errno.ERANGE:
3228 raise make_ex(ret, 'error retrieving migration source')
3229 return json.loads(decode_cstr(spec))
3233 @requires_not_closed
3234 def old_format(self):
3236 Find out whether the image uses the old RBD format.
3238 :returns: bool - whether the image uses the old RBD format
3242 ret = rbd_get_old_format(self.image, &old)
3244 raise make_ex(ret, 'error getting old_format for image %s' % (self.name))
3247 @requires_not_closed
3250 Get the size of the image. If open to a snapshot, returns the
3251 size of that snapshot.
3253 :returns: int - the size of the image in bytes
3255 cdef uint64_t image_size
3257 ret = rbd_get_size(self.image, &image_size)
3259 raise make_ex(ret, 'error getting size for image %s' % (self.name))
3262 @requires_not_closed
3265 Get the features bitmask of the image.
3267 :returns: int - the features bitmask of the image
3269 cdef uint64_t features
3271 ret = rbd_get_features(self.image, &features)
3273 raise make_ex(ret, 'error getting features for image %s' % (self.name))
3276 @requires_not_closed
3277 def update_features(self, features, enabled):
3279 Update the features bitmask of the image by enabling/disabling
3280 a single feature. The feature must support the ability to be
3281 dynamically enabled/disabled.
3283 :param features: feature bitmask to enable/disable
3285 :param enabled: whether to enable/disable the feature
3287 :raises: :class:`InvalidArgument`
3290 uint64_t _features = features
3291 uint8_t _enabled = bool(enabled)
3293 ret = rbd_update_features(self.image, _features, _enabled)
3295 raise make_ex(ret, 'error updating features for image %s' %
3298 @requires_not_closed
3299 def op_features(self):
3301 Get the op features bitmask of the image.
3303 :returns: int - the op features bitmask of the image
3305 cdef uint64_t op_features
3307 ret = rbd_get_op_features(self.image, &op_features)
3309 raise make_ex(ret, 'error getting op features for image %s' % (self.name))
3312 @requires_not_closed
3315 Get the number of overlapping bytes between the image and its parent
3316 image. If open to a snapshot, returns the overlap between the snapshot
3317 and the parent image.
3319 :returns: int - the overlap in bytes
3320 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3322 cdef uint64_t overlap
3324 ret = rbd_get_overlap(self.image, &overlap)
3326 raise make_ex(ret, 'error getting overlap for image %s' % (self.name))
3329 @requires_not_closed
3332 Get the flags bitmask of the image.
3334 :returns: int - the flags bitmask of the image
3338 ret = rbd_get_flags(self.image, &flags)
3340 raise make_ex(ret, 'error getting flags for image %s' % (self.name))
3343 @requires_not_closed
3346 Get information about the image's group.
3348 :returns: dict - contains the following keys:
3350 * ``pool`` (int) - id of the group pool
3352 * ``name`` (str) - name of the group
3355 cdef rbd_group_info_t info
3357 ret = rbd_get_group(self.image, &info, sizeof(info))
3359 raise make_ex(ret, 'error getting group for image %s' % self.name)
3362 'name' : decode_cstr(info.name)
3364 rbd_group_info_cleanup(&info, sizeof(info))
3367 @requires_not_closed
3368 def is_exclusive_lock_owner(self):
3370 Get the status of the image exclusive lock.
3372 :returns: bool - true if the image is exclusively locked
3376 ret = rbd_is_exclusive_lock_owner(self.image, &owner)
3378 raise make_ex(ret, 'error getting lock status for image %s' % (self.name))
3381 @requires_not_closed
3382 def copy(self, dest_ioctx, dest_name, features=None, order=None,
3383 stripe_unit=None, stripe_count=None, data_pool=None):
3385 Copy the image to another location.
3387 :param dest_ioctx: determines which pool to copy into
3388 :type dest_ioctx: :class:`rados.Ioctx`
3389 :param dest_name: the name of the copy
3390 :type dest_name: str
3391 :param features: bitmask of features to enable; if set, must include layering
3393 :param order: the image is split into (2**order) byte objects
3395 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
3396 :type stripe_unit: int
3397 :param stripe_count: objects to stripe over before looping
3398 :type stripe_count: int
3399 :param data_pool: optional separate pool for data blocks
3400 :type data_pool: str
3401 :raises: :class:`TypeError`
3402 :raises: :class:`InvalidArgument`
3403 :raises: :class:`ImageExists`
3404 :raises: :class:`FunctionNotSupported`
3405 :raises: :class:`ArgumentOutOfRange`
3407 dest_name = cstr(dest_name, 'dest_name')
3408 data_pool = cstr(data_pool, 'data_pool', opt=True)
3410 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
3411 char *_dest_name = dest_name
3412 rbd_image_options_t opts
3414 rbd_image_options_create(&opts)
3416 if features is not None:
3417 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
3419 if order is not None:
3420 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
3422 if stripe_unit is not None:
3423 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
3425 if stripe_count is not None:
3426 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
3428 if data_pool is not None:
3429 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
3432 ret = rbd_copy3(self.image, _dest_ioctx, _dest_name, opts)
3434 rbd_image_options_destroy(opts)
3436 raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name))
3438 @requires_not_closed
3439 def deep_copy(self, dest_ioctx, dest_name, features=None, order=None,
3440 stripe_unit=None, stripe_count=None, data_pool=None):
3442 Deep copy the image to another location.
3444 :param dest_ioctx: determines which pool to copy into
3445 :type dest_ioctx: :class:`rados.Ioctx`
3446 :param dest_name: the name of the copy
3447 :type dest_name: str
3448 :param features: bitmask of features to enable; if set, must include layering
3450 :param order: the image is split into (2**order) byte objects
3452 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
3453 :type stripe_unit: int
3454 :param stripe_count: objects to stripe over before looping
3455 :type stripe_count: int
3456 :param data_pool: optional separate pool for data blocks
3457 :type data_pool: str
3458 :raises: :class:`TypeError`
3459 :raises: :class:`InvalidArgument`
3460 :raises: :class:`ImageExists`
3461 :raises: :class:`FunctionNotSupported`
3462 :raises: :class:`ArgumentOutOfRange`
3464 dest_name = cstr(dest_name, 'dest_name')
3465 data_pool = cstr(data_pool, 'data_pool', opt=True)
3467 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
3468 char *_dest_name = dest_name
3469 rbd_image_options_t opts
3471 rbd_image_options_create(&opts)
3473 if features is not None:
3474 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
3476 if order is not None:
3477 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
3479 if stripe_unit is not None:
3480 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
3482 if stripe_count is not None:
3483 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
3485 if data_pool is not None:
3486 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
3489 ret = rbd_deep_copy(self.image, _dest_ioctx, _dest_name, opts)
3491 rbd_image_options_destroy(opts)
3493 raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name))
3495 @requires_not_closed
3496 def list_snaps(self):
3498 Iterate over the snapshots of an image.
3500 :returns: :class:`SnapIterator`
3502 return SnapIterator(self)
3504 @requires_not_closed
3505 def create_snap(self, name, flags=0):
3507 Create a snapshot of the image.
3509 :param name: the name of the snapshot
3511 :raises: :class:`ImageExists`, :class:`InvalidArgument`
3513 name = cstr(name, 'name')
3516 uint32_t _flags = flags
3517 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3519 ret = rbd_snap_create2(self.image, _name, _flags, prog_cb, NULL)
3521 raise make_ex(ret, 'error creating snapshot %s from %s' % (name, self.name))
3523 @requires_not_closed
3524 def rename_snap(self, srcname, dstname):
3526 rename a snapshot of the image.
3528 :param srcname: the src name of the snapshot
3530 :param dstname: the dst name of the snapshot
3532 :raises: :class:`ImageExists`
3534 srcname = cstr(srcname, 'srcname')
3535 dstname = cstr(dstname, 'dstname')
3537 char *_srcname = srcname
3538 char *_dstname = dstname
3540 ret = rbd_snap_rename(self.image, _srcname, _dstname)
3542 raise make_ex(ret, 'error renaming snapshot of %s from %s to %s' % (self.name, srcname, dstname))
3544 @requires_not_closed
3545 def remove_snap(self, name):
3547 Delete a snapshot of the image.
3549 :param name: the name of the snapshot
3551 :raises: :class:`IOError`, :class:`ImageBusy`, :class:`ImageNotFound`
3553 name = cstr(name, 'name')
3554 cdef char *_name = name
3556 ret = rbd_snap_remove(self.image, _name)
3558 raise make_ex(ret, 'error removing snapshot %s from %s' % (name, self.name))
3560 @requires_not_closed
3561 def remove_snap2(self, name, flags):
3563 Delete a snapshot of the image.
3565 :param name: the name of the snapshot
3566 :param flags: the flags for removal
3568 :raises: :class:`IOError`, :class:`ImageBusy`
3570 self.require_not_closed()
3572 name = cstr(name, 'name')
3575 uint32_t _flags = flags
3576 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3578 ret = rbd_snap_remove2(self.image, _name, _flags, prog_cb, NULL)
3580 raise make_ex(ret, 'error removing snapshot %s from %s with flags %lx' % (name, self.name, flags))
3582 @requires_not_closed
3583 def remove_snap_by_id(self, snap_id):
3585 Delete a snapshot of the image by its id.
3587 :param id: the id of the snapshot
3589 :raises: :class:`IOError`, :class:`ImageBusy`
3592 uint64_t _snap_id = snap_id
3594 ret = rbd_snap_remove_by_id(self.image, _snap_id)
3596 raise make_ex(ret, 'error removing snapshot %s from %s' % (snap_id, self.name))
3598 @requires_not_closed
3599 def rollback_to_snap(self, name):
3601 Revert the image to its contents at a snapshot. This is a
3602 potentially expensive operation, since it rolls back each
3603 object individually.
3605 :param name: the snapshot to rollback to
3607 :raises: :class:`IOError`
3609 name = cstr(name, 'name')
3610 cdef char *_name = name
3612 ret = rbd_snap_rollback(self.image, _name)
3614 raise make_ex(ret, 'error rolling back image %s to snapshot %s' % (self.name, name))
3616 @requires_not_closed
3617 def protect_snap(self, name):
3619 Mark a snapshot as protected. This means it can't be deleted
3620 until it is unprotected.
3622 :param name: the snapshot to protect
3624 :raises: :class:`IOError`, :class:`ImageNotFound`
3626 name = cstr(name, 'name')
3627 cdef char *_name = name
3629 ret = rbd_snap_protect(self.image, _name)
3631 raise make_ex(ret, 'error protecting snapshot %s@%s' % (self.name, name))
3633 @requires_not_closed
3634 def unprotect_snap(self, name):
3636 Mark a snapshot unprotected. This allows it to be deleted if
3639 :param name: the snapshot to unprotect
3641 :raises: :class:`IOError`, :class:`ImageNotFound`
3643 name = cstr(name, 'name')
3644 cdef char *_name = name
3646 ret = rbd_snap_unprotect(self.image, _name)
3648 raise make_ex(ret, 'error unprotecting snapshot %s@%s' % (self.name, name))
3650 @requires_not_closed
3651 def is_protected_snap(self, name):
3653 Find out whether a snapshot is protected from deletion.
3655 :param name: the snapshot to check
3657 :returns: bool - whether the snapshot is protected
3658 :raises: :class:`IOError`, :class:`ImageNotFound`
3660 name = cstr(name, 'name')
3665 ret = rbd_snap_is_protected(self.image, _name, &is_protected)
3667 raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name))
3668 return is_protected == 1
3670 @requires_not_closed
3671 def snap_exists(self, name):
3673 Find out whether a snapshot is exists.
3675 :param name: the snapshot to check
3677 :returns: bool - whether the snapshot is exists
3679 name = cstr(name, 'name')
3682 bint _exists = False
3684 ret = rbd_snap_exists(self.image, _name, &_exists)
3686 raise make_ex(ret, 'error getting snapshot exists for %s' % self.name)
3687 return bool(_exists != 0)
3689 @requires_not_closed
3690 def get_snap_limit(self):
3692 Get the snapshot limit for an image.
3694 :returns: int - the snapshot limit for an image
3699 ret = rbd_snap_get_limit(self.image, &limit)
3701 raise make_ex(ret, 'error getting snapshot limit for %s' % self.name)
3704 @requires_not_closed
3705 def set_snap_limit(self, limit):
3707 Set the snapshot limit for an image.
3709 :param limit: the new limit to set
3712 uint64_t _limit = limit
3714 ret = rbd_snap_set_limit(self.image, _limit)
3716 raise make_ex(ret, 'error setting snapshot limit for %s' % self.name)
3719 @requires_not_closed
3720 def get_snap_timestamp(self, snap_id):
3722 Get the snapshot timestamp for an image.
3723 :param snap_id: the snapshot id of a snap shot
3724 :returns: datetime - the snapshot timestamp for an image
3728 uint64_t _snap_id = snap_id
3730 ret = rbd_snap_get_timestamp(self.image, _snap_id, ×tamp)
3732 raise make_ex(ret, 'error getting snapshot timestamp for image: %s, snap_id: %d' % (self.name, snap_id))
3733 return datetime.utcfromtimestamp(timestamp.tv_sec)
3735 @requires_not_closed
3736 def remove_snap_limit(self):
3738 Remove the snapshot limit for an image, essentially setting
3739 the limit to the maximum size allowed by the implementation.
3742 ret = rbd_snap_set_limit(self.image, UINT64_MAX)
3744 raise make_ex(ret, 'error removing snapshot limit for %s' % self.name)
3747 @requires_not_closed
3748 def set_snap(self, name):
3750 Set the snapshot to read from. Writes will raise ReadOnlyImage
3751 while a snapshot is set. Pass None to unset the snapshot
3752 (reads come from the current image) , and allow writing again.
3754 :param name: the snapshot to read from, or None to unset the snapshot
3755 :type name: str or None
3757 name = cstr(name, 'name', opt=True)
3758 cdef char *_name = opt_str(name)
3760 ret = rbd_snap_set(self.image, _name)
3762 raise make_ex(ret, 'error setting image %s to snapshot %s' % (self.name, name))
3764 @requires_not_closed
3765 def set_snap_by_id(self, snap_id):
3767 Set the snapshot to read from. Writes will raise ReadOnlyImage
3768 while a snapshot is set. Pass None to unset the snapshot
3769 (reads come from the current image) , and allow writing again.
3771 :param snap_id: the snapshot to read from, or None to unset the snapshot
3775 snap_id = _LIBRADOS_SNAP_HEAD
3776 cdef int64_t _snap_id = snap_id
3778 ret = rbd_snap_set_by_id(self.image, _snap_id)
3780 raise make_ex(ret, 'error setting image %s to snapshot %d' % (self.name, snap_id))
3782 @requires_not_closed
3783 def snap_get_name(self, snap_id):
3785 Get snapshot name by id.
3787 :param snap_id: the snapshot id
3789 :returns: str - snapshot name
3790 :raises: :class:`ImageNotFound`
3793 int ret = -errno.ERANGE
3794 int64_t _snap_id = snap_id
3796 char *image_name = NULL
3798 while ret == -errno.ERANGE:
3799 image_name = <char *>realloc_chk(image_name, size)
3801 ret = rbd_snap_get_name(self.image, _snap_id, image_name, &size)
3804 raise make_ex(ret, 'error snap_get_name.')
3805 return decode_cstr(image_name)
3809 @requires_not_closed
3810 def snap_get_id(self, snap_name):
3812 Get snapshot id by name.
3814 :param snap_name: the snapshot name
3815 :type snap_name: str
3816 :returns: int - snapshot id
3817 :raises: :class:`ImageNotFound`
3819 snap_name = cstr(snap_name, 'snap_name')
3821 const char *_snap_name = snap_name
3824 ret = rbd_snap_get_id(self.image, _snap_name, &snap_id)
3826 raise make_ex(ret, 'error snap_get_id.')
3829 @requires_not_closed
3830 def read(self, offset, length, fadvise_flags=0):
3832 Read data from the image. Raises :class:`InvalidArgument` if
3833 part of the range specified is outside the image.
3835 :param offset: the offset to start reading at
3837 :param length: how many bytes to read
3839 :param fadvise_flags: fadvise flags for this read
3840 :type fadvise_flags: int
3841 :returns: str - the data read
3842 :raises: :class:`InvalidArgument`, :class:`IOError`
3845 # This usage of the Python API allows us to construct a string
3846 # that librbd directly reads into, avoiding an extra copy. Although
3847 # strings are normally immutable, this usage is explicitly supported
3848 # for freshly created string objects.
3851 uint64_t _offset = offset
3852 size_t _length = length
3853 int _fadvise_flags = fadvise_flags
3854 PyObject* ret_s = NULL
3855 ret_s = PyBytes_FromStringAndSize(NULL, length)
3857 ret_buf = PyBytes_AsString(ret_s)
3859 ret = rbd_read2(self.image, _offset, _length, ret_buf,
3862 raise make_ex(ret, 'error reading %s %ld~%ld' % (self.name, offset, length))
3864 if ret != <ssize_t>length:
3865 _PyBytes_Resize(&ret_s, ret)
3867 return <object>ret_s
3869 # We DECREF unconditionally: the cast to object above will have
3870 # INCREFed if necessary. This also takes care of exceptions,
3871 # including if _PyString_Resize fails (that will free the string
3872 # itself and set ret_s to NULL, hence XDECREF).
3873 ref.Py_XDECREF(ret_s)
3875 @requires_not_closed
3876 def diff_iterate(self, offset, length, from_snapshot, iterate_cb,
3877 include_parent = True, whole_object = False):
3879 Iterate over the changed extents of an image.
3881 This will call iterate_cb with three arguments:
3883 (offset, length, exists)
3885 where the changed extent starts at offset bytes, continues for
3886 length bytes, and is full of data (if exists is True) or zeroes
3887 (if exists is False).
3889 If from_snapshot is None, it is interpreted as the beginning
3890 of time and this generates all allocated extents.
3892 The end version is whatever is currently selected (via set_snap)
3895 iterate_cb may raise an exception, which will abort the diff and will be
3896 propagated to the caller.
3898 Raises :class:`InvalidArgument` if from_snapshot is after
3899 the currently set snapshot.
3901 Raises :class:`ImageNotFound` if from_snapshot is not the name
3902 of a snapshot of the image.
3904 :param offset: start offset in bytes
3906 :param length: size of region to report on, in bytes
3908 :param from_snapshot: starting snapshot name, or None
3909 :type from_snapshot: str or None
3910 :param iterate_cb: function to call for each extent
3911 :type iterate_cb: function acception arguments for offset,
3913 :param include_parent: True if full history diff should include parent
3914 :type include_parent: bool
3915 :param whole_object: True if diff extents should cover whole object
3916 :type whole_object: bool
3917 :raises: :class:`InvalidArgument`, :class:`IOError`,
3918 :class:`ImageNotFound`
3920 from_snapshot = cstr(from_snapshot, 'from_snapshot', opt=True)
3922 char *_from_snapshot = opt_str(from_snapshot)
3923 uint64_t _offset = offset, _length = length
3924 uint8_t _include_parent = include_parent
3925 uint8_t _whole_object = whole_object
3927 ret = rbd_diff_iterate2(self.image, _from_snapshot, _offset,
3928 _length, _include_parent, _whole_object,
3929 &diff_iterate_cb, <void *>iterate_cb)
3931 msg = 'error generating diff from snapshot %s' % from_snapshot
3932 raise make_ex(ret, msg)
3934 @requires_not_closed
3935 def write(self, data, offset, fadvise_flags=0):
3937 Write data to the image. Raises :class:`InvalidArgument` if
3938 part of the write would fall outside the image.
3940 :param data: the data to be written
3942 :param offset: where to start writing data
3944 :param fadvise_flags: fadvise flags for this write
3945 :type fadvise_flags: int
3946 :returns: int - the number of bytes written
3947 :raises: :class:`IncompleteWriteError`, :class:`LogicError`,
3948 :class:`InvalidArgument`, :class:`IOError`
3950 if not isinstance(data, bytes):
3951 raise TypeError('data must be a byte string')
3953 uint64_t _offset = offset, length = len(data)
3955 int _fadvise_flags = fadvise_flags
3957 ret = rbd_write2(self.image, _offset, length, _data, _fadvise_flags)
3959 if ret == <ssize_t>length:
3962 raise make_ex(ret, "error writing to %s" % self.name)
3963 elif ret < <ssize_t>length:
3964 raise IncompleteWriteError("Wrote only %ld out of %ld bytes" % (ret, length))
3966 raise LogicError("logic error: rbd_write(%s) \
3967 returned %d, but %d was the maximum number of bytes it could have \
3968 written." % (self.name, ret, length))
3970 @requires_not_closed
3971 def discard(self, offset, length):
3973 Trim the range from the image. It will be logically filled
3976 cdef uint64_t _offset = offset, _length = length
3978 ret = rbd_discard(self.image, _offset, _length)
3980 msg = 'error discarding region %d~%d' % (offset, length)
3981 raise make_ex(ret, msg)
3983 @requires_not_closed
3984 def write_zeroes(self, offset, length, zero_flags = 0):
3986 Zero the range from the image. By default it will attempt to
3987 discard/unmap as much space as possible but any unaligned
3988 extent segments will still be zeroed.
3991 uint64_t _offset = offset, _length = length
3992 int _zero_flags = zero_flags
3994 ret = rbd_write_zeroes(self.image, _offset, _length,
3997 msg = 'error zeroing region %d~%d' % (offset, length)
3998 raise make_ex(ret, msg)
4000 @requires_not_closed
4003 Block until all writes are fully flushed if caching is enabled.
4006 ret = rbd_flush(self.image)
4008 raise make_ex(ret, 'error flushing image')
4010 @requires_not_closed
4011 def invalidate_cache(self):
4013 Drop any cached data for the image.
4016 ret = rbd_invalidate_cache(self.image)
4018 raise make_ex(ret, 'error invalidating cache')
4020 @requires_not_closed
4021 def stripe_unit(self):
4023 Return the stripe unit used for the image.
4025 cdef uint64_t stripe_unit
4027 ret = rbd_get_stripe_unit(self.image, &stripe_unit)
4029 raise make_ex(ret, 'error getting stripe unit for image %s' % (self.name))
4032 @requires_not_closed
4033 def stripe_count(self):
4035 Return the stripe count used for the image.
4037 cdef uint64_t stripe_count
4039 ret = rbd_get_stripe_count(self.image, &stripe_count)
4041 raise make_ex(ret, 'error getting stripe count for image %s' % (self.name))
4044 @requires_not_closed
4045 def create_timestamp(self):
4047 Return the create timestamp for the image.
4052 ret = rbd_get_create_timestamp(self.image, ×tamp)
4054 raise make_ex(ret, 'error getting create timestamp for image: %s' % (self.name))
4055 return datetime.utcfromtimestamp(timestamp.tv_sec)
4057 @requires_not_closed
4058 def access_timestamp(self):
4060 Return the access timestamp for the image.
4065 ret = rbd_get_access_timestamp(self.image, ×tamp)
4067 raise make_ex(ret, 'error getting access timestamp for image: %s' % (self.name))
4068 return datetime.fromtimestamp(timestamp.tv_sec)
4070 @requires_not_closed
4071 def modify_timestamp(self):
4073 Return the modify timestamp for the image.
4078 ret = rbd_get_modify_timestamp(self.image, ×tamp)
4080 raise make_ex(ret, 'error getting modify timestamp for image: %s' % (self.name))
4081 return datetime.fromtimestamp(timestamp.tv_sec)
4083 @requires_not_closed
4084 def flatten(self, on_progress=None):
4086 Flatten clone image (copy all blocks from parent to child)
4087 :param on_progress: optional progress callback function
4088 :type on_progress: callback function
4091 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
4092 void *_prog_arg = NULL
4094 _prog_cb = &progress_callback
4095 _prog_arg = <void *>on_progress
4097 ret = rbd_flatten_with_progress(self.image, _prog_cb, _prog_arg)
4099 raise make_ex(ret, "error flattening %s" % self.name)
4101 @requires_not_closed
4102 def sparsify(self, sparse_size):
4104 Reclaim space for zeroed image extents
4107 size_t _sparse_size = sparse_size
4109 ret = rbd_sparsify(self.image, _sparse_size)
4111 raise make_ex(ret, "error sparsifying %s" % self.name)
4113 @requires_not_closed
4114 def rebuild_object_map(self):
4116 Rebuild the object map for the image HEAD or currently set snapshot
4118 cdef librbd_progress_fn_t prog_cb = &no_op_progress_callback
4120 ret = rbd_rebuild_object_map(self.image, prog_cb, NULL)
4122 raise make_ex(ret, "error rebuilding object map %s" % self.name)
4124 @requires_not_closed
4125 def list_children(self):
4127 List children of the currently set snapshot (set via set_snap()).
4129 :returns: list - a list of (pool name, image name) tuples
4132 rbd_linked_image_spec_t *children = NULL
4133 size_t num_children = 10
4137 children = <rbd_linked_image_spec_t*>realloc_chk(
4138 children, num_children * sizeof(rbd_linked_image_spec_t))
4140 ret = rbd_list_children3(self.image, children,
4144 elif ret != -errno.ERANGE:
4145 raise make_ex(ret, 'error listing children.')
4147 return [(decode_cstr(x.pool_name), decode_cstr(x.image_name)) for x
4148 in children[:num_children] if not x.trash]
4151 rbd_linked_image_spec_list_cleanup(children, num_children)
4154 @requires_not_closed
4155 def list_children2(self):
4157 Iterate over the children of the image or its snapshot.
4159 :returns: :class:`ChildIterator`
4161 return ChildIterator(self)
4163 @requires_not_closed
4164 def list_descendants(self):
4166 Iterate over the descendants of the image.
4168 :returns: :class:`ChildIterator`
4170 return ChildIterator(self, True)
4172 @requires_not_closed
4173 def list_lockers(self):
4175 List clients that have locked the image and information
4178 :returns: dict - contains the following keys:
4180 * ``tag`` - the tag associated with the lock (every
4181 additional locker must use the same tag)
4182 * ``exclusive`` - boolean indicating whether the
4183 lock is exclusive or shared
4184 * ``lockers`` - a list of (client, cookie, address)
4188 size_t clients_size = 512, cookies_size = 512
4189 size_t addrs_size = 512, tag_size = 512
4191 char *c_clients = NULL
4192 char *c_cookies = NULL
4193 char *c_addrs = NULL
4198 c_clients = <char *>realloc_chk(c_clients, clients_size)
4199 c_cookies = <char *>realloc_chk(c_cookies, cookies_size)
4200 c_addrs = <char *>realloc_chk(c_addrs, addrs_size)
4201 c_tag = <char *>realloc_chk(c_tag, tag_size)
4203 ret = rbd_list_lockers(self.image, &exclusive,
4205 c_clients, &clients_size,
4206 c_cookies, &cookies_size,
4207 c_addrs, &addrs_size)
4210 elif ret != -errno.ERANGE:
4211 raise make_ex(ret, 'error listing images')
4214 clients = map(decode_cstr, c_clients[:clients_size - 1].split(b'\0'))
4215 cookies = map(decode_cstr, c_cookies[:cookies_size - 1].split(b'\0'))
4216 addrs = map(decode_cstr, c_addrs[:addrs_size - 1].split(b'\0'))
4218 'tag' : decode_cstr(c_tag),
4219 'exclusive' : exclusive == 1,
4220 'lockers' : list(zip(clients, cookies, addrs)),
4228 @requires_not_closed
4229 def lock_acquire(self, lock_mode):
4231 Acquire a managed lock on the image.
4233 :param lock_mode: lock mode to set
4234 :type lock_mode: int
4235 :raises: :class:`ImageBusy` if the lock could not be acquired
4238 rbd_lock_mode_t _lock_mode = lock_mode
4240 ret = rbd_lock_acquire(self.image, _lock_mode)
4242 raise make_ex(ret, 'error acquiring lock on image')
4244 @requires_not_closed
4245 def lock_release(self):
4247 Release a managed lock on the image that was previously acquired.
4250 ret = rbd_lock_release(self.image)
4252 raise make_ex(ret, 'error releasing lock on image')
4254 @requires_not_closed
4255 def lock_get_owners(self):
4257 Iterate over the lock owners of an image.
4259 :returns: :class:`LockOwnerIterator`
4261 return LockOwnerIterator(self)
4263 @requires_not_closed
4264 def lock_break(self, lock_mode, lock_owner):
4266 Break the image lock held by a another client.
4268 :param lock_owner: the owner of the lock to break
4269 :type lock_owner: str
4271 lock_owner = cstr(lock_owner, 'lock_owner')
4273 rbd_lock_mode_t _lock_mode = lock_mode
4274 char *_lock_owner = lock_owner
4276 ret = rbd_lock_break(self.image, _lock_mode, _lock_owner)
4278 raise make_ex(ret, 'error breaking lock on image')
4280 @requires_not_closed
4281 def lock_exclusive(self, cookie):
4283 Take an exclusive lock on the image.
4285 :raises: :class:`ImageBusy` if a different client or cookie locked it
4286 :class:`ImageExists` if the same client and cookie locked it
4288 cookie = cstr(cookie, 'cookie')
4289 cdef char *_cookie = cookie
4291 ret = rbd_lock_exclusive(self.image, _cookie)
4293 raise make_ex(ret, 'error acquiring exclusive lock on image')
4295 @requires_not_closed
4296 def lock_shared(self, cookie, tag):
4298 Take a shared lock on the image. The tag must match
4299 that of the existing lockers, if any.
4301 :raises: :class:`ImageBusy` if a different client or cookie locked it
4302 :class:`ImageExists` if the same client and cookie locked it
4304 cookie = cstr(cookie, 'cookie')
4305 tag = cstr(tag, 'tag')
4307 char *_cookie = cookie
4310 ret = rbd_lock_shared(self.image, _cookie, _tag)
4312 raise make_ex(ret, 'error acquiring shared lock on image')
4314 @requires_not_closed
4315 def unlock(self, cookie):
4317 Release a lock on the image that was locked by this rados client.
4319 cookie = cstr(cookie, 'cookie')
4320 cdef char *_cookie = cookie
4322 ret = rbd_unlock(self.image, _cookie)
4324 raise make_ex(ret, 'error unlocking image')
4326 @requires_not_closed
4327 def break_lock(self, client, cookie):
4329 Release a lock held by another rados client.
4331 client = cstr(client, 'client')
4332 cookie = cstr(cookie, 'cookie')
4334 char *_client = client
4335 char *_cookie = cookie
4337 ret = rbd_break_lock(self.image, _client, _cookie)
4339 raise make_ex(ret, 'error unlocking image')
4341 @requires_not_closed
4342 def mirror_image_enable(self, mode=RBD_MIRROR_IMAGE_MODE_JOURNAL):
4344 Enable mirroring for the image.
4346 cdef rbd_mirror_image_mode_t c_mode = mode
4348 ret = rbd_mirror_image_enable2(self.image, c_mode)
4350 raise make_ex(ret, 'error enabling mirroring for image %s' % self.name)
4352 @requires_not_closed
4353 def mirror_image_disable(self, force):
4355 Disable mirroring for the image.
4357 :param force: force disabling
4360 cdef bint c_force = force
4362 ret = rbd_mirror_image_disable(self.image, c_force)
4364 raise make_ex(ret, 'error disabling mirroring for image %s' % self.name)
4366 @requires_not_closed
4367 def mirror_image_promote(self, force):
4369 Promote the image to primary for mirroring.
4371 :param force: force promoting
4374 cdef bint c_force = force
4376 ret = rbd_mirror_image_promote(self.image, c_force)
4378 raise make_ex(ret, 'error promoting image %s to primary' % self.name)
4380 @requires_not_closed
4381 def mirror_image_demote(self):
4383 Demote the image to secondary for mirroring.
4386 ret = rbd_mirror_image_demote(self.image)
4388 raise make_ex(ret, 'error demoting image %s to secondary' % self.name)
4390 @requires_not_closed
4391 def mirror_image_resync(self):
4393 Flag the image to resync.
4396 ret = rbd_mirror_image_resync(self.image)
4398 raise make_ex(ret, 'error to resync image %s' % self.name)
4400 @requires_not_closed
4401 def mirror_image_create_snapshot(self, flags=0):
4403 Create mirror snapshot.
4405 :param flags: create snapshot flags
4407 :returns: int - the snapshot Id
4410 uint32_t _flags = flags
4413 ret = rbd_mirror_image_create_snapshot2(self.image, _flags,
4416 raise make_ex(ret, 'error creating mirror snapshot for image %s' %
4420 @requires_not_closed
4421 def aio_mirror_image_create_snapshot(self, flags, oncomplete):
4423 Asynchronously create mirror snapshot.
4425 Raises :class:`InvalidArgument` if the image is not in mirror
4428 oncomplete will be called with the created snap ID as
4429 well as the completion:
4431 oncomplete(completion, snap_id)
4433 :param flags: create snapshot flags
4435 :param oncomplete: what to do when the read is complete
4436 :type oncomplete: completion
4437 :returns: :class:`Completion` - the completion object
4438 :raises: :class:`InvalidArgument`
4441 uint32_t _flags = flags
4442 Completion completion
4444 def oncomplete_(completion_v):
4445 cdef Completion _completion_v = completion_v
4446 return_value = _completion_v.get_return_value()
4447 snap_id = <object>(<uint64_t *>_completion_v.buf)[0] \
4448 if return_value >= 0 else None
4449 return oncomplete(_completion_v, snap_id)
4451 completion = self.__get_completion(oncomplete_)
4452 completion.buf = PyBytes_FromStringAndSize(NULL, sizeof(uint64_t))
4454 completion.__persist()
4456 ret = rbd_aio_mirror_image_create_snapshot(self.image, _flags,
4457 <uint64_t *>completion.buf,
4458 completion.rbd_comp)
4460 raise make_ex(ret, 'error creating mirror snapshot for image %s' %
4463 completion.__unpersist()
4468 @requires_not_closed
4469 def mirror_image_get_info(self):
4471 Get mirror info for the image.
4473 :returns: dict - contains the following keys:
4475 * ``global_id`` (str) - image global id
4477 * ``state`` (int) - mirror state
4479 * ``primary`` (bool) - is image primary
4481 cdef rbd_mirror_image_info_t c_info
4483 ret = rbd_mirror_image_get_info(self.image, &c_info, sizeof(c_info))
4485 raise make_ex(ret, 'error getting mirror info for image %s' % self.name)
4487 'global_id' : decode_cstr(c_info.global_id),
4488 'state' : int(c_info.state),
4489 'primary' : c_info.primary,
4491 rbd_mirror_image_get_info_cleanup(&c_info)
4494 @requires_not_closed
4495 def aio_mirror_image_get_info(self, oncomplete):
4497 Asynchronously get mirror info for the image.
4499 oncomplete will be called with the returned info as
4500 well as the completion:
4502 oncomplete(completion, info)
4504 :param oncomplete: what to do when get info is complete
4505 :type oncomplete: completion
4506 :returns: :class:`Completion` - the completion object
4509 Completion completion
4511 def oncomplete_(completion_v):
4513 Completion _completion_v = completion_v
4514 rbd_mirror_image_info_t *c_info = <rbd_mirror_image_info_t *>_completion_v.buf
4516 'global_id' : decode_cstr(c_info[0].global_id),
4517 'state' : int(c_info[0].state),
4518 'primary' : c_info[0].primary,
4520 rbd_mirror_image_get_info_cleanup(c_info)
4521 return oncomplete(_completion_v, info)
4523 completion = self.__get_completion(oncomplete_)
4524 completion.buf = PyBytes_FromStringAndSize(
4525 NULL, sizeof(rbd_mirror_image_info_t))
4527 completion.__persist()
4529 ret = rbd_aio_mirror_image_get_info(
4530 self.image, <rbd_mirror_image_info_t *>completion.buf,
4531 sizeof(rbd_mirror_image_info_t), completion.rbd_comp)
4534 ret, 'error getting mirror info for image %s' % self.name)
4536 completion.__unpersist()
4541 @requires_not_closed
4542 def mirror_image_get_mode(self):
4544 Get mirror mode for the image.
4546 :returns: int - mirror mode
4548 cdef rbd_mirror_image_mode_t c_mode
4550 ret = rbd_mirror_image_get_mode(self.image, &c_mode)
4552 raise make_ex(ret, 'error getting mirror mode for image %s' % self.name)
4555 @requires_not_closed
4556 def aio_mirror_image_get_mode(self, oncomplete):
4558 Asynchronously get mirror mode for the image.
4560 oncomplete will be called with the returned mode as
4561 well as the completion:
4563 oncomplete(completion, mode)
4565 :param oncomplete: what to do when get info is complete
4566 :type oncomplete: completion
4567 :returns: :class:`Completion` - the completion object
4570 Completion completion
4572 def oncomplete_(completion_v):
4573 cdef Completion _completion_v = completion_v
4574 return_value = _completion_v.get_return_value()
4575 mode = int((<rbd_mirror_image_mode_t *>_completion_v.buf)[0]) \
4576 if return_value >= 0 else None
4577 return oncomplete(_completion_v, mode)
4579 completion = self.__get_completion(oncomplete_)
4580 completion.buf = PyBytes_FromStringAndSize(
4581 NULL, sizeof(rbd_mirror_image_mode_t))
4583 completion.__persist()
4585 ret = rbd_aio_mirror_image_get_mode(
4586 self.image, <rbd_mirror_image_mode_t *>completion.buf,
4587 completion.rbd_comp)
4590 ret, 'error getting mirror mode for image %s' % self.name)
4592 completion.__unpersist()
4597 @requires_not_closed
4598 def mirror_image_get_status(self):
4600 Get mirror status for the image.
4602 :returns: dict - contains the following keys:
4604 * ``name`` (str) - mirror image name
4606 * ``id`` (str) - mirror image id
4608 * ``info`` (dict) - mirror image info
4610 * ``state`` (int) - status mirror state
4612 * ``description`` (str) - status description
4614 * ``last_update`` (datetime) - last status update time
4616 * ``up`` (bool) - is mirroring agent up
4618 * ``remote_statuses`` (array) -
4620 * ``mirror_uuid`` (str) - remote mirror uuid
4622 * ``state`` (int) - status mirror state
4624 * ``description`` (str) - status description
4626 * ``last_update`` (datetime) - last status update time
4628 * ``up`` (bool) - is mirroring agent up
4631 rbd_mirror_image_site_status_t *s_status
4632 rbd_mirror_image_global_status_t c_status
4635 ret = rbd_mirror_image_get_global_status(self.image, &c_status,
4638 raise make_ex(ret, 'error getting mirror status for image %s' % self.name)
4642 for i in range(c_status.site_statuses_count):
4643 s_status = &c_status.site_statuses[i]
4645 'state' : s_status.state,
4646 'description' : decode_cstr(s_status.description),
4647 'last_update' : datetime.utcfromtimestamp(s_status.last_update),
4650 mirror_uuid = decode_cstr(s_status.mirror_uuid)
4651 if mirror_uuid == '':
4652 local_status = site_status
4654 site_status['mirror_uuid'] = mirror_uuid
4655 site_statuses.append(site_status)
4657 'name': decode_cstr(c_status.name),
4660 'global_id' : decode_cstr(c_status.info.global_id),
4661 'state' : int(c_status.info.state),
4662 'primary' : c_status.info.primary,
4664 'remote_statuses': site_statuses,
4667 status.update(local_status)
4669 rbd_mirror_image_global_status_cleanup(&c_status)
4672 @requires_not_closed
4673 def mirror_image_get_instance_id(self):
4675 Get mirror instance id for the image.
4677 :returns: str - instance id
4680 int ret = -errno.ERANGE
4682 char *instance_id = NULL
4684 while ret == -errno.ERANGE and size <= 4096:
4685 instance_id = <char *>realloc_chk(instance_id, size)
4687 ret = rbd_mirror_image_get_instance_id(self.image,
4691 'error getting mirror instance id for image %s' %
4693 return decode_cstr(instance_id)
4697 @requires_not_closed
4698 def aio_read(self, offset, length, oncomplete, fadvise_flags=0):
4700 Asynchronously read data from the image
4702 Raises :class:`InvalidArgument` if part of the range specified is
4705 oncomplete will be called with the returned read value as
4706 well as the completion:
4708 oncomplete(completion, data_read)
4710 :param offset: the offset to start reading at
4712 :param length: how many bytes to read
4714 :param oncomplete: what to do when the read is complete
4715 :type oncomplete: completion
4716 :param fadvise_flags: fadvise flags for this read
4717 :type fadvise_flags: int
4718 :returns: :class:`Completion` - the completion object
4719 :raises: :class:`InvalidArgument`, :class:`IOError`
4723 uint64_t _offset = offset
4724 size_t _length = length
4725 int _fadvise_flags = fadvise_flags
4726 Completion completion
4728 def oncomplete_(completion_v):
4729 cdef Completion _completion_v = completion_v
4730 return_value = _completion_v.get_return_value()
4731 if return_value > 0 and return_value != length:
4732 _PyBytes_Resize(&_completion_v.buf, return_value)
4733 return oncomplete(_completion_v, <object>_completion_v.buf if return_value >= 0 else None)
4735 completion = self.__get_completion(oncomplete_)
4736 completion.buf = PyBytes_FromStringAndSize(NULL, length)
4737 ret_buf = PyBytes_AsString(completion.buf)
4739 completion.__persist()
4741 ret = rbd_aio_read2(self.image, _offset, _length, ret_buf,
4742 completion.rbd_comp, _fadvise_flags)
4744 raise make_ex(ret, 'error reading %s %ld~%ld' %
4745 (self.name, offset, length))
4747 completion.__unpersist()
4752 @requires_not_closed
4753 def aio_write(self, data, offset, oncomplete, fadvise_flags=0):
4755 Asynchronously write data to the image
4757 Raises :class:`InvalidArgument` if part of the write would fall outside
4760 oncomplete will be called with the completion:
4762 oncomplete(completion)
4764 :param data: the data to be written
4766 :param offset: the offset to start writing at
4768 :param oncomplete: what to do when the write is complete
4769 :type oncomplete: completion
4770 :param fadvise_flags: fadvise flags for this write
4771 :type fadvise_flags: int
4772 :returns: :class:`Completion` - the completion object
4773 :raises: :class:`InvalidArgument`, :class:`IOError`
4776 uint64_t _offset = offset
4778 size_t _length = len(data)
4779 int _fadvise_flags = fadvise_flags
4780 Completion completion
4782 completion = self.__get_completion(oncomplete)
4784 completion.__persist()
4786 ret = rbd_aio_write2(self.image, _offset, _length, _data,
4787 completion.rbd_comp, _fadvise_flags)
4789 raise make_ex(ret, 'error writing %s %ld~%ld' %
4790 (self.name, offset, _length))
4792 completion.__unpersist()
4797 @requires_not_closed
4798 def aio_discard(self, offset, length, oncomplete):
4800 Asynchronously trim the range from the image. It will be logically
4804 uint64_t _offset = offset
4805 size_t _length = length
4806 Completion completion
4808 completion = self.__get_completion(oncomplete)
4810 completion.__persist()
4812 ret = rbd_aio_discard(self.image, _offset, _length,
4813 completion.rbd_comp)
4815 raise make_ex(ret, 'error discarding %s %ld~%ld' %
4816 (self.name, offset, _length))
4818 completion.__unpersist()
4823 @requires_not_closed
4824 def aio_write_zeroes(self, offset, length, oncomplete, zero_flags = 0):
4826 Asynchronously Zero the range from the image. By default it will attempt
4827 to discard/unmap as much space as possible but any unaligned extent
4828 segments will still be zeroed.
4831 uint64_t _offset = offset
4832 size_t _length = length
4833 int _zero_flags = zero_flags
4834 Completion completion
4836 completion = self.__get_completion(oncomplete)
4838 completion.__persist()
4840 ret = rbd_aio_write_zeroes(self.image, _offset, _length,
4841 completion.rbd_comp, _zero_flags, 0)
4843 raise make_ex(ret, 'error zeroing %s %ld~%ld' %
4844 (self.name, offset, length))
4846 completion.__unpersist()
4851 @requires_not_closed
4852 def aio_flush(self, oncomplete):
4854 Asynchronously wait until all writes are fully flushed if caching is
4857 cdef Completion completion = self.__get_completion(oncomplete)
4859 completion.__persist()
4861 ret = rbd_aio_flush(self.image, completion.rbd_comp)
4863 raise make_ex(ret, 'error flushing')
4865 completion.__unpersist()
4870 @requires_not_closed
4871 def metadata_get(self, key):
4873 Get image metadata for the given key.
4875 :param key: metadata key
4877 :returns: str - metadata value
4879 key = cstr(key, 'key')
4887 value = <char *>realloc_chk(value, size)
4889 ret = rbd_metadata_get(self.image, _key, value, &size)
4890 if ret != -errno.ERANGE:
4892 if ret == -errno.ENOENT:
4893 raise KeyError('no metadata %s for image %s' % (key, self.name))
4895 raise make_ex(ret, 'error getting metadata %s for image %s' %
4897 return decode_cstr(value)
4901 @requires_not_closed
4902 def metadata_set(self, key, value):
4904 Set image metadata for the given key.
4906 :param key: metadata key
4908 :param value: metadata value
4911 key = cstr(key, 'key')
4912 value = cstr(value, 'value')
4915 char *_value = value
4917 ret = rbd_metadata_set(self.image, _key, _value)
4920 raise make_ex(ret, 'error setting metadata %s for image %s' %
4923 @requires_not_closed
4924 def metadata_remove(self, key):
4926 Remove image metadata for the given key.
4928 :param key: metadata key
4931 key = cstr(key, 'key')
4935 ret = rbd_metadata_remove(self.image, _key)
4937 if ret == -errno.ENOENT:
4938 raise KeyError('no metadata %s for image %s' % (key, self.name))
4940 raise make_ex(ret, 'error removing metadata %s for image %s' %
4943 @requires_not_closed
4944 def metadata_list(self):
4946 List image metadata.
4948 :returns: :class:`MetadataIterator`
4950 return MetadataIterator(self)
4952 @requires_not_closed
4953 def watchers_list(self):
4955 List image watchers.
4957 :returns: :class:`WatcherIterator`
4959 return WatcherIterator(self)
4961 @requires_not_closed
4962 def config_list(self):
4964 List image-level config overrides.
4966 :returns: :class:`ConfigImageIterator`
4968 return ConfigImageIterator(self)
4970 @requires_not_closed
4971 def config_set(self, key, value):
4973 Set an image-level configuration override.
4980 conf_key = 'conf_' + key
4981 conf_key = cstr(conf_key, 'key')
4982 value = cstr(value, 'value')
4984 char *_key = conf_key
4985 char *_value = value
4987 ret = rbd_metadata_set(self.image, _key, _value)
4990 raise make_ex(ret, 'error setting config %s for image %s' %
4993 @requires_not_closed
4994 def config_get(self, key):
4996 Get an image-level configuration override.
5000 :returns: str - value
5002 conf_key = 'conf_' + key
5003 conf_key = cstr(conf_key, 'key')
5005 char *_key = conf_key
5011 value = <char *>realloc_chk(value, size)
5013 ret = rbd_metadata_get(self.image, _key, value, &size)
5014 if ret != -errno.ERANGE:
5016 if ret == -errno.ENOENT:
5017 raise KeyError('no config %s for image %s' % (key, self.name))
5019 raise make_ex(ret, 'error getting config %s for image %s' %
5021 return decode_cstr(value)
5025 @requires_not_closed
5026 def config_remove(self, key):
5028 Remove an image-level configuration override.
5033 conf_key = 'conf_' + key
5034 conf_key = cstr(conf_key, 'key')
5036 char *_key = conf_key
5038 ret = rbd_metadata_remove(self.image, _key)
5040 if ret == -errno.ENOENT:
5041 raise KeyError('no config %s for image %s' % (key, self.name))
5043 raise make_ex(ret, 'error removing config %s for image %s' %
5046 @requires_not_closed
5047 def snap_get_namespace_type(self, snap_id):
5049 Get the snapshot namespace type.
5050 :param snap_id: the snapshot id of a snap shot
5054 rbd_snap_namespace_type_t namespace_type
5055 uint64_t _snap_id = snap_id
5057 ret = rbd_snap_get_namespace_type(self.image, _snap_id, &namespace_type)
5059 raise make_ex(ret, 'error getting snapshot namespace type for image: %s, snap_id: %d' % (self.name, snap_id))
5061 return namespace_type
5063 @requires_not_closed
5064 def snap_get_group_namespace(self, snap_id):
5066 get the group namespace details.
5067 :param snap_id: the snapshot id of the group snapshot
5069 :returns: dict - contains the following keys:
5071 * ``pool`` (int) - pool id
5073 * ``name`` (str) - group name
5075 * ``snap_name`` (str) - group snap name
5078 rbd_snap_group_namespace_t group_namespace
5079 uint64_t _snap_id = snap_id
5081 ret = rbd_snap_get_group_namespace(self.image, _snap_id,
5083 sizeof(rbd_snap_group_namespace_t))
5085 raise make_ex(ret, 'error getting snapshot group namespace for image: %s, snap_id: %d' % (self.name, snap_id))
5088 'pool' : group_namespace.group_pool,
5089 'name' : decode_cstr(group_namespace.group_name),
5090 'snap_name' : decode_cstr(group_namespace.group_snap_name)
5092 rbd_snap_group_namespace_cleanup(&group_namespace,
5093 sizeof(rbd_snap_group_namespace_t))
5096 @requires_not_closed
5097 def snap_get_trash_namespace(self, snap_id):
5099 get the trash namespace details.
5100 :param snap_id: the snapshot id of the trash snapshot
5102 :returns: dict - contains the following keys:
5104 * ``original_name`` (str) - original snap name
5107 uint64_t _snap_id = snap_id
5112 _name = <char*>realloc_chk(_name, _size);
5114 ret = rbd_snap_get_trash_namespace(self.image, _snap_id,
5118 elif ret != -errno.ERANGE:
5119 raise make_ex(ret, 'error getting snapshot trash '
5120 'namespace image: %s, snap_id: %d' % (self.name, snap_id))
5122 'original_name' : decode_cstr(_name)
5127 @requires_not_closed
5128 def snap_get_mirror_namespace(self, snap_id):
5130 get the mirror namespace details.
5131 :param snap_id: the snapshot id of the mirror snapshot
5133 :returns: dict - contains the following keys:
5135 * ``state`` (int) - the snapshot state
5137 * ``mirror_peer_uuids`` (list) - mirror peer uuids
5139 * ``complete`` (bool) - True if snapshot is complete
5141 * ``primary_mirror_uuid`` (str) - primary mirror uuid
5143 * ``primary_snap_id`` (int) - primary snapshot Id
5145 * ``last_copied_object_number`` (int) - last copied object number
5148 rbd_snap_mirror_namespace_t sn
5149 uint64_t _snap_id = snap_id
5151 ret = rbd_snap_get_mirror_namespace(
5152 self.image, _snap_id, &sn,
5153 sizeof(rbd_snap_mirror_namespace_t))
5155 raise make_ex(ret, 'error getting snapshot mirror '
5156 'namespace for image: %s, snap_id: %d' %
5157 (self.name, snap_id))
5159 cdef char *p = sn.mirror_peer_uuids
5160 for i in range(sn.mirror_peer_uuids_count):
5161 uuid = decode_cstr(p)
5166 'mirror_peer_uuids' : uuids,
5167 'complete' : sn.complete,
5168 'primary_mirror_uuid' : decode_cstr(sn.primary_mirror_uuid),
5169 'primary_snap_id' : sn.primary_snap_id,
5170 'last_copied_object_number' : sn.last_copied_object_number,
5172 rbd_snap_mirror_namespace_cleanup(
5173 &sn, sizeof(rbd_snap_mirror_namespace_t))
5176 @requires_not_closed
5177 def encryption_format(self, format, passphrase,
5178 cipher_alg=RBD_ENCRYPTION_ALGORITHM_AES256):
5179 passphrase = cstr(passphrase, "passphrase")
5180 cdef rbd_encryption_format_t _format = format
5181 cdef rbd_encryption_luks1_format_options_t _luks1_opts
5182 cdef rbd_encryption_luks2_format_options_t _luks2_opts
5183 cdef char* _passphrase = passphrase
5185 if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
5186 _luks1_opts.alg = cipher_alg
5187 _luks1_opts.passphrase = _passphrase
5188 _luks1_opts.passphrase_size = len(passphrase)
5190 ret = rbd_encryption_format(self.image, _format, &_luks1_opts,
5191 sizeof(_luks1_opts))
5195 'error formatting image %s with format luks1' % self.name)
5196 elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
5197 _luks2_opts.alg = cipher_alg
5198 _luks2_opts.passphrase = _passphrase
5199 _luks2_opts.passphrase_size = len(passphrase)
5201 ret = rbd_encryption_format(self.image, _format, &_luks2_opts,
5202 sizeof(_luks2_opts))
5206 'error formatting image %s with format luks2' % self.name)
5208 raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
5210 @requires_not_closed
5211 def encryption_load(self, format, passphrase):
5212 passphrase = cstr(passphrase, "passphrase")
5213 cdef rbd_encryption_format_t _format = format
5214 cdef rbd_encryption_luks1_format_options_t _luks1_opts
5215 cdef rbd_encryption_luks2_format_options_t _luks2_opts
5216 cdef rbd_encryption_luks_format_options_t _luks_opts
5217 cdef char* _passphrase = passphrase
5219 if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
5220 _luks1_opts.passphrase = _passphrase
5221 _luks1_opts.passphrase_size = len(passphrase)
5223 ret = rbd_encryption_load(self.image, _format, &_luks1_opts,
5224 sizeof(_luks1_opts))
5228 ('error loading encryption on image %s '
5229 'with format luks1') % self.name)
5230 elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
5231 _luks2_opts.passphrase = _passphrase
5232 _luks2_opts.passphrase_size = len(passphrase)
5234 ret = rbd_encryption_load(self.image, _format, &_luks2_opts,
5235 sizeof(_luks2_opts))
5239 ('error loading encryption on image %s '
5240 'with format luks2') % self.name)
5241 elif (format == RBD_ENCRYPTION_FORMAT_LUKS):
5242 _luks_opts.passphrase = _passphrase
5243 _luks_opts.passphrase_size = len(passphrase)
5245 ret = rbd_encryption_load(self.image, _format, &_luks_opts,
5250 ('error loading encryption on image %s '
5251 'with format luks') % self.name)
5253 raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
5255 @requires_not_closed
5256 def encryption_load2(self, specs):
5257 cdef rbd_encryption_spec_t *_specs
5258 cdef rbd_encryption_luks1_format_options_t* _luks1_opts
5259 cdef rbd_encryption_luks2_format_options_t* _luks2_opts
5260 cdef rbd_encryption_luks_format_options_t* _luks_opts
5261 cdef size_t spec_count = len(specs)
5263 _specs = <rbd_encryption_spec_t *>malloc(len(specs) *
5264 sizeof(rbd_encryption_spec_t))
5266 raise MemoryError("malloc failed")
5268 memset(<void *>_specs, 0, len(specs) * sizeof(rbd_encryption_spec_t))
5270 for i in range(len(specs)):
5271 format, passphrase = specs[i]
5272 passphrase = cstr(passphrase, "specs[%d][1]" % i)
5273 _specs[i].format = format
5274 if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
5275 _luks1_opts = <rbd_encryption_luks1_format_options_t *>malloc(
5276 sizeof(rbd_encryption_luks1_format_options_t))
5277 if _luks1_opts == NULL:
5278 raise MemoryError("malloc failed")
5279 _luks1_opts.passphrase = passphrase
5280 _luks1_opts.passphrase_size = len(passphrase)
5281 _specs[i].opts = <rbd_encryption_options_t> _luks1_opts
5282 _specs[i].opts_size = sizeof(
5283 rbd_encryption_luks1_format_options_t)
5284 elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
5285 _luks2_opts = <rbd_encryption_luks2_format_options_t *>malloc(
5286 sizeof(rbd_encryption_luks2_format_options_t))
5287 if _luks2_opts == NULL:
5288 raise MemoryError("malloc failed")
5289 _luks2_opts.passphrase = passphrase
5290 _luks2_opts.passphrase_size = len(passphrase)
5291 _specs[i].opts = <rbd_encryption_options_t> _luks2_opts
5292 _specs[i].opts_size = sizeof(
5293 rbd_encryption_luks2_format_options_t)
5294 elif (format == RBD_ENCRYPTION_FORMAT_LUKS):
5295 _luks_opts = <rbd_encryption_luks_format_options_t *>malloc(
5296 sizeof(rbd_encryption_luks_format_options_t))
5297 if _luks_opts == NULL:
5298 raise MemoryError("malloc failed")
5299 _luks_opts.passphrase = passphrase
5300 _luks_opts.passphrase_size = len(passphrase)
5301 _specs[i].opts = <rbd_encryption_options_t> _luks_opts
5302 _specs[i].opts_size = sizeof(
5303 rbd_encryption_luks_format_options_t)
5307 'specs[%d][1]: Unsupported encryption format' % i)
5309 ret = rbd_encryption_load2(self.image, _specs, spec_count)
5313 'error loading encryption on image %s' % self.name)
5315 for i in range(len(specs)):
5316 if _specs[i].opts != NULL:
5317 free(_specs[i].opts)
5321 cdef class ImageIterator(object):
5323 Iterator over RBD images in a pool
5325 Yields a dictionary containing information about the images
5329 * ``id`` (str) - image id
5331 * ``name`` (str) - image name
5333 cdef rados_ioctx_t ioctx
5334 cdef rbd_image_spec_t *images
5335 cdef size_t num_images
5337 def __init__(self, ioctx):
5338 self.ioctx = convert_ioctx(ioctx)
5340 self.num_images = 1024
5342 self.images = <rbd_image_spec_t*>realloc_chk(
5343 self.images, self.num_images * sizeof(rbd_image_spec_t))
5345 ret = rbd_list2(self.ioctx, self.images, &self.num_images)
5348 elif ret == -errno.ERANGE:
5349 self.num_images *= 2
5351 raise make_ex(ret, 'error listing images.')
5354 for i in range(self.num_images):
5356 'id' : decode_cstr(self.images[i].id),
5357 'name' : decode_cstr(self.images[i].name)
5360 def __dealloc__(self):
5362 rbd_image_spec_list_cleanup(self.images, self.num_images)
5366 cdef class LockOwnerIterator(object):
5368 Iterator over managed lock owners for an image
5370 Yields a dictionary containing information about the image's lock
5374 * ``mode`` (int) - active lock mode
5376 * ``owner`` (str) - lock owner name
5380 rbd_lock_mode_t lock_mode
5382 size_t num_lock_owners
5385 def __init__(self, Image image):
5386 image.require_not_closed()
5389 self.lock_owners = NULL
5390 self.num_lock_owners = 8
5392 self.lock_owners = <char**>realloc_chk(self.lock_owners,
5393 self.num_lock_owners *
5396 ret = rbd_lock_get_owners(image.image, &self.lock_mode,
5398 &self.num_lock_owners)
5401 elif ret == -errno.ENOENT:
5402 self.num_lock_owners = 0
5404 elif ret != -errno.ERANGE:
5405 raise make_ex(ret, 'error listing lock owners for image %s' % image.name)
5408 for i in range(self.num_lock_owners):
5410 'mode' : int(self.lock_mode),
5411 'owner' : decode_cstr(self.lock_owners[i]),
5414 def __dealloc__(self):
5415 if self.lock_owners:
5416 rbd_lock_get_owners_cleanup(self.lock_owners, self.num_lock_owners)
5417 free(self.lock_owners)
5419 cdef class MetadataIterator(object):
5421 Iterator over metadata list for an image.
5423 Yields ``(key, value)`` tuple.
5425 * ``key`` (str) - metadata key
5426 * ``value`` (str) - metadata value
5436 def __init__(self, Image image):
5437 image.require_not_closed()
5440 self.c_image = image.image
5441 self.last_read = strdup("")
5443 self.get_next_chunk()
5446 while len(self.next_chunk) > 0:
5447 for pair in self.next_chunk:
5449 if len(self.next_chunk) < self.max_read:
5451 self.get_next_chunk()
5453 def __dealloc__(self):
5455 free(self.last_read)
5457 def get_next_chunk(self):
5458 self.image.require_not_closed()
5462 size_t keys_size = 4096
5464 size_t vals_size = 4096
5467 c_keys = <char *>realloc_chk(c_keys, keys_size)
5468 c_vals = <char *>realloc_chk(c_vals, vals_size)
5470 ret = rbd_metadata_list(self.c_image, self.last_read,
5471 self.max_read, c_keys, &keys_size,
5475 elif ret != -errno.ERANGE:
5476 raise make_ex(ret, 'error listing metadata for image %s' %
5478 keys = [decode_cstr(key) for key in
5479 c_keys[:keys_size].split(b'\0') if key]
5480 vals = [decode_cstr(val) for val in
5481 c_vals[:vals_size].split(b'\0') if val]
5483 last_read = cstr(keys[-1], 'last_read')
5484 free(self.last_read)
5485 self.last_read = strdup(last_read)
5486 self.next_chunk = list(zip(keys, vals))
5491 cdef class SnapIterator(object):
5493 Iterator over snapshot info for an image.
5495 Yields a dictionary containing information about a snapshot.
5499 * ``id`` (int) - numeric identifier of the snapshot
5501 * ``size`` (int) - size of the image at the time of snapshot (in bytes)
5503 * ``name`` (str) - name of the snapshot
5505 * ``namespace`` (int) - enum for snap namespace
5507 * ``group`` (dict) - optional for group namespace snapshots
5509 * ``trash`` (dict) - optional for trash namespace snapshots
5511 * ``mirror`` (dict) - optional for mirror namespace snapshots
5514 cdef rbd_snap_info_t *snaps
5518 def __init__(self, Image image):
5519 image.require_not_closed()
5525 self.snaps = <rbd_snap_info_t*>realloc_chk(self.snaps,
5527 sizeof(rbd_snap_info_t))
5529 ret = rbd_snap_list(image.image, self.snaps, &self.num_snaps)
5531 self.num_snaps = ret
5533 elif ret != -errno.ERANGE:
5534 raise make_ex(ret, 'error listing snapshots for image %s' % image.name)
5537 for i in range(self.num_snaps):
5539 'id' : self.snaps[i].id,
5540 'size' : self.snaps[i].size,
5541 'name' : decode_cstr(self.snaps[i].name),
5542 'namespace' : self.image.snap_get_namespace_type(self.snaps[i].id)
5544 if s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_GROUP:
5546 group = self.image.snap_get_group_namespace(self.snaps[i].id)
5550 elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_TRASH:
5552 trash = self.image.snap_get_trash_namespace(self.snaps[i].id)
5556 elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_MIRROR:
5558 mirror = self.image.snap_get_mirror_namespace(
5562 s['mirror'] = mirror
5565 def __dealloc__(self):
5567 rbd_snap_list_end(self.snaps)
5570 cdef class TrashIterator(object):
5572 Iterator over trash entries.
5574 Yields a dictionary containing trash info of an image.
5578 * `id` (str) - image id
5580 * `name` (str) - image name
5582 * `source` (str) - source of deletion
5584 * `deletion_time` (datetime) - time of deletion
5586 * `deferment_end_time` (datetime) - time that an image is allowed to be
5593 rbd_trash_image_info_t *entries
5595 def __init__(self, ioctx):
5596 self.ioctx = convert_ioctx(ioctx)
5597 self.num_entries = 1024
5600 self.entries = <rbd_trash_image_info_t*>realloc_chk(self.entries,
5602 sizeof(rbd_trash_image_info_t))
5604 ret = rbd_trash_list(self.ioctx, self.entries, &self.num_entries)
5606 self.num_entries = ret
5608 elif ret != -errno.ERANGE:
5609 raise make_ex(ret, 'error listing trash entries')
5611 __source_string = ['USER', 'MIRRORING']
5614 for i in range(self.num_entries):
5616 'id' : decode_cstr(self.entries[i].id),
5617 'name' : decode_cstr(self.entries[i].name),
5618 'source' : TrashIterator.__source_string[self.entries[i].source],
5619 'deletion_time' : datetime.utcfromtimestamp(self.entries[i].deletion_time),
5620 'deferment_end_time' : datetime.utcfromtimestamp(self.entries[i].deferment_end_time)
5623 def __dealloc__(self):
5624 rbd_trash_list_cleanup(self.entries, self.num_entries)
5628 cdef class ChildIterator(object):
5630 Iterator over child info for the image or its snapshot.
5632 Yields a dictionary containing information about a child.
5636 * ``pool`` (str) - name of the pool
5638 * ``pool_namespace`` (str) - namespace of the pool
5640 * ``image`` (str) - name of the child
5642 * ``id`` (str) - id of the child
5644 * ``trash`` (bool) - True if child is in trash bin
5647 cdef rbd_linked_image_spec_t *children
5648 cdef size_t num_children
5651 def __init__(self, Image image, descendants=False):
5652 image.require_not_closed()
5655 self.children = NULL
5656 self.num_children = 10
5658 self.children = <rbd_linked_image_spec_t*>realloc_chk(
5659 self.children, self.num_children * sizeof(rbd_linked_image_spec_t))
5662 ret = rbd_list_descendants(image.image, self.children,
5666 ret = rbd_list_children3(image.image, self.children,
5670 elif ret != -errno.ERANGE:
5671 raise make_ex(ret, 'error listing children.')
5674 for i in range(self.num_children):
5676 'pool' : decode_cstr(self.children[i].pool_name),
5677 'pool_namespace' : decode_cstr(self.children[i].pool_namespace),
5678 'image' : decode_cstr(self.children[i].image_name),
5679 'id' : decode_cstr(self.children[i].image_id),
5680 'trash' : self.children[i].trash
5683 def __dealloc__(self):
5685 rbd_linked_image_spec_list_cleanup(self.children, self.num_children)
5688 cdef class WatcherIterator(object):
5690 Iterator over watchers of an image.
5692 Yields a dictionary containing information about a watcher.
5696 * ``addr`` (str) - address of the watcher
5698 * ``id`` (int) - id of the watcher
5700 * ``cookie`` (int) - the watcher's cookie
5703 cdef rbd_image_watcher_t *watchers
5704 cdef size_t num_watchers
5707 def __init__(self, Image image):
5708 image.require_not_closed()
5711 self.watchers = NULL
5712 self.num_watchers = 10
5714 self.watchers = <rbd_image_watcher_t*>realloc_chk(self.watchers,
5716 sizeof(rbd_image_watcher_t))
5718 ret = rbd_watchers_list(image.image, self.watchers, &self.num_watchers)
5721 elif ret != -errno.ERANGE:
5722 raise make_ex(ret, 'error listing watchers.')
5725 for i in range(self.num_watchers):
5727 'addr' : decode_cstr(self.watchers[i].addr),
5728 'id' : self.watchers[i].id,
5729 'cookie' : self.watchers[i].cookie
5732 def __dealloc__(self):
5734 rbd_watchers_list_cleanup(self.watchers, self.num_watchers)
5737 cdef class ConfigImageIterator(object):
5739 Iterator over image-level overrides for an image.
5741 Yields a dictionary containing information about an override.
5745 * ``name`` (str) - override name
5747 * ``value`` (str) - override value
5749 * ``source`` (str) - override source
5753 rbd_config_option_t *options
5756 def __init__(self, Image image):
5757 image.require_not_closed()
5760 self.num_options = 32
5762 self.options = <rbd_config_option_t *>realloc_chk(
5763 self.options, self.num_options * sizeof(rbd_config_option_t))
5765 ret = rbd_config_image_list(image.image, self.options,
5768 if ret == -errno.ERANGE:
5770 self.num_options = 0
5771 raise make_ex(ret, 'error listing config options')
5775 for i in range(self.num_options):
5777 'name' : decode_cstr(self.options[i].name),
5778 'value' : decode_cstr(self.options[i].value),
5779 'source' : self.options[i].source,
5782 def __dealloc__(self):
5784 rbd_config_image_list_cleanup(self.options, self.num_options)
5787 cdef class GroupImageIterator(object):
5789 Iterator over image info for a group.
5791 Yields a dictionary containing information about an image.
5795 * ``name`` (str) - name of the image
5797 * ``pool`` (int) - id of the pool this image belongs to
5799 * ``state`` (int) - state of the image
5802 cdef rbd_group_image_info_t *images
5803 cdef size_t num_images
5806 def __init__(self, Group group):
5809 self.num_images = 10
5811 self.images = <rbd_group_image_info_t*>realloc_chk(self.images,
5813 sizeof(rbd_group_image_info_t))
5815 ret = rbd_group_image_list(group._ioctx, group._name,
5817 sizeof(rbd_group_image_info_t),
5822 elif ret != -errno.ERANGE:
5823 raise make_ex(ret, 'error listing images for group %s' % group.name, group_errno_to_exception)
5826 for i in range(self.num_images):
5828 'name' : decode_cstr(self.images[i].name),
5829 'pool' : self.images[i].pool,
5830 'state' : self.images[i].state,
5833 def __dealloc__(self):
5835 rbd_group_image_list_cleanup(self.images,
5836 sizeof(rbd_group_image_info_t),
5840 cdef class GroupSnapIterator(object):
5842 Iterator over snaps specs for a group.
5844 Yields a dictionary containing information about a snapshot.
5848 * ``name`` (str) - name of the snapshot
5850 * ``state`` (int) - state of the snapshot
5853 cdef rbd_group_snap_info_t *snaps
5854 cdef size_t num_snaps
5857 def __init__(self, Group group):
5862 self.snaps = <rbd_group_snap_info_t*>realloc_chk(self.snaps,
5864 sizeof(rbd_group_snap_info_t))
5866 ret = rbd_group_snap_list(group._ioctx, group._name, self.snaps,
5867 sizeof(rbd_group_snap_info_t),
5872 elif ret != -errno.ERANGE:
5873 raise make_ex(ret, 'error listing snapshots for group %s' % group.name, group_errno_to_exception)
5876 for i in range(self.num_snaps):
5878 'name' : decode_cstr(self.snaps[i].name),
5879 'state' : self.snaps[i].state,
5882 def __dealloc__(self):
5884 rbd_group_snap_list_cleanup(self.snaps,
5885 sizeof(rbd_group_snap_info_t),