]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/rbd/rbd.pyx
5e14e38c919754c2b7d36e82816a3c5271862266
[ceph.git] / ceph / src / pybind / rbd / rbd.pyx
1 # cython: embedsignature=True
2 """
3 This module is a thin wrapper around librbd.
4
5 It currently provides all the synchronous methods of librbd that do
6 not use callbacks.
7
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
12 method.
13 """
14 # Copyright 2011 Josh Durgin
15 # Copyright 2015 Hector Martin <marcan@marcan.st>
16
17 import cython
18 import json
19 import sys
20
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
26
27 try:
28 from collections.abc import Iterable
29 except ImportError:
30 from collections import Iterable
31 from datetime import datetime
32 import errno
33 from itertools import chain
34 import time
35
36 IF BUILD_DOC:
37 include "mock_rbd.pxi"
38 ELSE:
39 from c_rbd cimport *
40 cimport rados
41
42
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
51
52 cdef extern from "<errno.h>" nogil:
53 enum:
54 _ECANCELED "ECANCELED"
55
56
57 ECANCELED = _ECANCELED
58
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
70
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
76
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
81
82 RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID
83 RBD_FLAG_FAST_DIFF_INVALID = _RBD_FLAG_FAST_DIFF_INVALID
84
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
88
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
92
93 RBD_MIRROR_IMAGE_MODE_JOURNAL = _RBD_MIRROR_IMAGE_MODE_JOURNAL
94 RBD_MIRROR_IMAGE_MODE_SNAPSHOT = _RBD_MIRROR_IMAGE_MODE_SNAPSHOT
95
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
99
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
107
108 RBD_LOCK_MODE_EXCLUSIVE = _RBD_LOCK_MODE_EXCLUSIVE
109 RBD_LOCK_MODE_SHARED = _RBD_LOCK_MODE_SHARED
110
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
117
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
122
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
127
128 RBD_GROUP_IMAGE_STATE_ATTACHED = _RBD_GROUP_IMAGE_STATE_ATTACHED
129 RBD_GROUP_IMAGE_STATE_INCOMPLETE = _RBD_GROUP_IMAGE_STATE_INCOMPLETE
130
131 RBD_GROUP_SNAP_STATE_INCOMPLETE = _RBD_GROUP_SNAP_STATE_INCOMPLETE
132 RBD_GROUP_SNAP_STATE_COMPLETE = _RBD_GROUP_SNAP_STATE_COMPLETE
133
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
141
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
145
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
154
155 RBD_SNAP_CREATE_SKIP_QUIESCE = _RBD_SNAP_CREATE_SKIP_QUIESCE
156 RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR = _RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR
157
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
161
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
167
168 RBD_WRITE_ZEROES_FLAG_THICK_PROVISION = _RBD_WRITE_ZEROES_FLAG_THICK_PROVISION
169
170 class Error(Exception):
171 pass
172
173
174 class OSError(Error):
175 """ `OSError` class, derived from `Error` """
176 def __init__(self, message, errno=None):
177 super(OSError, self).__init__(message)
178 self.errno = errno
179
180 def __str__(self):
181 msg = super(OSError, self).__str__()
182 if self.errno is None:
183 return msg
184 return '[errno {0}] {1}'.format(self.errno, msg)
185
186 def __reduce__(self):
187 return (self.__class__, (self.message, self.errno))
188
189 class PermissionError(OSError):
190 def __init__(self, message, errno=None):
191 super(PermissionError, self).__init__(
192 "RBD permission error (%s)" % message, errno)
193
194
195 class ImageNotFound(OSError):
196 def __init__(self, message, errno=None):
197 super(ImageNotFound, self).__init__(
198 "RBD image not found (%s)" % message, errno)
199
200
201 class ObjectNotFound(OSError):
202 def __init__(self, message, errno=None):
203 super(ObjectNotFound, self).__init__(
204 "RBD object not found (%s)" % message, errno)
205
206
207 class ImageExists(OSError):
208 def __init__(self, message, errno=None):
209 super(ImageExists, self).__init__(
210 "RBD image already exists (%s)" % message, errno)
211
212
213 class ObjectExists(OSError):
214 def __init__(self, message, errno=None):
215 super(ObjectExists, self).__init__(
216 "RBD object already exists (%s)" % message, errno)
217
218
219 class IOError(OSError):
220 def __init__(self, message, errno=None):
221 super(IOError, self).__init__(
222 "RBD I/O error (%s)" % message, errno)
223
224
225 class NoSpace(OSError):
226 def __init__(self, message, errno=None):
227 super(NoSpace, self).__init__(
228 "RBD insufficient space available (%s)" % message, errno)
229
230
231 class IncompleteWriteError(OSError):
232 def __init__(self, message, errno=None):
233 super(IncompleteWriteError, self).__init__(
234 "RBD incomplete write (%s)" % message, errno)
235
236
237 class InvalidArgument(OSError):
238 def __init__(self, message, errno=None):
239 super(InvalidArgument, self).__init__(
240 "RBD invalid argument (%s)" % message, errno)
241
242
243 class LogicError(OSError):
244 def __init__(self, message, errno=None):
245 super(LogicError, self).__init__(
246 "RBD logic error (%s)" % message, errno)
247
248
249 class ReadOnlyImage(OSError):
250 def __init__(self, message, errno=None):
251 super(ReadOnlyImage, self).__init__(
252 "RBD read-only image (%s)" % message, errno)
253
254
255 class ImageBusy(OSError):
256 def __init__(self, message, errno=None):
257 super(ImageBusy, self).__init__(
258 "RBD image is busy (%s)" % message, errno)
259
260
261 class ImageHasSnapshots(OSError):
262 def __init__(self, message, errno=None):
263 super(ImageHasSnapshots, self).__init__(
264 "RBD image has snapshots (%s)" % message, errno)
265
266
267 class FunctionNotSupported(OSError):
268 def __init__(self, message, errno=None):
269 super(FunctionNotSupported, self).__init__(
270 "RBD function not supported (%s)" % message, errno)
271
272
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)
277
278
279 class ConnectionShutdown(OSError):
280 def __init__(self, message, errno=None):
281 super(ConnectionShutdown, self).__init__(
282 "RBD connection was shutdown (%s)" % message, errno)
283
284
285 class Timeout(OSError):
286 def __init__(self, message, errno=None):
287 super(Timeout, self).__init__(
288 "RBD operation timeout (%s)" % message, errno)
289
290
291 class DiskQuotaExceeded(OSError):
292 def __init__(self, message, errno=None):
293 super(DiskQuotaExceeded, self).__init__(
294 "RBD disk quota exceeded (%s)" % message, errno)
295
296 class OperationNotSupported(OSError):
297 def __init__(self, message, errno=None):
298 super(OperationNotSupported, self).__init__(
299 "RBD operation not supported (%s)" % message, errno)
300
301 class OperationCanceled(OSError):
302 def __init__(self, message, errno=None):
303 super(OperationCanceled, self).__init__(
304 "RBD operation canceled (%s)" % message, errno)
305
306 cdef errno_to_exception = {
307 errno.EPERM : PermissionError,
308 errno.ENOENT : ImageNotFound,
309 errno.EIO : IOError,
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,
323 }
324
325 cdef group_errno_to_exception = {
326 errno.EPERM : PermissionError,
327 errno.ENOENT : ObjectNotFound,
328 errno.EIO : IOError,
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,
342 }
343
344 cdef make_ex(ret, msg, exception_map=errno_to_exception):
345 """
346 Translate a librbd return code into an exception.
347
348 :param ret: the return code
349 :type ret: int
350 :param msg: the error message to use
351 :type msg: str
352 :returns: a subclass of :class:`Error`
353 """
354 ret = abs(ret)
355 if ret in exception_map:
356 return exception_map[ret](msg, errno=ret)
357 else:
358 return OSError(msg, errno=ret)
359
360
361 IF BUILD_DOC:
362 cdef rados_t convert_rados(rados) nogil:
363 return <rados_t>0
364
365 cdef rados_ioctx_t convert_ioctx(ioctx) nogil:
366 return <rados_ioctx_t>0
367 ELSE:
368 cdef rados_t convert_rados(rados.Rados rados) except? NULL:
369 return <rados_t>rados.cluster
370
371 cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL:
372 return <rados_ioctx_t>ioctx.io
373
374 cdef int progress_callback(uint64_t offset, uint64_t total, void* ptr) with gil:
375 return (<object>ptr)(offset, total)
376
377 cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr):
378 return 0
379
380 def cstr(val, name, encoding="utf-8", opt=False):
381 """
382 Create a byte string from a Python string
383
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
388 :rtype: bytes
389 :raises: :class:`InvalidArgument`
390 """
391 if opt and val is None:
392 return None
393 if isinstance(val, bytes):
394 return val
395 elif isinstance(val, str):
396 return val.encode(encoding)
397 else:
398 raise InvalidArgument('%s must be a string' % name)
399
400 def decode_cstr(val, encoding="utf-8"):
401 """
402 Decode a byte string into a Python string.
403
404 :param bytes val: byte string
405 :rtype: str or None
406 """
407 if val is None:
408 return None
409
410 return val.decode(encoding)
411
412
413 cdef char* opt_str(s) except? NULL:
414 if s is None:
415 return NULL
416 return s
417
418 cdef void* realloc_chk(void* ptr, size_t size) except NULL:
419 cdef void *ret = realloc(ptr, size)
420 if ret == NULL:
421 raise MemoryError("realloc failed")
422 return ret
423
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)
426
427 cdef class Completion
428
429 cdef void __aio_complete_cb(rbd_completion_t completion, void *args) with gil:
430 """
431 Callback to oncomplete() for asynchronous operations
432 """
433 cdef Completion cb = <Completion>args
434 cb._complete()
435
436
437 cdef class Completion(object):
438 """completion object"""
439
440 cdef:
441 object image
442 object oncomplete
443 rbd_completion_t rbd_comp
444 PyObject* buf
445 bint persisted
446 object exc_info
447
448 def __cinit__(self, image, object oncomplete):
449 self.oncomplete = oncomplete
450 self.image = image
451 self.persisted = False
452
453 def is_complete(self):
454 """
455 Has an asynchronous operation completed?
456
457 This does not imply that the callback has finished.
458
459 :returns: True if the operation is completed
460 """
461 with nogil:
462 ret = rbd_aio_is_complete(self.rbd_comp)
463 return ret == 1
464
465 def wait_for_complete_and_cb(self):
466 """
467 Wait for an asynchronous operation to complete
468
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.
474 """
475 with nogil:
476 rbd_aio_wait_for_complete(self.rbd_comp)
477
478 if self.exc_info:
479 raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
480
481 def get_return_value(self):
482 """
483 Get the return value of an asychronous operation
484
485 The return value is set when the operation is complete.
486
487 :returns: int - return value of the operation
488 """
489 with nogil:
490 ret = rbd_aio_get_return_value(self.rbd_comp)
491 return ret
492
493 def __dealloc__(self):
494 """
495 Release a completion
496
497 This is automatically called when the completion object is freed.
498 """
499 ref.Py_XDECREF(self.buf)
500 self.buf = NULL
501 if self.rbd_comp != NULL:
502 with nogil:
503 rbd_aio_release(self.rbd_comp)
504 self.rbd_comp = NULL
505
506 cdef void _complete(self):
507 try:
508 self.__unpersist()
509 if self.oncomplete:
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.
517 except:
518 self.exc_info = sys.exc_info()
519
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.
525 ref.Py_INCREF(self)
526 self.persisted = True
527
528 cdef __unpersist(self):
529 if self.persisted:
530 ref.Py_DECREF(self)
531 self.persisted = False
532
533
534 class RBD(object):
535 """
536 This class wraps librbd CRUD functions.
537 """
538 def version(self):
539 """
540 Get the version number of the ``librbd`` C library.
541
542 :returns: a tuple of ``(major, minor, extra)`` components of the
543 librbd version
544 """
545 cdef int major = 0
546 cdef int minor = 0
547 cdef int extra = 0
548 rbd_version(&major, &minor, &extra)
549 return (major, minor, extra)
550
551 def create(self, ioctx, name, size, order=None, old_format=False,
552 features=None, stripe_unit=None, stripe_count=None,
553 data_pool=None):
554 """
555 Create an rbd image.
556
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
560 :type name: str
561 :param size: how big the image is in bytes
562 :type size: int
563 :param order: the image is split into (2**order) byte objects
564 :type order: int
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
570 :type features: int
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
576 :type data_pool: str
577 :raises: :class:`ImageExists`
578 :raises: :class:`TypeError`
579 :raises: :class:`InvalidArgument`
580 :raises: :class:`FunctionNotSupported`
581 """
582 name = cstr(name, 'name')
583 data_pool = cstr(data_pool, 'data_pool', opt=True)
584 cdef:
585 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
586 char *_name = name
587 uint64_t _size = size
588 int _order = 0
589 rbd_image_options_t opts
590 if order is not None:
591 _order = order
592 if old_format:
593 if (features or
594 ((stripe_unit is not None) and stripe_unit != 0) or
595 ((stripe_count is not None) and stripe_count != 0) or
596 data_pool):
597 raise InvalidArgument('format 1 images do not support feature '
598 'masks, non-default striping, nor data '
599 'pool')
600 with nogil:
601 ret = rbd_create(_ioctx, _name, _size, &_order)
602 else:
603 rbd_image_options_create(&opts)
604 try:
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,
610 features)
611 if order is not None:
612 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
613 _order)
614 if stripe_unit is not None:
615 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
616 stripe_unit)
617 if stripe_count is not None:
618 rbd_image_options_set_uint64(opts,
619 RBD_IMAGE_OPTION_STRIPE_COUNT,
620 stripe_count)
621 if data_pool is not None:
622 rbd_image_options_set_string(opts,
623 RBD_IMAGE_OPTION_DATA_POOL,
624 data_pool)
625 with nogil:
626 ret = rbd_create4(_ioctx, _name, _size, opts)
627 finally:
628 rbd_image_options_destroy(opts)
629 if ret < 0:
630 raise make_ex(ret, 'error creating image')
631
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,
634 data_pool=None):
635 """
636 Clone a parent rbd snapshot into a COW sparse child.
637
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
641 :type name: str
642 :param p_snapname: the parent image snapshot name
643 :type name: str
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
647 :type name: str
648 :param features: bitmask of features to enable; if set, must include layering
649 :type features: int
650 :param order: the image is split into (2**order) byte objects
651 :type order: int
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
657 :type data_pool: str
658 :raises: :class:`TypeError`
659 :raises: :class:`InvalidArgument`
660 :raises: :class:`ImageExists`
661 :raises: :class:`FunctionNotSupported`
662 :raises: :class:`ArgumentOutOfRange`
663 """
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)
668 cdef:
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
675
676 rbd_image_options_create(&opts)
677 try:
678 if features is not None:
679 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
680 features)
681 if order is not None:
682 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
683 order)
684 if stripe_unit is not None:
685 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
686 stripe_unit)
687 if stripe_count is not None:
688 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
689 stripe_count)
690 if data_pool is not None:
691 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
692 data_pool)
693 with nogil:
694 ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname,
695 _c_ioctx, _c_name, opts)
696 finally:
697 rbd_image_options_destroy(opts)
698 if ret < 0:
699 raise make_ex(ret, 'error creating clone')
700
701 def list(self, ioctx):
702 """
703 List image names.
704
705 :param ioctx: determines which RADOS pool is read
706 :type ioctx: :class:`rados.Ioctx`
707 :returns: list -- a list of image names
708 """
709 cdef:
710 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
711 size_t size = 512
712 char *c_names = NULL
713 try:
714 while True:
715 c_names = <char *>realloc_chk(c_names, size)
716 with nogil:
717 ret = rbd_list(_ioctx, c_names, &size)
718 if ret >= 0:
719 break
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')
723 if name]
724 finally:
725 free(c_names)
726
727 def list2(self, ioctx):
728 """
729 Iterate over the images in the pool.
730
731 :param ioctx: determines which RADOS pool the image is in
732 :type ioctx: :class:`rados.Ioctx`
733 :returns: :class:`ImageIterator`
734 """
735 return ImageIterator(ioctx)
736
737 def remove(self, ioctx, name, on_progress=None):
738 """
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.
746
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
750 :type name: str
751 :param on_progress: optional progress callback function
752 :type on_progress: callback function
753 :raises: :class:`ImageNotFound`, :class:`ImageBusy`,
754 :class:`ImageHasSnapshots`
755 """
756 name = cstr(name, 'name')
757 cdef:
758 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
759 char *_name = name
760 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
761 void *_prog_arg = NULL
762 if on_progress:
763 _prog_cb = &progress_callback
764 _prog_arg = <void *>on_progress
765 with nogil:
766 ret = rbd_remove_with_progress(_ioctx, _name, _prog_cb, _prog_arg)
767 if ret != 0:
768 raise make_ex(ret, 'error removing image')
769
770 def rename(self, ioctx, src, dest):
771 """
772 Rename an RBD image.
773
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
777 :type src: str
778 :param dest: the new name of the image
779 :type dest: str
780 :raises: :class:`ImageNotFound`, :class:`ImageExists`
781 """
782 src = cstr(src, 'src')
783 dest = cstr(dest, 'dest')
784 cdef:
785 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
786 char *_src = src
787 char *_dest = dest
788 with nogil:
789 ret = rbd_rename(_ioctx, _src, _dest)
790 if ret != 0:
791 raise make_ex(ret, 'error renaming image')
792
793 def trash_move(self, ioctx, name, delay=0):
794 """
795 Move an RBD image to the trash.
796
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
800 :type name: str
801 :param delay: time delay in seconds before the image can be deleted
802 from trash
803 :type delay: int
804 :raises: :class:`ImageNotFound`
805 """
806 name = cstr(name, 'name')
807 cdef:
808 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
809 char *_name = name
810 uint64_t _delay = delay
811 with nogil:
812 ret = rbd_trash_move(_ioctx, _name, _delay)
813 if ret != 0:
814 raise make_ex(ret, 'error moving image to trash')
815
816 def trash_purge(self, ioctx, expire_ts=None, threshold=-1):
817 """
818 Delete RBD images from trash in bulk.
819
820 By default it removes images with deferment end time less than now.
821
822 The timestamp is configurable, e.g. delete images that have expired a
823 week ago.
824
825 If the threshold is used it deletes images until X% pool usage is met.
826
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
833 """
834 if expire_ts:
835 expire_epoch_ts = time.mktime(expire_ts.timetuple())
836 else:
837 expire_epoch_ts = 0
838
839 cdef:
840 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
841 time_t _expire_ts = expire_epoch_ts
842 float _threshold = threshold
843 with nogil:
844 ret = rbd_trash_purge(_ioctx, _expire_ts, _threshold)
845 if ret != 0:
846 raise make_ex(ret, 'error purging images from trash')
847
848 def trash_remove(self, ioctx, image_id, force=False, on_progress=None):
849 """
850 Delete an RBD image from trash. If image deferment time has not
851 expired :class:`PermissionError` is raised.
852
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
856 :type image_id: str
857 :param force: force remove even if deferment time has not expired
858 :type force: bool
859 :param on_progress: optional progress callback function
860 :type on_progress: callback function
861 :raises: :class:`ImageNotFound`, :class:`PermissionError`
862 """
863 image_id = cstr(image_id, 'image_id')
864 cdef:
865 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
866 char *_image_id = image_id
867 int _force = force
868 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
869 void *_prog_arg = NULL
870 if on_progress:
871 _prog_cb = &progress_callback
872 _prog_arg = <void *>on_progress
873 with nogil:
874 ret = rbd_trash_remove_with_progress(_ioctx, _image_id, _force,
875 _prog_cb, _prog_arg)
876 if ret != 0:
877 raise make_ex(ret, 'error deleting image from trash')
878
879 def trash_get(self, ioctx, image_id):
880 """
881 Retrieve RBD image info from trash.
882
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
886 :type image_id: str
887 :returns: dict - contains the following keys:
888
889 * ``id`` (str) - image id
890
891 * ``name`` (str) - image name
892
893 * ``source`` (str) - source of deletion
894
895 * ``deletion_time`` (datetime) - time of deletion
896
897 * ``deferment_end_time`` (datetime) - time that an image is allowed
898 to be removed from trash
899
900 :raises: :class:`ImageNotFound`
901 """
902 image_id = cstr(image_id, 'image_id')
903 cdef:
904 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
905 char *_image_id = image_id
906 rbd_trash_image_info_t c_info
907 with nogil:
908 ret = rbd_trash_get(_ioctx, _image_id, &c_info)
909 if ret != 0:
910 raise make_ex(ret, 'error retrieving image from trash')
911
912 __source_string = ['USER', 'MIRRORING', 'MIGRATION', 'REMOVING']
913 info = {
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)
919 }
920 rbd_trash_get_cleanup(&c_info)
921 return info
922
923 def trash_list(self, ioctx):
924 """
925 List all entries from trash.
926
927 :param ioctx: determines which RADOS pool the image is in
928 :type ioctx: :class:`rados.Ioctx`
929 :returns: :class:`TrashIterator`
930 """
931 return TrashIterator(ioctx)
932
933 def trash_restore(self, ioctx, image_id, name):
934 """
935 Restore an RBD image from trash.
936
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
940 :type image_id: str
941 :param name: the new name of the restored image
942 :type name: str
943 :raises: :class:`ImageNotFound`
944 """
945 image_id = cstr(image_id, 'image_id')
946 name = cstr(name, 'name')
947 cdef:
948 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
949 char *_image_id = image_id
950 char *_name = name
951 with nogil:
952 ret = rbd_trash_restore(_ioctx, _image_id, _name)
953 if ret != 0:
954 raise make_ex(ret, 'error restoring image from trash')
955
956 def migration_prepare(self, ioctx, image_name, dest_ioctx, dest_image_name,
957 features=None, order=None, stripe_unit=None, stripe_count=None,
958 data_pool=None):
959 """
960 Prepare an RBD image migration.
961
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
965 :type src: str
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
971 :type features: int
972 :param order: the image is split into (2**order) byte objects
973 :type order: int
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
979 :type data_pool: str
980 :raises: :class:`TypeError`
981 :raises: :class:`InvalidArgument`
982 :raises: :class:`ImageExists`
983 :raises: :class:`FunctionNotSupported`
984 :raises: :class:`ArgumentOutOfRange`
985 """
986 image_name = cstr(image_name, 'image_name')
987 dest_image_name = cstr(dest_image_name, 'dest_image_name')
988 cdef:
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
994
995 rbd_image_options_create(&opts)
996 try:
997 if features is not None:
998 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
999 features)
1000 if order is not None:
1001 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
1002 order)
1003 if stripe_unit is not None:
1004 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
1005 stripe_unit)
1006 if stripe_count is not None:
1007 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
1008 stripe_count)
1009 if data_pool is not None:
1010 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
1011 data_pool)
1012 with nogil:
1013 ret = rbd_migration_prepare(_ioctx, _image_name, _dest_ioctx,
1014 _dest_image_name, opts)
1015 finally:
1016 rbd_image_options_destroy(opts)
1017 if ret < 0:
1018 raise make_ex(ret, 'error migrating image %s' % (image_name))
1019
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):
1023 """
1024 Prepare an RBD image migration.
1025
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
1033 :type features: int
1034 :param order: the image is split into (2**order) byte objects
1035 :type order: int
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`
1047 """
1048 source_spec = cstr(source_spec, 'source_spec')
1049 dest_image_name = cstr(dest_image_name, 'dest_image_name')
1050 cdef:
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
1055
1056 rbd_image_options_create(&opts)
1057 try:
1058 if features is not None:
1059 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
1060 features)
1061 if order is not None:
1062 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
1063 order)
1064 if stripe_unit is not None:
1065 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
1066 stripe_unit)
1067 if stripe_count is not None:
1068 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
1069 stripe_count)
1070 if data_pool is not None:
1071 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
1072 data_pool)
1073 with nogil:
1074 ret = rbd_migration_prepare_import(_source_spec, _dest_ioctx,
1075 _dest_image_name, opts)
1076 finally:
1077 rbd_image_options_destroy(opts)
1078 if ret < 0:
1079 raise make_ex(ret, 'error migrating image %s' % (source_spec))
1080
1081 def migration_execute(self, ioctx, image_name, on_progress=None):
1082 """
1083 Execute a prepared RBD image migration.
1084
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`
1092 """
1093 image_name = cstr(image_name, 'image_name')
1094 cdef:
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
1099 if on_progress:
1100 _prog_cb = &progress_callback
1101 _prog_arg = <void *>on_progress
1102 with nogil:
1103 ret = rbd_migration_execute_with_progress(_ioctx, _image_name,
1104 _prog_cb, _prog_arg)
1105 if ret != 0:
1106 raise make_ex(ret, 'error aborting migration')
1107
1108 def migration_commit(self, ioctx, image_name, on_progress=None):
1109 """
1110 Commit an executed RBD image migration.
1111
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`
1119 """
1120 image_name = cstr(image_name, 'image_name')
1121 cdef:
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
1126 if on_progress:
1127 _prog_cb = &progress_callback
1128 _prog_arg = <void *>on_progress
1129 with nogil:
1130 ret = rbd_migration_commit_with_progress(_ioctx, _image_name,
1131 _prog_cb, _prog_arg)
1132 if ret != 0:
1133 raise make_ex(ret, 'error aborting migration')
1134
1135 def migration_abort(self, ioctx, image_name, on_progress=None):
1136 """
1137 Cancel a previously started but interrupted migration.
1138
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`
1146 """
1147 image_name = cstr(image_name, 'image_name')
1148 cdef:
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
1153 if on_progress:
1154 _prog_cb = &progress_callback
1155 _prog_arg = <void *>on_progress
1156 with nogil:
1157 ret = rbd_migration_abort_with_progress(_ioctx, _image_name,
1158 _prog_cb, _prog_arg)
1159 if ret != 0:
1160 raise make_ex(ret, 'error aborting migration')
1161
1162 def migration_status(self, ioctx, image_name):
1163 """
1164 Return RBD image migration status.
1165
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:
1171
1172 * ``source_pool_id`` (int) - source image pool id
1173
1174 * ``source_pool_namespace`` (str) - source image pool namespace
1175
1176 * ``source_image_name`` (str) - source image name
1177
1178 * ``source_image_id`` (str) - source image id
1179
1180 * ``dest_pool_id`` (int) - destination image pool id
1181
1182 * ``dest_pool_namespace`` (str) - destination image pool namespace
1183
1184 * ``dest_image_name`` (str) - destination image name
1185
1186 * ``dest_image_id`` (str) - destination image id
1187
1188 * ``state`` (int) - current migration state
1189
1190 * ``state_description`` (str) - migration state description
1191
1192 :raises: :class:`ImageNotFound`
1193 """
1194 image_name = cstr(image_name, 'image_name')
1195 cdef:
1196 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1197 char *_image_name = image_name
1198 rbd_image_migration_status_t c_status
1199 with nogil:
1200 ret = rbd_migration_status(_ioctx, _image_name, &c_status,
1201 sizeof(c_status))
1202 if ret != 0:
1203 raise make_ex(ret, 'error getting migration status')
1204
1205 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)
1216 }
1217
1218 rbd_migration_status_cleanup(&c_status)
1219
1220 return status
1221
1222 def mirror_site_name_get(self, rados):
1223 """
1224 Get the local cluster's friendly site name
1225
1226 :param rados: cluster connection
1227 :type rados: :class: rados.Rados
1228 :returns: str - local site name
1229 """
1230 cdef:
1231 rados_t _rados = convert_rados(rados)
1232 char *_site_name = NULL
1233 size_t _max_size = 512
1234 try:
1235 while True:
1236 _site_name = <char *>realloc_chk(_site_name, _max_size)
1237 with nogil:
1238 ret = rbd_mirror_site_name_get(_rados, _site_name,
1239 &_max_size)
1240 if ret >= 0:
1241 break
1242 elif ret != -errno.ERANGE:
1243 raise make_ex(ret, 'error getting site name')
1244 return decode_cstr(_site_name)
1245 finally:
1246 free(_site_name)
1247
1248 def mirror_site_name_set(self, rados, site_name):
1249 """
1250 Set the local cluster's friendly site name
1251
1252 :param rados: cluster connection
1253 :type rados: :class: rados.Rados
1254 :param site_name: friendly site name
1255 :type str:
1256 """
1257 site_name = cstr(site_name, 'site_name')
1258 cdef:
1259 rados_t _rados = convert_rados(rados)
1260 char *_site_name = site_name
1261 with nogil:
1262 ret = rbd_mirror_site_name_set(_rados, _site_name)
1263 if ret != 0:
1264 raise make_ex(ret, 'error setting mirror site name')
1265
1266 def mirror_mode_get(self, ioctx):
1267 """
1268 Get pool mirror mode.
1269
1270 :param ioctx: determines which RADOS pool is read
1271 :type ioctx: :class:`rados.Ioctx`
1272 :returns: int - pool mirror mode
1273 """
1274 cdef:
1275 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1276 rbd_mirror_mode_t mirror_mode
1277 with nogil:
1278 ret = rbd_mirror_mode_get(_ioctx, &mirror_mode)
1279 if ret != 0:
1280 raise make_ex(ret, 'error getting mirror mode')
1281 return mirror_mode
1282
1283 def mirror_mode_set(self, ioctx, mirror_mode):
1284 """
1285 Set pool mirror mode.
1286
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
1291 """
1292 cdef:
1293 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1294 rbd_mirror_mode_t _mirror_mode = mirror_mode
1295 with nogil:
1296 ret = rbd_mirror_mode_set(_ioctx, _mirror_mode)
1297 if ret != 0:
1298 raise make_ex(ret, 'error setting mirror mode')
1299
1300 def mirror_uuid_get(self, ioctx):
1301 """
1302 Get pool mirror uuid
1303
1304 :param ioctx: determines which RADOS pool is read
1305 :type ioctx: :class:`rados.Ioctx`
1306 :returns: ste - pool mirror uuid
1307 """
1308 cdef:
1309 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1310 char *_uuid = NULL
1311 size_t _max_size = 512
1312 try:
1313 while True:
1314 _uuid = <char *>realloc_chk(_uuid, _max_size)
1315 with nogil:
1316 ret = rbd_mirror_uuid_get(_ioctx, _uuid, &_max_size)
1317 if ret >= 0:
1318 break
1319 elif ret != -errno.ERANGE:
1320 raise make_ex(ret, 'error retrieving mirror uuid')
1321 return decode_cstr(_uuid)
1322 finally:
1323 free(_uuid)
1324
1325 def mirror_peer_bootstrap_create(self, ioctx):
1326 """
1327 Creates a new RBD mirroring bootstrap token for an
1328 external cluster.
1329
1330 :param ioctx: determines which RADOS pool is written
1331 :type ioctx: :class:`rados.Ioctx`
1332 :returns: str - bootstrap token
1333 """
1334 cdef:
1335 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1336 char *_token = NULL
1337 size_t _max_size = 512
1338 try:
1339 while True:
1340 _token = <char *>realloc_chk(_token, _max_size)
1341 with nogil:
1342 ret = rbd_mirror_peer_bootstrap_create(_ioctx, _token,
1343 &_max_size)
1344 if ret >= 0:
1345 break
1346 elif ret != -errno.ERANGE:
1347 raise make_ex(ret, 'error creating bootstrap token')
1348 return decode_cstr(_token)
1349 finally:
1350 free(_token)
1351
1352 def mirror_peer_bootstrap_import(self, ioctx, direction, token):
1353 """
1354 Import a bootstrap token from an external cluster to
1355 auto-configure the mirror peer.
1356
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
1362 :type token: str
1363 """
1364 token = cstr(token, 'token')
1365 cdef:
1366 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1367 rbd_mirror_peer_direction_t _direction = direction
1368 char *_token = token
1369 with nogil:
1370 ret = rbd_mirror_peer_bootstrap_import(_ioctx, _direction, _token)
1371 if ret != 0:
1372 raise make_ex(ret, 'error importing bootstrap token')
1373
1374 def mirror_peer_add(self, ioctx, site_name, client_name,
1375 direction=RBD_MIRROR_PEER_DIRECTION_RX_TX):
1376 """
1377 Add mirror peer.
1378
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
1388 """
1389 site_name = cstr(site_name, 'site_name')
1390 client_name = cstr(client_name, 'client_name')
1391 cdef:
1392 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1393 char *_uuid = NULL
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
1398 try:
1399 _uuid = <char *>realloc_chk(_uuid, _uuid_max_length)
1400 with nogil:
1401 ret = rbd_mirror_peer_site_add(_ioctx, _uuid, _uuid_max_length,
1402 _direction, _site_name,
1403 _client_name)
1404 if ret != 0:
1405 raise make_ex(ret, 'error adding mirror peer')
1406 return decode_cstr(_uuid)
1407 finally:
1408 free(_uuid)
1409
1410 def mirror_peer_remove(self, ioctx, uuid):
1411 """
1412 Remove mirror peer.
1413
1414 :param ioctx: determines which RADOS pool is used
1415 :type ioctx: :class:`rados.Ioctx`
1416 :param uuid: peer uuid
1417 :type uuid: str
1418 """
1419 uuid = cstr(uuid, 'uuid')
1420 cdef:
1421 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1422 char *_uuid = uuid
1423 with nogil:
1424 ret = rbd_mirror_peer_site_remove(_ioctx, _uuid)
1425 if ret != 0:
1426 raise make_ex(ret, 'error removing mirror peer')
1427
1428 def mirror_peer_list(self, ioctx):
1429 """
1430 Iterate over the peers of a pool.
1431
1432 :param ioctx: determines which RADOS pool is read
1433 :type ioctx: :class:`rados.Ioctx`
1434 :returns: :class:`MirrorPeerIterator`
1435 """
1436 return MirrorPeerIterator(ioctx)
1437
1438 def mirror_peer_set_client(self, ioctx, uuid, client_name):
1439 """
1440 Set mirror peer client name
1441
1442 :param ioctx: determines which RADOS pool is written
1443 :type ioctx: :class:`rados.Ioctx`
1444 :param uuid: uuid of the mirror peer
1445 :type uuid: str
1446 :param client_name: client name of the mirror peer to set
1447 :type client_name: str
1448 """
1449 uuid = cstr(uuid, 'uuid')
1450 client_name = cstr(client_name, 'client_name')
1451 cdef:
1452 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1453 char *_uuid = uuid
1454 char *_client_name = client_name
1455 with nogil:
1456 ret = rbd_mirror_peer_site_set_client_name(_ioctx, _uuid,
1457 _client_name)
1458 if ret != 0:
1459 raise make_ex(ret, 'error setting mirror peer client name')
1460
1461 def mirror_peer_set_name(self, ioctx, uuid, site_name):
1462 """
1463 Set mirror peer site name
1464
1465 :param ioctx: determines which RADOS pool is written
1466 :type ioctx: :class:`rados.Ioctx`
1467 :param uuid: uuid of the mirror peer
1468 :type uuid: str
1469 :param site_name: site name of the mirror peer to set
1470 :type site_name: str
1471 """
1472 uuid = cstr(uuid, 'uuid')
1473 site_name = cstr(site_name, 'site_name')
1474 cdef:
1475 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1476 char *_uuid = uuid
1477 char *_site_name = site_name
1478 with nogil:
1479 ret = rbd_mirror_peer_site_set_name(_ioctx, _uuid, _site_name)
1480 if ret != 0:
1481 raise make_ex(ret, 'error setting mirror peer site name')
1482
1483 def mirror_peer_set_cluster(self, ioctx, uuid, cluster_name):
1484 self.mirror_peer_set_name(ioctx, uuid, cluster_name)
1485
1486 def mirror_peer_get_attributes(self, ioctx, uuid):
1487 """
1488 Get optional mirror peer attributes
1489
1490 :param ioctx: determines which RADOS pool is written
1491 :type ioctx: :class:`rados.Ioctx`
1492 :param uuid: uuid of the mirror peer
1493 :type uuid: str
1494
1495 :returns: dict - contains the following keys:
1496
1497 * ``mon_host`` (str) - monitor addresses
1498
1499 * ``key`` (str) - CephX key
1500 """
1501 uuid = cstr(uuid, 'uuid')
1502 cdef:
1503 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1504 char *_uuid = uuid
1505 char *_keys = NULL
1506 char *_vals = NULL
1507 size_t _keys_size = 512
1508 size_t _vals_size = 512
1509 size_t _count = 0
1510 try:
1511 while True:
1512 _keys = <char *>realloc_chk(_keys, _keys_size)
1513 _vals = <char *>realloc_chk(_vals, _vals_size)
1514 with nogil:
1515 ret = rbd_mirror_peer_site_get_attributes(
1516 _ioctx, _uuid, _keys, &_keys_size, _vals, &_vals_size,
1517 &_count)
1518 if ret >= 0:
1519 break
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))
1525 finally:
1526 free(_keys)
1527 free(_vals)
1528
1529 def mirror_peer_set_attributes(self, ioctx, uuid, attributes):
1530 """
1531 Set optional mirror peer attributes
1532
1533 :param ioctx: determines which RADOS pool is written
1534 :type ioctx: :class:`rados.Ioctx`
1535 :param uuid: uuid of the mirror peer
1536 :type uuid: str
1537 :param attributes: 'mon_host' and 'key' attributes
1538 :type attributes: dict
1539 """
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()])
1543 cdef:
1544 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1545 char *_uuid = uuid
1546 char *_keys = keys_str
1547 char *_vals = vals_str
1548 size_t _count = len(attributes)
1549
1550 with nogil:
1551 ret = rbd_mirror_peer_site_set_attributes(_ioctx, _uuid, _keys,
1552 _vals, _count)
1553 if ret != 0:
1554 raise make_ex(ret, 'error setting mirror peer attributes')
1555
1556 def mirror_image_status_list(self, ioctx):
1557 """
1558 Iterate over the mirror image statuses of a pool.
1559
1560 :param ioctx: determines which RADOS pool is read
1561 :type ioctx: :class:`rados.Ioctx`
1562 :returns: :class:`MirrorImageStatusIterator`
1563 """
1564 return MirrorImageStatusIterator(ioctx)
1565
1566 def mirror_image_status_summary(self, ioctx):
1567 """
1568 Get mirror image status summary of a pool.
1569
1570 :param ioctx: determines which RADOS pool is read
1571 :type ioctx: :class:`rados.Ioctx`
1572 :returns: list - a list of (state, count) tuples
1573 """
1574 cdef:
1575 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1576 rbd_mirror_image_status_state_t *states = NULL
1577 int *counts = NULL
1578 size_t maxlen = 32
1579 try:
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)
1583 with nogil:
1584 ret = rbd_mirror_image_status_summary(_ioctx, states, counts,
1585 &maxlen)
1586 if ret < 0:
1587 raise make_ex(ret, 'error getting mirror image status summary')
1588 return [(states[i], counts[i]) for i in range(maxlen)]
1589 finally:
1590 free(states)
1591 free(counts)
1592
1593 def mirror_image_instance_id_list(self, ioctx):
1594 """
1595 Iterate over the mirror image instance ids of a pool.
1596
1597 :param ioctx: determines which RADOS pool is read
1598 :type ioctx: :class:`rados.Ioctx`
1599 :returns: :class:`MirrorImageInstanceIdIterator`
1600 """
1601 return MirrorImageInstanceIdIterator(ioctx)
1602
1603 def mirror_image_info_list(self, ioctx, mode_filter=None):
1604 """
1605 Iterate over the mirror image instance ids of a pool.
1606
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`
1611 """
1612 return MirrorImageInfoIterator(ioctx, mode_filter)
1613
1614 def pool_metadata_get(self, ioctx, key):
1615 """
1616 Get pool metadata for the given key.
1617
1618 :param ioctx: determines which RADOS pool is read
1619 :type ioctx: :class:`rados.Ioctx`
1620 :param key: metadata key
1621 :type key: str
1622 :returns: str - metadata value
1623 """
1624 key = cstr(key, 'key')
1625 cdef:
1626 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1627 char *_key = key
1628 size_t size = 4096
1629 char *value = NULL
1630 int ret
1631 try:
1632 while True:
1633 value = <char *>realloc_chk(value, size)
1634 with nogil:
1635 ret = rbd_pool_metadata_get(_ioctx, _key, value, &size)
1636 if ret != -errno.ERANGE:
1637 break
1638 if ret == -errno.ENOENT:
1639 raise KeyError('no metadata %s' % (key))
1640 if ret != 0:
1641 raise make_ex(ret, 'error getting metadata %s' % (key))
1642 return decode_cstr(value)
1643 finally:
1644 free(value)
1645
1646 def pool_metadata_set(self, ioctx, key, value):
1647 """
1648 Set pool metadata for the given key.
1649
1650 :param ioctx: determines which RADOS pool is read
1651 :type ioctx: :class:`rados.Ioctx`
1652 :param key: metadata key
1653 :type key: str
1654 :param value: metadata value
1655 :type value: str
1656 """
1657 key = cstr(key, 'key')
1658 value = cstr(value, 'value')
1659 cdef:
1660 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1661 char *_key = key
1662 char *_value = value
1663 with nogil:
1664 ret = rbd_pool_metadata_set(_ioctx, _key, _value)
1665
1666 if ret != 0:
1667 raise make_ex(ret, 'error setting metadata %s' % (key))
1668
1669 def pool_metadata_remove(self, ioctx, key):
1670 """
1671 Remove pool metadata for the given key.
1672
1673 :param ioctx: determines which RADOS pool is read
1674 :type ioctx: :class:`rados.Ioctx`
1675 :param key: metadata key
1676 :type key: str
1677 :returns: str - metadata value
1678 """
1679 key = cstr(key, 'key')
1680 cdef:
1681 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1682 char *_key = key
1683 with nogil:
1684 ret = rbd_pool_metadata_remove(_ioctx, _key)
1685
1686 if ret == -errno.ENOENT:
1687 raise KeyError('no metadata %s' % (key))
1688 if ret != 0:
1689 raise make_ex(ret, 'error removing metadata %s' % (key))
1690
1691 def pool_metadata_list(self, ioctx):
1692 """
1693 List pool metadata.
1694
1695 :returns: :class:`PoolMetadataIterator`
1696 """
1697 return PoolMetadataIterator(ioctx)
1698
1699 def config_list(self, ioctx):
1700 """
1701 List pool-level config overrides.
1702
1703 :returns: :class:`ConfigPoolIterator`
1704 """
1705 return ConfigPoolIterator(ioctx)
1706
1707 def config_get(self, ioctx, key):
1708 """
1709 Get a pool-level configuration override.
1710
1711 :param ioctx: determines which RADOS pool is read
1712 :type ioctx: :class:`rados.Ioctx`
1713 :param key: key
1714 :type key: str
1715 :returns: str - value
1716 """
1717 conf_key = 'conf_' + key
1718 conf_key = cstr(conf_key, 'key')
1719 cdef:
1720 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1721 char *_key = conf_key
1722 size_t size = 4096
1723 char *value = NULL
1724 int ret
1725 try:
1726 while True:
1727 value = <char *>realloc_chk(value, size)
1728 with nogil:
1729 ret = rbd_pool_metadata_get(_ioctx, _key, value, &size)
1730 if ret != -errno.ERANGE:
1731 break
1732 if ret == -errno.ENOENT:
1733 raise KeyError('no config %s for pool %s' % (key, ioctx.get_pool_name()))
1734 if ret != 0:
1735 raise make_ex(ret, 'error getting config %s for pool %s' %
1736 (key, ioctx.get_pool_name()))
1737 return decode_cstr(value)
1738 finally:
1739 free(value)
1740
1741 def config_set(self, ioctx, key, value):
1742 """
1743 Get a pool-level configuration override.
1744
1745 :param ioctx: determines which RADOS pool is read
1746 :type ioctx: :class:`rados.Ioctx`
1747 :param key: key
1748 :type key: str
1749 :param value: value
1750 :type value: str
1751 """
1752 conf_key = 'conf_' + key
1753 conf_key = cstr(conf_key, 'key')
1754 value = cstr(value, 'value')
1755 cdef:
1756 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1757 char *_key = conf_key
1758 char *_value = value
1759 with nogil:
1760 ret = rbd_pool_metadata_set(_ioctx, _key, _value)
1761
1762 if ret != 0:
1763 raise make_ex(ret, 'error setting config %s for pool %s' %
1764 (key, ioctx.get_pool_name()))
1765
1766 def config_remove(self, ioctx, key):
1767 """
1768 Remove a pool-level configuration override.
1769
1770 :param ioctx: determines which RADOS pool is read
1771 :type ioctx: :class:`rados.Ioctx`
1772 :param key: key
1773 :type key: str
1774 :returns: str - value
1775 """
1776 conf_key = 'conf_' + key
1777 conf_key = cstr(conf_key, 'key')
1778 cdef:
1779 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1780 char *_key = conf_key
1781 with nogil:
1782 ret = rbd_pool_metadata_remove(_ioctx, _key)
1783
1784 if ret == -errno.ENOENT:
1785 raise KeyError('no config %s for pool %s' %
1786 (key, ioctx.get_pool_name()))
1787 if ret != 0:
1788 raise make_ex(ret, 'error removing config %s for pool %s' %
1789 (key, ioctx.get_pool_name()))
1790
1791 def group_create(self, ioctx, name):
1792 """
1793 Create a group.
1794
1795 :param ioctx: determines which RADOS pool is used
1796 :type ioctx: :class:`rados.Ioctx`
1797 :param name: the name of the group
1798 :type name: str
1799 :raises: :class:`ObjectExists`
1800 :raises: :class:`InvalidArgument`
1801 :raises: :class:`FunctionNotSupported`
1802 """
1803 name = cstr(name, 'name')
1804 cdef:
1805 char *_name = name
1806 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1807 with nogil:
1808 ret = rbd_group_create(_ioctx, _name)
1809 if ret != 0:
1810 raise make_ex(ret, 'error creating group %s' % name, group_errno_to_exception)
1811
1812 def group_remove(self, ioctx, name):
1813 """
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
1816 from the group.
1817
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
1821 :type name: str
1822 :raises: :class:`ObjectNotFound`
1823 :raises: :class:`InvalidArgument`
1824 :raises: :class:`FunctionNotSupported`
1825 """
1826 name = cstr(name, 'name')
1827 cdef:
1828 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1829 char *_name = name
1830 with nogil:
1831 ret = rbd_group_remove(_ioctx, _name)
1832 if ret != 0:
1833 raise make_ex(ret, 'error removing group', group_errno_to_exception)
1834
1835 def group_list(self, ioctx):
1836 """
1837 List groups.
1838
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`
1843 """
1844 cdef:
1845 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1846 size_t size = 512
1847 char *c_names = NULL
1848 try:
1849 while True:
1850 c_names = <char *>realloc_chk(c_names, size)
1851 with nogil:
1852 ret = rbd_group_list(_ioctx, c_names, &size)
1853 if ret >= 0:
1854 break
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')
1858 if name]
1859 finally:
1860 free(c_names)
1861
1862 def group_rename(self, ioctx, src, dest):
1863 """
1864 Rename an RBD group.
1865
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
1869 :type src: str
1870 :param dest: the new name of the group
1871 :type dest: str
1872 :raises: :class:`ObjectExists`
1873 :raises: :class:`ObjectNotFound`
1874 :raises: :class:`InvalidArgument`
1875 :raises: :class:`FunctionNotSupported`
1876 """
1877 src = cstr(src, 'src')
1878 dest = cstr(dest, 'dest')
1879 cdef:
1880 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1881 char *_src = src
1882 char *_dest = dest
1883 with nogil:
1884 ret = rbd_group_rename(_ioctx, _src, _dest)
1885 if ret != 0:
1886 raise make_ex(ret, 'error renaming group')
1887
1888 def namespace_create(self, ioctx, name):
1889 """
1890 Create an RBD namespace within a pool
1891
1892 :param ioctx: determines which RADOS pool
1893 :type ioctx: :class:`rados.Ioctx`
1894 :param name: namespace name
1895 :type name: str
1896 """
1897 name = cstr(name, 'name')
1898 cdef:
1899 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1900 const char *_name = name
1901 with nogil:
1902 ret = rbd_namespace_create(_ioctx, _name)
1903 if ret != 0:
1904 raise make_ex(ret, 'error creating namespace')
1905
1906 def namespace_remove(self, ioctx, name):
1907 """
1908 Remove an RBD namespace from a pool
1909
1910 :param ioctx: determines which RADOS pool
1911 :type ioctx: :class:`rados.Ioctx`
1912 :param name: namespace name
1913 :type name: str
1914 """
1915 name = cstr(name, 'name')
1916 cdef:
1917 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1918 const char *_name = name
1919 with nogil:
1920 ret = rbd_namespace_remove(_ioctx, _name)
1921 if ret != 0:
1922 raise make_ex(ret, 'error removing namespace')
1923
1924 def namespace_exists(self, ioctx, name):
1925 """
1926 Verifies if a namespace exists within a pool
1927
1928 :param ioctx: determines which RADOS pool
1929 :type ioctx: :class:`rados.Ioctx`
1930 :param name: namespace name
1931 :type name: str
1932 :returns: bool - true if namespace exists
1933 """
1934 name = cstr(name, 'name')
1935 cdef:
1936 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1937 const char *_name = name
1938 bint _exists = False
1939 with nogil:
1940 ret = rbd_namespace_exists(_ioctx, _name, &_exists)
1941 if ret != 0:
1942 raise make_ex(ret, 'error verifying namespace')
1943 return bool(_exists != 0)
1944
1945 def namespace_list(self, ioctx):
1946 """
1947 List all namespaces within a pool
1948
1949 :param ioctx: determines which RADOS pool
1950 :type ioctx: :class:`rados.Ioctx`
1951 :returns: list - collection of namespace names
1952 """
1953 cdef:
1954 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1955 char *_names = NULL
1956 size_t _size = 512
1957 try:
1958 while True:
1959 _names = <char *>realloc_chk(_names, _size)
1960 with nogil:
1961 ret = rbd_namespace_list(_ioctx, _names, &_size)
1962 if ret >= 0:
1963 break
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')
1967 if name]
1968 finally:
1969 free(_names)
1970
1971 def pool_init(self, ioctx, force):
1972 """
1973 Initialize an RBD pool
1974 :param ioctx: determines which RADOS pool
1975 :type ioctx: :class:`rados.Ioctx`
1976 :param force: force init
1977 :type force: bool
1978 """
1979 cdef:
1980 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1981 bint _force = force
1982 with nogil:
1983 ret = rbd_pool_init(_ioctx, _force)
1984 if ret != 0:
1985 raise make_ex(ret, 'error initializing pool')
1986
1987 def pool_stats_get(self, ioctx):
1988 """
1989 Return RBD pool stats
1990
1991 :param ioctx: determines which RADOS pool
1992 :type ioctx: :class:`rados.Ioctx`
1993 :returns: dict - contains the following keys:
1994
1995 * ``image_count`` (int) - image count
1996
1997 * ``image_provisioned_bytes`` (int) - image total HEAD provisioned bytes
1998
1999 * ``image_max_provisioned_bytes`` (int) - image total max provisioned bytes
2000
2001 * ``image_snap_count`` (int) - image snap count
2002
2003 * ``trash_count`` (int) - trash image count
2004
2005 * ``trash_provisioned_bytes`` (int) - trash total HEAD provisioned bytes
2006
2007 * ``trash_max_provisioned_bytes`` (int) - trash total max provisioned bytes
2008
2009 * ``trash_snap_count`` (int) - trash snap count
2010
2011 """
2012 cdef:
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
2023
2024 rbd_pool_stats_create(&_stats)
2025 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_IMAGES,
2026 &_image_count)
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,
2032 &_image_snap_count)
2033 rbd_pool_stats_option_add_uint64(_stats, RBD_POOL_STAT_OPTION_TRASH_IMAGES,
2034 &_trash_count)
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,
2040 &_trash_snap_count)
2041 try:
2042 with nogil:
2043 ret = rbd_pool_stats_get(_ioctx, _stats)
2044 if ret != 0:
2045 raise make_ex(ret, 'error retrieving pool stats')
2046 else:
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}
2055 finally:
2056 rbd_pool_stats_destroy(_stats)
2057
2058 def features_to_string(self, features):
2059 """
2060 Convert features bitmask to str.
2061
2062 :param features: feature bitmask
2063 :type features: int
2064 :returns: str - the features str of the image
2065 :raises: :class:`InvalidArgument`
2066 """
2067 cdef:
2068 int ret = -errno.ERANGE
2069 uint64_t _features = features
2070 size_t size = 1024
2071 char *str_features = NULL
2072 try:
2073 while ret == -errno.ERANGE:
2074 str_features = <char *>realloc_chk(str_features, size)
2075 with nogil:
2076 ret = rbd_features_to_string(_features, str_features, &size)
2077
2078 if ret != 0:
2079 raise make_ex(ret, 'error converting features bitmask to str')
2080 return decode_cstr(str_features)
2081 finally:
2082 free(str_features)
2083
2084 def features_from_string(self, str_features):
2085 """
2086 Get features bitmask from str, if str_features is empty, it will return
2087 RBD_FEATURES_DEFAULT.
2088
2089 :param str_features: feature str
2090 :type str_features: str
2091 :returns: int - the features bitmask of the image
2092 :raises: :class:`InvalidArgument`
2093 """
2094 str_features = cstr(str_features, 'str_features')
2095 cdef:
2096 const char *_str_features = str_features
2097 uint64_t features
2098 with nogil:
2099 ret = rbd_features_from_string(_str_features, &features)
2100 if ret != 0:
2101 raise make_ex(ret, 'error getting features bitmask from str')
2102 return features
2103
2104 def aio_open_image(self, oncomplete, ioctx, name=None, snapshot=None,
2105 read_only=False, image_id=None):
2106 """
2107 Asynchronously open the image at the given snapshot.
2108 Specify either name or id, otherwise :class:`InvalidArgument` is raised.
2109
2110 oncomplete will be called with the created Image object as
2111 well as the completion:
2112
2113 oncomplete(completion, image)
2114
2115 If a snapshot is specified, the image will be read-only, unless
2116 :func:`Image.set_snap` is called later.
2117
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.
2121
2122 To clean up from opening the image, :func:`Image.close` or
2123 :func:`Image.aio_close` should be called.
2124
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
2130 :type name: str
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
2136 :type image_id: str
2137 :returns: :class:`Completion` - the completion object
2138 """
2139
2140 image = Image(ioctx, name, snapshot, read_only, image_id, oncomplete)
2141 comp, image._open_completion = image._open_completion, None
2142 return comp
2143
2144 cdef class MirrorPeerIterator(object):
2145 """
2146 Iterator over mirror peer info for a pool.
2147
2148 Yields a dictionary containing information about a peer.
2149
2150 Keys are:
2151
2152 * ``uuid`` (str) - uuid of the peer
2153
2154 * ``direction`` (int) - direction enum
2155
2156 * ``site_name`` (str) - cluster name of the peer
2157
2158 * ``mirror_uuid`` (str) - mirror uuid of the peer
2159
2160 * ``client_name`` (str) - client name of the peer
2161 """
2162
2163 cdef:
2164 rbd_mirror_peer_site_t *peers
2165 int num_peers
2166
2167 def __init__(self, ioctx):
2168 cdef:
2169 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2170 self.peers = NULL
2171 self.num_peers = 10
2172 while True:
2173 self.peers = <rbd_mirror_peer_site_t *>realloc_chk(
2174 self.peers, self.num_peers * sizeof(rbd_mirror_peer_site_t))
2175 with nogil:
2176 ret = rbd_mirror_peer_site_list(_ioctx, self.peers,
2177 &self.num_peers)
2178 if ret < 0:
2179 if ret == -errno.ERANGE:
2180 continue
2181 self.num_peers = 0
2182 raise make_ex(ret, 'error listing peers')
2183 break
2184
2185 def __iter__(self):
2186 for i in range(self.num_peers):
2187 yield {
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),
2194 }
2195
2196 def __dealloc__(self):
2197 if self.peers:
2198 rbd_mirror_peer_site_list_cleanup(self.peers, self.num_peers)
2199 free(self.peers)
2200
2201 cdef class MirrorImageStatusIterator(object):
2202 """
2203 Iterator over mirror image status for a pool.
2204
2205 Yields a dictionary containing mirror status of an image.
2206
2207 Keys are:
2208
2209 * ``name`` (str) - mirror image name
2210
2211 * ``id`` (str) - mirror image id
2212
2213 * ``info`` (dict) - mirror image info
2214
2215 * ``state`` (int) - status mirror state
2216
2217 * ``description`` (str) - status description
2218
2219 * ``last_update`` (datetime) - last status update time
2220
2221 * ``up`` (bool) - is mirroring agent up
2222
2223 * ``remote_statuses`` (array) -
2224
2225 * ``mirror uuid`` (str) - remote mirror uuid
2226
2227 * ``state`` (int) - status mirror state
2228
2229 * ``description`` (str) - status description
2230
2231 * ``last_update`` (datetime) - last status update time
2232
2233 * ``up`` (bool) - is mirroring agent up
2234 """
2235
2236 cdef:
2237 rados_ioctx_t ioctx
2238 size_t max_read
2239 char *last_read
2240 char **image_ids
2241 rbd_mirror_image_site_status_t *s_status
2242 rbd_mirror_image_global_status_t *images
2243 size_t size
2244
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)
2253 self.size = 0
2254 self.get_next_chunk()
2255
2256
2257 def __iter__(self):
2258 while self.size > 0:
2259 for i in range(self.size):
2260 local_status = None
2261 site_statuses = []
2262
2263 for x in range(self.images[i].site_statuses_count):
2264 s_status = &self.images[i].site_statuses[x]
2265 site_status = {
2266 'state' : s_status.state,
2267 'description' : decode_cstr(s_status.description),
2268 'last_update' : datetime.utcfromtimestamp(s_status.last_update),
2269 'up' : s_status.up,
2270 }
2271 mirror_uuid = decode_cstr(s_status.mirror_uuid)
2272 if mirror_uuid == '':
2273 local_status = site_status
2274 else:
2275 site_status['mirror_uuid'] = mirror_uuid
2276 site_statuses.append(site_status)
2277
2278 status = {
2279 'name' : decode_cstr(self.images[i].name),
2280 'id' : decode_cstr(self.image_ids[i]),
2281 'info' : {
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())
2286 },
2287 'remote_statuses': site_statuses,
2288 }
2289 if local_status:
2290 status.update(local_status)
2291 yield status
2292 if self.size < self.max_read:
2293 break
2294 self.get_next_chunk()
2295
2296 def __dealloc__(self):
2297 rbd_mirror_image_global_status_list_cleanup(self.image_ids, self.images,
2298 self.size)
2299 if self.last_read:
2300 free(self.last_read)
2301 if self.image_ids:
2302 free(self.image_ids)
2303 if self.images:
2304 free(self.images)
2305
2306 def get_next_chunk(self):
2307 if self.size > 0:
2308 rbd_mirror_image_global_status_list_cleanup(self.image_ids,
2309 self.images,
2310 self.size)
2311 self.size = 0
2312 with nogil:
2313 ret = rbd_mirror_image_global_status_list(self.ioctx,
2314 self.last_read,
2315 self.max_read,
2316 self.image_ids,
2317 self.images, &self.size)
2318 if ret < 0:
2319 raise make_ex(ret, 'error listing mirror images status')
2320 if self.size > 0:
2321 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2322 free(self.last_read)
2323 self.last_read = strdup(last_read)
2324 else:
2325 free(self.last_read)
2326 self.last_read = strdup("")
2327
2328 cdef class MirrorImageInstanceIdIterator(object):
2329 """
2330 Iterator over mirror image instance id for a pool.
2331
2332 Yields ``(image_id, instance_id)`` tuple.
2333 """
2334
2335 cdef:
2336 rados_ioctx_t ioctx
2337 size_t max_read
2338 char *last_read
2339 char **image_ids
2340 char **instance_ids
2341 size_t size
2342
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)
2351 self.size = 0
2352 self.get_next_chunk()
2353
2354 def __iter__(self):
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:
2360 break
2361 self.get_next_chunk()
2362
2363 def __dealloc__(self):
2364 rbd_mirror_image_instance_id_list_cleanup(self.image_ids,
2365 self.instance_ids, self.size)
2366 if self.last_read:
2367 free(self.last_read)
2368 if self.image_ids:
2369 free(self.image_ids)
2370 if self.instance_ids:
2371 free(self.instance_ids)
2372
2373 def get_next_chunk(self):
2374 if self.size > 0:
2375 rbd_mirror_image_instance_id_list_cleanup(self.image_ids,
2376 self.instance_ids,
2377 self.size)
2378 self.size = 0
2379 with nogil:
2380 ret = rbd_mirror_image_instance_id_list(self.ioctx, self.last_read,
2381 self.max_read,
2382 self.image_ids,
2383 self.instance_ids,
2384 &self.size)
2385 if ret < 0:
2386 raise make_ex(ret, 'error listing mirror images instance ids')
2387 if self.size > 0:
2388 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2389 free(self.last_read)
2390 self.last_read = strdup(last_read)
2391 else:
2392 free(self.last_read)
2393 self.last_read = strdup("")
2394
2395 cdef class MirrorImageInfoIterator(object):
2396 """
2397 Iterator over mirror image info for a pool.
2398
2399 Yields ``(image_id, info)`` tuple.
2400 """
2401
2402 cdef:
2403 rados_ioctx_t ioctx
2404 rbd_mirror_image_mode_t mode_filter
2405 rbd_mirror_image_mode_t *mode_filter_ptr
2406 size_t max_read
2407 char *last_read
2408 char **image_ids
2409 rbd_mirror_image_info_t *info_entries
2410 rbd_mirror_image_mode_t *mode_entries
2411 size_t size
2412
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
2418 else:
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)
2428 self.size = 0
2429 self.get_next_chunk()
2430
2431 def __iter__(self):
2432 while self.size > 0:
2433 for i in range(self.size):
2434 yield (decode_cstr(self.image_ids[i]),
2435 {
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,
2440 })
2441 if self.size < self.max_read:
2442 break
2443 self.get_next_chunk()
2444
2445 def __dealloc__(self):
2446 rbd_mirror_image_info_list_cleanup(self.image_ids, self.info_entries,
2447 self.size)
2448 if self.last_read:
2449 free(self.last_read)
2450 if self.image_ids:
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)
2456
2457 def get_next_chunk(self):
2458 if self.size > 0:
2459 rbd_mirror_image_info_list_cleanup(self.image_ids,
2460 self.info_entries, self.size)
2461 self.size = 0
2462 with nogil:
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)
2467 if ret < 0:
2468 raise make_ex(ret, 'error listing mirror image info')
2469 if self.size > 0:
2470 last_read = cstr(self.image_ids[self.size - 1], 'last_read')
2471 free(self.last_read)
2472 self.last_read = strdup(last_read)
2473 else:
2474 free(self.last_read)
2475 self.last_read = strdup("")
2476
2477 cdef class PoolMetadataIterator(object):
2478 """
2479 Iterator over pool metadata list.
2480
2481 Yields ``(key, value)`` tuple.
2482
2483 * ``key`` (str) - metadata key
2484 * ``value`` (str) - metadata value
2485 """
2486
2487 cdef:
2488 rados_ioctx_t ioctx
2489 char *last_read
2490 uint64_t max_read
2491 object next_chunk
2492
2493 def __init__(self, ioctx):
2494 self.ioctx = convert_ioctx(ioctx)
2495 self.last_read = strdup("")
2496 self.max_read = 32
2497 self.get_next_chunk()
2498
2499 def __iter__(self):
2500 while len(self.next_chunk) > 0:
2501 for pair in self.next_chunk:
2502 yield pair
2503 if len(self.next_chunk) < self.max_read:
2504 break
2505 self.get_next_chunk()
2506
2507 def __dealloc__(self):
2508 if self.last_read:
2509 free(self.last_read)
2510
2511 def get_next_chunk(self):
2512 cdef:
2513 char *c_keys = NULL
2514 size_t keys_size = 4096
2515 char *c_vals = NULL
2516 size_t vals_size = 4096
2517 try:
2518 while True:
2519 c_keys = <char *>realloc_chk(c_keys, keys_size)
2520 c_vals = <char *>realloc_chk(c_vals, vals_size)
2521 with nogil:
2522 ret = rbd_pool_metadata_list(self.ioctx, self.last_read,
2523 self.max_read, c_keys,
2524 &keys_size, c_vals, &vals_size)
2525 if ret >= 0:
2526 break
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]
2533 if len(keys) > 0:
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))
2538 finally:
2539 free(c_keys)
2540 free(c_vals)
2541
2542 cdef class ConfigPoolIterator(object):
2543 """
2544 Iterator over pool-level overrides for a pool.
2545
2546 Yields a dictionary containing information about an override.
2547
2548 Keys are:
2549
2550 * ``name`` (str) - override name
2551
2552 * ``value`` (str) - override value
2553
2554 * ``source`` (str) - override source
2555 """
2556
2557 cdef:
2558 rbd_config_option_t *options
2559 int num_options
2560
2561 def __init__(self, ioctx):
2562 cdef:
2563 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
2564 self.options = NULL
2565 self.num_options = 32
2566 while True:
2567 self.options = <rbd_config_option_t *>realloc_chk(
2568 self.options, self.num_options * sizeof(rbd_config_option_t))
2569 with nogil:
2570 ret = rbd_config_pool_list(_ioctx, self.options, &self.num_options)
2571 if ret < 0:
2572 if ret == -errno.ERANGE:
2573 continue
2574 self.num_options = 0
2575 raise make_ex(ret, 'error listing config options')
2576 break
2577
2578 def __iter__(self):
2579 for i in range(self.num_options):
2580 yield {
2581 'name' : decode_cstr(self.options[i].name),
2582 'value' : decode_cstr(self.options[i].value),
2583 'source' : self.options[i].source,
2584 }
2585
2586 def __dealloc__(self):
2587 if self.options:
2588 rbd_config_pool_list_cleanup(self.options, self.num_options)
2589 free(self.options)
2590
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():
2597 return -9000
2598 ret = (<object>cb)(offset, length, bool(write))
2599 if ret is None:
2600 return 0
2601 return ret
2602
2603 cdef class Group(object):
2604 """
2605 This class represents an RBD group. It is used to interact with
2606 snapshots and images members.
2607 """
2608
2609 cdef object name
2610 cdef char *_name
2611 cdef object ioctx
2612
2613 cdef rados_ioctx_t _ioctx
2614
2615 def __init__(self, ioctx, name):
2616 name = cstr(name, 'name')
2617 self.name = name
2618
2619 self._ioctx = convert_ioctx(ioctx)
2620 self._name = name
2621
2622 def __enter__(self):
2623 return self
2624
2625 def __exit__(self, type_, value, traceback):
2626 return False
2627
2628 def add_image(self, image_ioctx, image_name):
2629 """
2630 Add an image to a group.
2631
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
2635 :type name: str
2636
2637 :raises: :class:`ObjectNotFound`
2638 :raises: :class:`ObjectExists`
2639 :raises: :class:`InvalidArgument`
2640 :raises: :class:`FunctionNotSupported`
2641 """
2642 image_name = cstr(image_name, 'image_name')
2643 cdef:
2644 rados_ioctx_t _image_ioctx = convert_ioctx(image_ioctx)
2645 char *_image_name = image_name
2646 with nogil:
2647 ret = rbd_group_image_add(self._ioctx, self._name, _image_ioctx, _image_name)
2648 if ret != 0:
2649 raise make_ex(ret, 'error adding image to group', group_errno_to_exception)
2650
2651 def remove_image(self, image_ioctx, image_name):
2652 """
2653 Remove an image from a group.
2654
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
2658 :type name: str
2659
2660 :raises: :class:`ObjectNotFound`
2661 :raises: :class:`InvalidArgument`
2662 :raises: :class:`FunctionNotSupported`
2663 """
2664 image_name = cstr(image_name, 'image_name')
2665 cdef:
2666 rados_ioctx_t _image_ioctx = convert_ioctx(image_ioctx)
2667 char *_image_name = image_name
2668 with nogil:
2669 ret = rbd_group_image_remove(self._ioctx, self._name, _image_ioctx, _image_name)
2670 if ret != 0:
2671 raise make_ex(ret, 'error removing image from group', group_errno_to_exception)
2672
2673
2674 def list_images(self):
2675 """
2676 Iterate over the images of a group.
2677
2678 :returns: :class:`GroupImageIterator`
2679 """
2680 return GroupImageIterator(self)
2681
2682 def create_snap(self, snap_name, flags=0):
2683 """
2684 Create a snapshot for the group.
2685
2686 :param snap_name: the name of the snapshot to create
2687 :param flags: create snapshot flags
2688 :type name: str
2689
2690 :raises: :class:`ObjectNotFound`
2691 :raises: :class:`ObjectExists`
2692 :raises: :class:`InvalidArgument`
2693 :raises: :class:`FunctionNotSupported`
2694 """
2695 snap_name = cstr(snap_name, 'snap_name')
2696 cdef:
2697 char *_snap_name = snap_name
2698 uint32_t _flags = flags
2699 with nogil:
2700 ret = rbd_group_snap_create2(self._ioctx, self._name, _snap_name,
2701 _flags)
2702 if ret != 0:
2703 raise make_ex(ret, 'error creating group snapshot', group_errno_to_exception)
2704
2705 def remove_snap(self, snap_name):
2706 """
2707 Remove a snapshot from the group.
2708
2709 :param snap_name: the name of the snapshot to remove
2710 :type name: str
2711
2712 :raises: :class:`ObjectNotFound`
2713 :raises: :class:`InvalidArgument`
2714 :raises: :class:`FunctionNotSupported`
2715 """
2716 snap_name = cstr(snap_name, 'snap_name')
2717 cdef:
2718 char *_snap_name = snap_name
2719 with nogil:
2720 ret = rbd_group_snap_remove(self._ioctx, self._name, _snap_name)
2721 if ret != 0:
2722 raise make_ex(ret, 'error removing group snapshot', group_errno_to_exception)
2723
2724 def rename_snap(self, old_snap_name, new_snap_name):
2725 """
2726 Rename group's snapshot.
2727
2728 :raises: :class:`ObjectNotFound`
2729 :raises: :class:`ObjectExists`
2730 :raises: :class:`InvalidArgument`
2731 :raises: :class:`FunctionNotSupported`
2732 """
2733
2734 old_snap_name = cstr(old_snap_name, 'old_snap_name')
2735 new_snap_name = cstr(new_snap_name, 'new_snap_name')
2736 cdef:
2737 char *_old_snap_name = old_snap_name
2738 char *_new_snap_name = new_snap_name
2739 with nogil:
2740 ret = rbd_group_snap_rename(self._ioctx, self._name, _old_snap_name,
2741 _new_snap_name)
2742 if ret != 0:
2743 raise make_ex(ret, 'error renaming group snapshot',
2744 group_errno_to_exception)
2745
2746 def list_snaps(self):
2747 """
2748 Iterate over the images of a group.
2749
2750 :returns: :class:`GroupSnapIterator`
2751 """
2752 return GroupSnapIterator(self)
2753
2754 def rollback_to_snap(self, name):
2755 """
2756 Rollback group to snapshot.
2757
2758 :param name: the group snapshot to rollback to
2759 :type name: str
2760 :raises: :class:`ObjectNotFound`
2761 :raises: :class:`IOError`
2762 """
2763 name = cstr(name, 'name')
2764 cdef char *_name = name
2765 with nogil:
2766 ret = rbd_group_snap_rollback(self._ioctx, self._name, _name)
2767 if ret != 0:
2768 raise make_ex(ret, 'error rolling back group to snapshot', group_errno_to_exception)
2769
2770 def requires_not_closed(f):
2771 def wrapper(self, *args, **kwargs):
2772 self.require_not_closed()
2773 return f(self, *args, **kwargs)
2774
2775 return wrapper
2776
2777 cdef class Image(object):
2778 """
2779 This class represents an RBD image. It is used to perform I/O on
2780 the image and interact with snapshots.
2781
2782 **Note**: Any method of this class may raise :class:`ImageNotFound`
2783 if the image has been deleted.
2784 """
2785 cdef rbd_image_t image
2786 cdef bint closed
2787 cdef object name
2788 cdef object ioctx
2789 cdef rados_ioctx_t _ioctx
2790 cdef Completion _open_completion
2791
2792 def __init__(self, ioctx, name=None, snapshot=None,
2793 read_only=False, image_id=None, _oncomplete=None):
2794 """
2795 Open the image at the given snapshot.
2796 Specify either name or id, otherwise :class:`InvalidArgument` is raised.
2797
2798 If a snapshot is specified, the image will be read-only, unless
2799 :func:`Image.set_snap` is called later.
2800
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.
2804
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`).
2808
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
2812 :type name: str
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
2818 :type image_id: str
2819 """
2820 name = cstr(name, 'name', opt=True)
2821 image_id = cstr(image_id, 'image_id', opt=True)
2822 snapshot = cstr(snapshot, 'snapshot', opt=True)
2823 self.closed = 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:
2829 self.name = name
2830 else:
2831 self.name = image_id
2832 # Keep around a reference to the ioctx, so it won't get deleted
2833 self.ioctx = ioctx
2834 cdef:
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
2840
2841 if _oncomplete:
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:
2846 self.closed = False
2847 if name is None:
2848 self.name = self.get_name()
2849 return _oncomplete(_completion_v, self)
2850
2851 completion = self.__get_completion(oncomplete)
2852 try:
2853 completion.__persist()
2854 if read_only:
2855 with nogil:
2856 if name is not None:
2857 ret = rbd_aio_open_read_only(
2858 _ioctx, _name, &self.image, _snapshot,
2859 completion.rbd_comp)
2860 else:
2861 ret = rbd_aio_open_by_id_read_only(
2862 _ioctx, _image_id, &self.image, _snapshot,
2863 completion.rbd_comp)
2864 else:
2865 with nogil:
2866 if name is not None:
2867 ret = rbd_aio_open(
2868 _ioctx, _name, &self.image, _snapshot,
2869 completion.rbd_comp)
2870 else:
2871 ret = rbd_aio_open_by_id(
2872 _ioctx, _image_id, &self.image, _snapshot,
2873 completion.rbd_comp)
2874 if ret != 0:
2875 raise make_ex(ret, 'error opening image %s at snapshot %s' %
2876 (self.name, snapshot))
2877 except:
2878 completion.__unpersist()
2879 raise
2880
2881 self._open_completion = completion
2882 return
2883
2884 if read_only:
2885 with nogil:
2886 if name is not None:
2887 ret = rbd_open_read_only(_ioctx, _name, &self.image, _snapshot)
2888 else:
2889 ret = rbd_open_by_id_read_only(_ioctx, _image_id, &self.image, _snapshot)
2890 else:
2891 with nogil:
2892 if name is not None:
2893 ret = rbd_open(_ioctx, _name, &self.image, _snapshot)
2894 else:
2895 ret = rbd_open_by_id(_ioctx, _image_id, &self.image, _snapshot)
2896 if ret != 0:
2897 raise make_ex(ret, 'error opening image %s at snapshot %s' % (self.name, snapshot))
2898 self.closed = False
2899 if name is None:
2900 self.name = self.get_name()
2901
2902 def __enter__(self):
2903 return self
2904
2905 def __exit__(self, type_, value, traceback):
2906 """
2907 Closes the image. See :func:`close`
2908 """
2909 self.close()
2910 return False
2911
2912 def __get_completion(self, oncomplete):
2913 """
2914 Constructs a completion to use with asynchronous operations
2915
2916 :param oncomplete: callback for the completion
2917
2918 :raises: :class:`Error`
2919 :returns: completion object
2920 """
2921
2922 completion_obj = Completion(self, oncomplete)
2923
2924 cdef:
2925 rbd_completion_t completion
2926 PyObject* p_completion_obj= <PyObject*>completion_obj
2927
2928 with nogil:
2929 ret = rbd_aio_create_completion(p_completion_obj, __aio_complete_cb,
2930 &completion)
2931 if ret < 0:
2932 raise make_ex(ret, "error getting a completion")
2933
2934 completion_obj.rbd_comp = completion
2935 return completion_obj
2936
2937 def require_not_closed(self):
2938 """
2939 Checks if the Image is not closed
2940
2941 :raises: :class:`InvalidArgument`
2942 """
2943 if self.closed:
2944 raise InvalidArgument("image is closed")
2945
2946 def close(self):
2947 """
2948 Release the resources used by this image object.
2949
2950 After this is called, this object should not be used.
2951 """
2952 if not self.closed:
2953 self.closed = True
2954 with nogil:
2955 ret = rbd_close(self.image)
2956 if ret < 0:
2957 raise make_ex(ret, 'error while closing image %s' % (
2958 self.name,))
2959
2960 @requires_not_closed
2961 def aio_close(self, oncomplete):
2962 """
2963 Asynchronously close the image.
2964
2965 After this is called, this object should not be used.
2966
2967 :param oncomplete: what to do when close is complete
2968 :type oncomplete: completion
2969 :returns: :class:`Completion` - the completion object
2970 """
2971 cdef Completion completion = self.__get_completion(oncomplete)
2972 self.closed = True
2973 try:
2974 completion.__persist()
2975 with nogil:
2976 ret = rbd_aio_close(self.image, completion.rbd_comp)
2977 if ret < 0:
2978 raise make_ex(ret, 'error while closing image %s' %
2979 self.name)
2980 except:
2981 completion.__unpersist()
2982 raise
2983 return completion
2984
2985 def __dealloc__(self):
2986 self.close()
2987
2988 def __repr__(self):
2989 return "rbd.Image(ioctx, %r)" % self.name
2990
2991 @requires_not_closed
2992 def resize(self, size, allow_shrink=True):
2993 """
2994 Change the size of the image, allow shrink.
2995
2996 :param size: the new size of the image
2997 :type size: int
2998 :param allow_shrink: permit shrinking
2999 :type allow_shrink: bool
3000 """
3001 old_size = self.size()
3002 if old_size == size:
3003 return
3004 if not allow_shrink and old_size > size:
3005 raise InvalidArgument("error allow_shrink is False but old_size > new_size")
3006 cdef:
3007 uint64_t _size = size
3008 bint _allow_shrink = allow_shrink
3009 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3010 with nogil:
3011 ret = rbd_resize2(self.image, _size, _allow_shrink, prog_cb, NULL)
3012 if ret < 0:
3013 raise make_ex(ret, 'error resizing image %s' % self.name)
3014
3015 @requires_not_closed
3016 def stat(self):
3017 """
3018 Get information about the image. Currently parent pool and
3019 parent name are always -1 and ''.
3020
3021 :returns: dict - contains the following keys:
3022
3023 * ``size`` (int) - the size of the image in bytes
3024
3025 * ``obj_size`` (int) - the size of each object that comprises the
3026 image
3027
3028 * ``num_objs`` (int) - the number of objects in the image
3029
3030 * ``order`` (int) - log_2(object_size)
3031
3032 * ``block_name_prefix`` (str) - the prefix of the RADOS objects used
3033 to store the image
3034
3035 * ``parent_pool`` (int) - deprecated
3036
3037 * ``parent_name`` (str) - deprecated
3038
3039 See also :meth:`format` and :meth:`features`.
3040
3041 """
3042 cdef rbd_image_info_t info
3043 with nogil:
3044 ret = rbd_stat(self.image, &info, sizeof(info))
3045 if ret != 0:
3046 raise make_ex(ret, 'error getting info for image %s' % self.name)
3047 return {
3048 'size' : info.size,
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
3055 }
3056
3057 @requires_not_closed
3058 def get_name(self):
3059 """
3060 Get the RBD image name
3061
3062 :returns: str - image name
3063 """
3064 cdef:
3065 int ret = -errno.ERANGE
3066 size_t size = 64
3067 char *image_name = NULL
3068 try:
3069 while ret == -errno.ERANGE:
3070 image_name = <char *>realloc_chk(image_name, size)
3071 with nogil:
3072 ret = rbd_get_name(self.image, image_name, &size)
3073
3074 if ret != 0:
3075 raise make_ex(ret, 'error getting name for image %s' % self.name)
3076 return decode_cstr(image_name)
3077 finally:
3078 free(image_name)
3079
3080 @requires_not_closed
3081 def id(self):
3082 """
3083 Get the RBD v2 internal image id
3084
3085 :returns: str - image id
3086 """
3087 cdef:
3088 int ret = -errno.ERANGE
3089 size_t size = 32
3090 char *image_id = NULL
3091 try:
3092 while ret == -errno.ERANGE and size <= 4096:
3093 image_id = <char *>realloc_chk(image_id, size)
3094 with nogil:
3095 ret = rbd_get_id(self.image, image_id, size)
3096 if ret == -errno.ERANGE:
3097 size *= 2
3098
3099 if ret != 0:
3100 raise make_ex(ret, 'error getting id for image %s' % self.name)
3101 return decode_cstr(image_id)
3102 finally:
3103 free(image_id)
3104
3105 @requires_not_closed
3106 def block_name_prefix(self):
3107 """
3108 Get the RBD block name prefix
3109
3110 :returns: str - block name prefix
3111 """
3112 cdef:
3113 int ret = -errno.ERANGE
3114 size_t size = 32
3115 char *prefix = NULL
3116 try:
3117 while ret == -errno.ERANGE and size <= 4096:
3118 prefix = <char *>realloc_chk(prefix, size)
3119 with nogil:
3120 ret = rbd_get_block_name_prefix(self.image, prefix, size)
3121 if ret == -errno.ERANGE:
3122 size *= 2
3123
3124 if ret != 0:
3125 raise make_ex(ret, 'error getting block name prefix for image %s' % self.name)
3126 return decode_cstr(prefix)
3127 finally:
3128 free(prefix)
3129
3130 @requires_not_closed
3131 def data_pool_id(self):
3132 """
3133 Get the pool id of the pool where the data of this RBD image is stored.
3134
3135 :returns: int - the pool id
3136 """
3137 with nogil:
3138 ret = rbd_get_data_pool_id(self.image)
3139 if ret < 0:
3140 raise make_ex(ret, 'error getting data pool id for image %s' % self.name)
3141 return ret
3142
3143 @requires_not_closed
3144 def get_parent_image_spec(self):
3145 """
3146 Get spec of the cloned image's parent
3147
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
3153
3154 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3155 """
3156 cdef:
3157 rbd_linked_image_spec_t parent_spec
3158 rbd_snap_spec_t snap_spec
3159 with nogil:
3160 ret = rbd_get_parent(self.image, &parent_spec, &snap_spec)
3161 if ret != 0:
3162 raise make_ex(ret, 'error getting parent info for image %s' % self.name)
3163
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)}
3168
3169 rbd_linked_image_spec_cleanup(&parent_spec)
3170 rbd_snap_spec_cleanup(&snap_spec)
3171 return result
3172
3173 @requires_not_closed
3174 def parent_info(self):
3175 """
3176 Deprecated. Use `get_parent_image_spec` instead.
3177
3178 Get information about a cloned image's parent (if any)
3179
3180 :returns: tuple - ``(pool name, image name, snapshot name)`` components
3181 of the parent image
3182 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3183 """
3184 parent = self.get_parent_image_spec()
3185 return (parent['pool_name'], parent['image_name'], parent['snap_name'])
3186
3187 @requires_not_closed
3188 def parent_id(self):
3189 """
3190 Get image id of a cloned image's parent (if any)
3191
3192 :returns: str - the parent id
3193 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3194 """
3195 cdef:
3196 rbd_linked_image_spec_t parent_spec
3197 rbd_snap_spec_t snap_spec
3198 with nogil:
3199 ret = rbd_get_parent(self.image, &parent_spec, &snap_spec)
3200 if ret != 0:
3201 raise make_ex(ret, 'error getting parent info for image %s' % self.name)
3202
3203 result = decode_cstr(parent_spec.image_id)
3204
3205 rbd_linked_image_spec_cleanup(&parent_spec)
3206 rbd_snap_spec_cleanup(&snap_spec)
3207 return result
3208
3209 @requires_not_closed
3210 def migration_source_spec(self):
3211 """
3212 Get migration source spec (if any)
3213
3214 :returns: dict
3215 :raises: :class:`ImageNotFound` if the image is not migration destination
3216 """
3217 cdef:
3218 size_t size = 512
3219 char *spec = NULL
3220 try:
3221 while True:
3222 spec = <char *>realloc_chk(spec, size)
3223 with nogil:
3224 ret = rbd_get_migration_source_spec(self.image, spec, &size)
3225 if ret >= 0:
3226 break
3227 elif ret != -errno.ERANGE:
3228 raise make_ex(ret, 'error retrieving migration source')
3229 return json.loads(decode_cstr(spec))
3230 finally:
3231 free(spec)
3232
3233 @requires_not_closed
3234 def old_format(self):
3235 """
3236 Find out whether the image uses the old RBD format.
3237
3238 :returns: bool - whether the image uses the old RBD format
3239 """
3240 cdef uint8_t old
3241 with nogil:
3242 ret = rbd_get_old_format(self.image, &old)
3243 if ret != 0:
3244 raise make_ex(ret, 'error getting old_format for image %s' % (self.name))
3245 return old != 0
3246
3247 @requires_not_closed
3248 def size(self):
3249 """
3250 Get the size of the image. If open to a snapshot, returns the
3251 size of that snapshot.
3252
3253 :returns: int - the size of the image in bytes
3254 """
3255 cdef uint64_t image_size
3256 with nogil:
3257 ret = rbd_get_size(self.image, &image_size)
3258 if ret != 0:
3259 raise make_ex(ret, 'error getting size for image %s' % (self.name))
3260 return image_size
3261
3262 @requires_not_closed
3263 def features(self):
3264 """
3265 Get the features bitmask of the image.
3266
3267 :returns: int - the features bitmask of the image
3268 """
3269 cdef uint64_t features
3270 with nogil:
3271 ret = rbd_get_features(self.image, &features)
3272 if ret != 0:
3273 raise make_ex(ret, 'error getting features for image %s' % (self.name))
3274 return features
3275
3276 @requires_not_closed
3277 def update_features(self, features, enabled):
3278 """
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.
3282
3283 :param features: feature bitmask to enable/disable
3284 :type features: int
3285 :param enabled: whether to enable/disable the feature
3286 :type enabled: bool
3287 :raises: :class:`InvalidArgument`
3288 """
3289 cdef:
3290 uint64_t _features = features
3291 uint8_t _enabled = bool(enabled)
3292 with nogil:
3293 ret = rbd_update_features(self.image, _features, _enabled)
3294 if ret != 0:
3295 raise make_ex(ret, 'error updating features for image %s' %
3296 (self.name))
3297
3298 @requires_not_closed
3299 def op_features(self):
3300 """
3301 Get the op features bitmask of the image.
3302
3303 :returns: int - the op features bitmask of the image
3304 """
3305 cdef uint64_t op_features
3306 with nogil:
3307 ret = rbd_get_op_features(self.image, &op_features)
3308 if ret != 0:
3309 raise make_ex(ret, 'error getting op features for image %s' % (self.name))
3310 return op_features
3311
3312 @requires_not_closed
3313 def overlap(self):
3314 """
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.
3318
3319 :returns: int - the overlap in bytes
3320 :raises: :class:`ImageNotFound` if the image doesn't have a parent
3321 """
3322 cdef uint64_t overlap
3323 with nogil:
3324 ret = rbd_get_overlap(self.image, &overlap)
3325 if ret != 0:
3326 raise make_ex(ret, 'error getting overlap for image %s' % (self.name))
3327 return overlap
3328
3329 @requires_not_closed
3330 def flags(self):
3331 """
3332 Get the flags bitmask of the image.
3333
3334 :returns: int - the flags bitmask of the image
3335 """
3336 cdef uint64_t flags
3337 with nogil:
3338 ret = rbd_get_flags(self.image, &flags)
3339 if ret != 0:
3340 raise make_ex(ret, 'error getting flags for image %s' % (self.name))
3341 return flags
3342
3343 @requires_not_closed
3344 def group(self):
3345 """
3346 Get information about the image's group.
3347
3348 :returns: dict - contains the following keys:
3349
3350 * ``pool`` (int) - id of the group pool
3351
3352 * ``name`` (str) - name of the group
3353
3354 """
3355 cdef rbd_group_info_t info
3356 with nogil:
3357 ret = rbd_get_group(self.image, &info, sizeof(info))
3358 if ret != 0:
3359 raise make_ex(ret, 'error getting group for image %s' % self.name)
3360 result = {
3361 'pool' : info.pool,
3362 'name' : decode_cstr(info.name)
3363 }
3364 rbd_group_info_cleanup(&info, sizeof(info))
3365 return result
3366
3367 @requires_not_closed
3368 def is_exclusive_lock_owner(self):
3369 """
3370 Get the status of the image exclusive lock.
3371
3372 :returns: bool - true if the image is exclusively locked
3373 """
3374 cdef int owner
3375 with nogil:
3376 ret = rbd_is_exclusive_lock_owner(self.image, &owner)
3377 if ret != 0:
3378 raise make_ex(ret, 'error getting lock status for image %s' % (self.name))
3379 return owner == 1
3380
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):
3384 """
3385 Copy the image to another location.
3386
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
3392 :type features: int
3393 :param order: the image is split into (2**order) byte objects
3394 :type order: int
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`
3406 """
3407 dest_name = cstr(dest_name, 'dest_name')
3408 data_pool = cstr(data_pool, 'data_pool', opt=True)
3409 cdef:
3410 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
3411 char *_dest_name = dest_name
3412 rbd_image_options_t opts
3413
3414 rbd_image_options_create(&opts)
3415 try:
3416 if features is not None:
3417 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
3418 features)
3419 if order is not None:
3420 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
3421 order)
3422 if stripe_unit is not None:
3423 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
3424 stripe_unit)
3425 if stripe_count is not None:
3426 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
3427 stripe_count)
3428 if data_pool is not None:
3429 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
3430 data_pool)
3431 with nogil:
3432 ret = rbd_copy3(self.image, _dest_ioctx, _dest_name, opts)
3433 finally:
3434 rbd_image_options_destroy(opts)
3435 if ret < 0:
3436 raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name))
3437
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):
3441 """
3442 Deep copy the image to another location.
3443
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
3449 :type features: int
3450 :param order: the image is split into (2**order) byte objects
3451 :type order: int
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`
3463 """
3464 dest_name = cstr(dest_name, 'dest_name')
3465 data_pool = cstr(data_pool, 'data_pool', opt=True)
3466 cdef:
3467 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
3468 char *_dest_name = dest_name
3469 rbd_image_options_t opts
3470
3471 rbd_image_options_create(&opts)
3472 try:
3473 if features is not None:
3474 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
3475 features)
3476 if order is not None:
3477 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
3478 order)
3479 if stripe_unit is not None:
3480 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
3481 stripe_unit)
3482 if stripe_count is not None:
3483 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
3484 stripe_count)
3485 if data_pool is not None:
3486 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
3487 data_pool)
3488 with nogil:
3489 ret = rbd_deep_copy(self.image, _dest_ioctx, _dest_name, opts)
3490 finally:
3491 rbd_image_options_destroy(opts)
3492 if ret < 0:
3493 raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name))
3494
3495 @requires_not_closed
3496 def list_snaps(self):
3497 """
3498 Iterate over the snapshots of an image.
3499
3500 :returns: :class:`SnapIterator`
3501 """
3502 return SnapIterator(self)
3503
3504 @requires_not_closed
3505 def create_snap(self, name, flags=0):
3506 """
3507 Create a snapshot of the image.
3508
3509 :param name: the name of the snapshot
3510 :type name: str
3511 :raises: :class:`ImageExists`, :class:`InvalidArgument`
3512 """
3513 name = cstr(name, 'name')
3514 cdef:
3515 char *_name = name
3516 uint32_t _flags = flags
3517 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3518 with nogil:
3519 ret = rbd_snap_create2(self.image, _name, _flags, prog_cb, NULL)
3520 if ret != 0:
3521 raise make_ex(ret, 'error creating snapshot %s from %s' % (name, self.name))
3522
3523 @requires_not_closed
3524 def rename_snap(self, srcname, dstname):
3525 """
3526 rename a snapshot of the image.
3527
3528 :param srcname: the src name of the snapshot
3529 :type srcname: str
3530 :param dstname: the dst name of the snapshot
3531 :type dstname: str
3532 :raises: :class:`ImageExists`
3533 """
3534 srcname = cstr(srcname, 'srcname')
3535 dstname = cstr(dstname, 'dstname')
3536 cdef:
3537 char *_srcname = srcname
3538 char *_dstname = dstname
3539 with nogil:
3540 ret = rbd_snap_rename(self.image, _srcname, _dstname)
3541 if ret != 0:
3542 raise make_ex(ret, 'error renaming snapshot of %s from %s to %s' % (self.name, srcname, dstname))
3543
3544 @requires_not_closed
3545 def remove_snap(self, name):
3546 """
3547 Delete a snapshot of the image.
3548
3549 :param name: the name of the snapshot
3550 :type name: str
3551 :raises: :class:`IOError`, :class:`ImageBusy`, :class:`ImageNotFound`
3552 """
3553 name = cstr(name, 'name')
3554 cdef char *_name = name
3555 with nogil:
3556 ret = rbd_snap_remove(self.image, _name)
3557 if ret != 0:
3558 raise make_ex(ret, 'error removing snapshot %s from %s' % (name, self.name))
3559
3560 @requires_not_closed
3561 def remove_snap2(self, name, flags):
3562 """
3563 Delete a snapshot of the image.
3564
3565 :param name: the name of the snapshot
3566 :param flags: the flags for removal
3567 :type name: str
3568 :raises: :class:`IOError`, :class:`ImageBusy`
3569 """
3570 self.require_not_closed()
3571
3572 name = cstr(name, 'name')
3573 cdef:
3574 char *_name = name
3575 uint32_t _flags = flags
3576 librbd_progress_fn_t prog_cb = &no_op_progress_callback
3577 with nogil:
3578 ret = rbd_snap_remove2(self.image, _name, _flags, prog_cb, NULL)
3579 if ret != 0:
3580 raise make_ex(ret, 'error removing snapshot %s from %s with flags %lx' % (name, self.name, flags))
3581
3582 @requires_not_closed
3583 def remove_snap_by_id(self, snap_id):
3584 """
3585 Delete a snapshot of the image by its id.
3586
3587 :param id: the id of the snapshot
3588 :type name: int
3589 :raises: :class:`IOError`, :class:`ImageBusy`
3590 """
3591 cdef:
3592 uint64_t _snap_id = snap_id
3593 with nogil:
3594 ret = rbd_snap_remove_by_id(self.image, _snap_id)
3595 if ret != 0:
3596 raise make_ex(ret, 'error removing snapshot %s from %s' % (snap_id, self.name))
3597
3598 @requires_not_closed
3599 def rollback_to_snap(self, name):
3600 """
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.
3604
3605 :param name: the snapshot to rollback to
3606 :type name: str
3607 :raises: :class:`IOError`
3608 """
3609 name = cstr(name, 'name')
3610 cdef char *_name = name
3611 with nogil:
3612 ret = rbd_snap_rollback(self.image, _name)
3613 if ret != 0:
3614 raise make_ex(ret, 'error rolling back image %s to snapshot %s' % (self.name, name))
3615
3616 @requires_not_closed
3617 def protect_snap(self, name):
3618 """
3619 Mark a snapshot as protected. This means it can't be deleted
3620 until it is unprotected.
3621
3622 :param name: the snapshot to protect
3623 :type name: str
3624 :raises: :class:`IOError`, :class:`ImageNotFound`
3625 """
3626 name = cstr(name, 'name')
3627 cdef char *_name = name
3628 with nogil:
3629 ret = rbd_snap_protect(self.image, _name)
3630 if ret != 0:
3631 raise make_ex(ret, 'error protecting snapshot %s@%s' % (self.name, name))
3632
3633 @requires_not_closed
3634 def unprotect_snap(self, name):
3635 """
3636 Mark a snapshot unprotected. This allows it to be deleted if
3637 it was protected.
3638
3639 :param name: the snapshot to unprotect
3640 :type name: str
3641 :raises: :class:`IOError`, :class:`ImageNotFound`
3642 """
3643 name = cstr(name, 'name')
3644 cdef char *_name = name
3645 with nogil:
3646 ret = rbd_snap_unprotect(self.image, _name)
3647 if ret != 0:
3648 raise make_ex(ret, 'error unprotecting snapshot %s@%s' % (self.name, name))
3649
3650 @requires_not_closed
3651 def is_protected_snap(self, name):
3652 """
3653 Find out whether a snapshot is protected from deletion.
3654
3655 :param name: the snapshot to check
3656 :type name: str
3657 :returns: bool - whether the snapshot is protected
3658 :raises: :class:`IOError`, :class:`ImageNotFound`
3659 """
3660 name = cstr(name, 'name')
3661 cdef:
3662 char *_name = name
3663 int is_protected
3664 with nogil:
3665 ret = rbd_snap_is_protected(self.image, _name, &is_protected)
3666 if ret != 0:
3667 raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name))
3668 return is_protected == 1
3669
3670 @requires_not_closed
3671 def snap_exists(self, name):
3672 """
3673 Find out whether a snapshot is exists.
3674
3675 :param name: the snapshot to check
3676 :type name: str
3677 :returns: bool - whether the snapshot is exists
3678 """
3679 name = cstr(name, 'name')
3680 cdef:
3681 char *_name = name
3682 bint _exists = False
3683 with nogil:
3684 ret = rbd_snap_exists(self.image, _name, &_exists)
3685 if ret != 0:
3686 raise make_ex(ret, 'error getting snapshot exists for %s' % self.name)
3687 return bool(_exists != 0)
3688
3689 @requires_not_closed
3690 def get_snap_limit(self):
3691 """
3692 Get the snapshot limit for an image.
3693
3694 :returns: int - the snapshot limit for an image
3695 """
3696 cdef:
3697 uint64_t limit
3698 with nogil:
3699 ret = rbd_snap_get_limit(self.image, &limit)
3700 if ret != 0:
3701 raise make_ex(ret, 'error getting snapshot limit for %s' % self.name)
3702 return limit
3703
3704 @requires_not_closed
3705 def set_snap_limit(self, limit):
3706 """
3707 Set the snapshot limit for an image.
3708
3709 :param limit: the new limit to set
3710 """
3711 cdef:
3712 uint64_t _limit = limit
3713 with nogil:
3714 ret = rbd_snap_set_limit(self.image, _limit)
3715 if ret != 0:
3716 raise make_ex(ret, 'error setting snapshot limit for %s' % self.name)
3717 return ret
3718
3719 @requires_not_closed
3720 def get_snap_timestamp(self, snap_id):
3721 """
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
3725 """
3726 cdef:
3727 timespec timestamp
3728 uint64_t _snap_id = snap_id
3729 with nogil:
3730 ret = rbd_snap_get_timestamp(self.image, _snap_id, &timestamp)
3731 if ret != 0:
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)
3734
3735 @requires_not_closed
3736 def remove_snap_limit(self):
3737 """
3738 Remove the snapshot limit for an image, essentially setting
3739 the limit to the maximum size allowed by the implementation.
3740 """
3741 with nogil:
3742 ret = rbd_snap_set_limit(self.image, UINT64_MAX)
3743 if ret != 0:
3744 raise make_ex(ret, 'error removing snapshot limit for %s' % self.name)
3745 return ret
3746
3747 @requires_not_closed
3748 def set_snap(self, name):
3749 """
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.
3753
3754 :param name: the snapshot to read from, or None to unset the snapshot
3755 :type name: str or None
3756 """
3757 name = cstr(name, 'name', opt=True)
3758 cdef char *_name = opt_str(name)
3759 with nogil:
3760 ret = rbd_snap_set(self.image, _name)
3761 if ret != 0:
3762 raise make_ex(ret, 'error setting image %s to snapshot %s' % (self.name, name))
3763
3764 @requires_not_closed
3765 def set_snap_by_id(self, snap_id):
3766 """
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.
3770
3771 :param snap_id: the snapshot to read from, or None to unset the snapshot
3772 :type snap_id: int
3773 """
3774 if not snap_id:
3775 snap_id = _LIBRADOS_SNAP_HEAD
3776 cdef int64_t _snap_id = snap_id
3777 with nogil:
3778 ret = rbd_snap_set_by_id(self.image, _snap_id)
3779 if ret != 0:
3780 raise make_ex(ret, 'error setting image %s to snapshot %d' % (self.name, snap_id))
3781
3782 @requires_not_closed
3783 def snap_get_name(self, snap_id):
3784 """
3785 Get snapshot name by id.
3786
3787 :param snap_id: the snapshot id
3788 :type snap_id: int
3789 :returns: str - snapshot name
3790 :raises: :class:`ImageNotFound`
3791 """
3792 cdef:
3793 int ret = -errno.ERANGE
3794 int64_t _snap_id = snap_id
3795 size_t size = 512
3796 char *image_name = NULL
3797 try:
3798 while ret == -errno.ERANGE:
3799 image_name = <char *>realloc_chk(image_name, size)
3800 with nogil:
3801 ret = rbd_snap_get_name(self.image, _snap_id, image_name, &size)
3802
3803 if ret != 0:
3804 raise make_ex(ret, 'error snap_get_name.')
3805 return decode_cstr(image_name)
3806 finally:
3807 free(image_name)
3808
3809 @requires_not_closed
3810 def snap_get_id(self, snap_name):
3811 """
3812 Get snapshot id by name.
3813
3814 :param snap_name: the snapshot name
3815 :type snap_name: str
3816 :returns: int - snapshot id
3817 :raises: :class:`ImageNotFound`
3818 """
3819 snap_name = cstr(snap_name, 'snap_name')
3820 cdef:
3821 const char *_snap_name = snap_name
3822 uint64_t snap_id
3823 with nogil:
3824 ret = rbd_snap_get_id(self.image, _snap_name, &snap_id)
3825 if ret != 0:
3826 raise make_ex(ret, 'error snap_get_id.')
3827 return snap_id
3828
3829 @requires_not_closed
3830 def read(self, offset, length, fadvise_flags=0):
3831 """
3832 Read data from the image. Raises :class:`InvalidArgument` if
3833 part of the range specified is outside the image.
3834
3835 :param offset: the offset to start reading at
3836 :type offset: int
3837 :param length: how many bytes to read
3838 :type length: int
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`
3843 """
3844
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.
3849 cdef:
3850 char *ret_buf
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)
3856 try:
3857 ret_buf = PyBytes_AsString(ret_s)
3858 with nogil:
3859 ret = rbd_read2(self.image, _offset, _length, ret_buf,
3860 _fadvise_flags)
3861 if ret < 0:
3862 raise make_ex(ret, 'error reading %s %ld~%ld' % (self.name, offset, length))
3863
3864 if ret != <ssize_t>length:
3865 _PyBytes_Resize(&ret_s, ret)
3866
3867 return <object>ret_s
3868 finally:
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)
3874
3875 @requires_not_closed
3876 def diff_iterate(self, offset, length, from_snapshot, iterate_cb,
3877 include_parent = True, whole_object = False):
3878 """
3879 Iterate over the changed extents of an image.
3880
3881 This will call iterate_cb with three arguments:
3882
3883 (offset, length, exists)
3884
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).
3888
3889 If from_snapshot is None, it is interpreted as the beginning
3890 of time and this generates all allocated extents.
3891
3892 The end version is whatever is currently selected (via set_snap)
3893 for the image.
3894
3895 iterate_cb may raise an exception, which will abort the diff and will be
3896 propagated to the caller.
3897
3898 Raises :class:`InvalidArgument` if from_snapshot is after
3899 the currently set snapshot.
3900
3901 Raises :class:`ImageNotFound` if from_snapshot is not the name
3902 of a snapshot of the image.
3903
3904 :param offset: start offset in bytes
3905 :type offset: int
3906 :param length: size of region to report on, in bytes
3907 :type length: int
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,
3912 length, and exists
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`
3919 """
3920 from_snapshot = cstr(from_snapshot, 'from_snapshot', opt=True)
3921 cdef:
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
3926 with nogil:
3927 ret = rbd_diff_iterate2(self.image, _from_snapshot, _offset,
3928 _length, _include_parent, _whole_object,
3929 &diff_iterate_cb, <void *>iterate_cb)
3930 if ret < 0:
3931 msg = 'error generating diff from snapshot %s' % from_snapshot
3932 raise make_ex(ret, msg)
3933
3934 @requires_not_closed
3935 def write(self, data, offset, fadvise_flags=0):
3936 """
3937 Write data to the image. Raises :class:`InvalidArgument` if
3938 part of the write would fall outside the image.
3939
3940 :param data: the data to be written
3941 :type data: bytes
3942 :param offset: where to start writing data
3943 :type offset: int
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`
3949 """
3950 if not isinstance(data, bytes):
3951 raise TypeError('data must be a byte string')
3952 cdef:
3953 uint64_t _offset = offset, length = len(data)
3954 char *_data = data
3955 int _fadvise_flags = fadvise_flags
3956 with nogil:
3957 ret = rbd_write2(self.image, _offset, length, _data, _fadvise_flags)
3958
3959 if ret == <ssize_t>length:
3960 return ret
3961 elif ret < 0:
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))
3965 else:
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))
3969
3970 @requires_not_closed
3971 def discard(self, offset, length):
3972 """
3973 Trim the range from the image. It will be logically filled
3974 with zeroes.
3975 """
3976 cdef uint64_t _offset = offset, _length = length
3977 with nogil:
3978 ret = rbd_discard(self.image, _offset, _length)
3979 if ret < 0:
3980 msg = 'error discarding region %d~%d' % (offset, length)
3981 raise make_ex(ret, msg)
3982
3983 @requires_not_closed
3984 def write_zeroes(self, offset, length, zero_flags = 0):
3985 """
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.
3989 """
3990 cdef:
3991 uint64_t _offset = offset, _length = length
3992 int _zero_flags = zero_flags
3993 with nogil:
3994 ret = rbd_write_zeroes(self.image, _offset, _length,
3995 _zero_flags, 0)
3996 if ret < 0:
3997 msg = 'error zeroing region %d~%d' % (offset, length)
3998 raise make_ex(ret, msg)
3999
4000 @requires_not_closed
4001 def flush(self):
4002 """
4003 Block until all writes are fully flushed if caching is enabled.
4004 """
4005 with nogil:
4006 ret = rbd_flush(self.image)
4007 if ret < 0:
4008 raise make_ex(ret, 'error flushing image')
4009
4010 @requires_not_closed
4011 def invalidate_cache(self):
4012 """
4013 Drop any cached data for the image.
4014 """
4015 with nogil:
4016 ret = rbd_invalidate_cache(self.image)
4017 if ret < 0:
4018 raise make_ex(ret, 'error invalidating cache')
4019
4020 @requires_not_closed
4021 def stripe_unit(self):
4022 """
4023 Return the stripe unit used for the image.
4024 """
4025 cdef uint64_t stripe_unit
4026 with nogil:
4027 ret = rbd_get_stripe_unit(self.image, &stripe_unit)
4028 if ret != 0:
4029 raise make_ex(ret, 'error getting stripe unit for image %s' % (self.name))
4030 return stripe_unit
4031
4032 @requires_not_closed
4033 def stripe_count(self):
4034 """
4035 Return the stripe count used for the image.
4036 """
4037 cdef uint64_t stripe_count
4038 with nogil:
4039 ret = rbd_get_stripe_count(self.image, &stripe_count)
4040 if ret != 0:
4041 raise make_ex(ret, 'error getting stripe count for image %s' % (self.name))
4042 return stripe_count
4043
4044 @requires_not_closed
4045 def create_timestamp(self):
4046 """
4047 Return the create timestamp for the image.
4048 """
4049 cdef:
4050 timespec timestamp
4051 with nogil:
4052 ret = rbd_get_create_timestamp(self.image, &timestamp)
4053 if ret != 0:
4054 raise make_ex(ret, 'error getting create timestamp for image: %s' % (self.name))
4055 return datetime.utcfromtimestamp(timestamp.tv_sec)
4056
4057 @requires_not_closed
4058 def access_timestamp(self):
4059 """
4060 Return the access timestamp for the image.
4061 """
4062 cdef:
4063 timespec timestamp
4064 with nogil:
4065 ret = rbd_get_access_timestamp(self.image, &timestamp)
4066 if ret != 0:
4067 raise make_ex(ret, 'error getting access timestamp for image: %s' % (self.name))
4068 return datetime.fromtimestamp(timestamp.tv_sec)
4069
4070 @requires_not_closed
4071 def modify_timestamp(self):
4072 """
4073 Return the modify timestamp for the image.
4074 """
4075 cdef:
4076 timespec timestamp
4077 with nogil:
4078 ret = rbd_get_modify_timestamp(self.image, &timestamp)
4079 if ret != 0:
4080 raise make_ex(ret, 'error getting modify timestamp for image: %s' % (self.name))
4081 return datetime.fromtimestamp(timestamp.tv_sec)
4082
4083 @requires_not_closed
4084 def flatten(self, on_progress=None):
4085 """
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
4089 """
4090 cdef:
4091 librbd_progress_fn_t _prog_cb = &no_op_progress_callback
4092 void *_prog_arg = NULL
4093 if on_progress:
4094 _prog_cb = &progress_callback
4095 _prog_arg = <void *>on_progress
4096 with nogil:
4097 ret = rbd_flatten_with_progress(self.image, _prog_cb, _prog_arg)
4098 if ret < 0:
4099 raise make_ex(ret, "error flattening %s" % self.name)
4100
4101 @requires_not_closed
4102 def sparsify(self, sparse_size):
4103 """
4104 Reclaim space for zeroed image extents
4105 """
4106 cdef:
4107 size_t _sparse_size = sparse_size
4108 with nogil:
4109 ret = rbd_sparsify(self.image, _sparse_size)
4110 if ret < 0:
4111 raise make_ex(ret, "error sparsifying %s" % self.name)
4112
4113 @requires_not_closed
4114 def rebuild_object_map(self):
4115 """
4116 Rebuild the object map for the image HEAD or currently set snapshot
4117 """
4118 cdef librbd_progress_fn_t prog_cb = &no_op_progress_callback
4119 with nogil:
4120 ret = rbd_rebuild_object_map(self.image, prog_cb, NULL)
4121 if ret < 0:
4122 raise make_ex(ret, "error rebuilding object map %s" % self.name)
4123
4124 @requires_not_closed
4125 def list_children(self):
4126 """
4127 List children of the currently set snapshot (set via set_snap()).
4128
4129 :returns: list - a list of (pool name, image name) tuples
4130 """
4131 cdef:
4132 rbd_linked_image_spec_t *children = NULL
4133 size_t num_children = 10
4134
4135 try:
4136 while True:
4137 children = <rbd_linked_image_spec_t*>realloc_chk(
4138 children, num_children * sizeof(rbd_linked_image_spec_t))
4139 with nogil:
4140 ret = rbd_list_children3(self.image, children,
4141 &num_children)
4142 if ret >= 0:
4143 break
4144 elif ret != -errno.ERANGE:
4145 raise make_ex(ret, 'error listing children.')
4146
4147 return [(decode_cstr(x.pool_name), decode_cstr(x.image_name)) for x
4148 in children[:num_children] if not x.trash]
4149 finally:
4150 if children:
4151 rbd_linked_image_spec_list_cleanup(children, num_children)
4152 free(children)
4153
4154 @requires_not_closed
4155 def list_children2(self):
4156 """
4157 Iterate over the children of the image or its snapshot.
4158
4159 :returns: :class:`ChildIterator`
4160 """
4161 return ChildIterator(self)
4162
4163 @requires_not_closed
4164 def list_descendants(self):
4165 """
4166 Iterate over the descendants of the image.
4167
4168 :returns: :class:`ChildIterator`
4169 """
4170 return ChildIterator(self, True)
4171
4172 @requires_not_closed
4173 def list_lockers(self):
4174 """
4175 List clients that have locked the image and information
4176 about the lock.
4177
4178 :returns: dict - contains the following keys:
4179
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)
4185 tuples
4186 """
4187 cdef:
4188 size_t clients_size = 512, cookies_size = 512
4189 size_t addrs_size = 512, tag_size = 512
4190 int exclusive = 0
4191 char *c_clients = NULL
4192 char *c_cookies = NULL
4193 char *c_addrs = NULL
4194 char *c_tag = NULL
4195
4196 try:
4197 while True:
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)
4202 with nogil:
4203 ret = rbd_list_lockers(self.image, &exclusive,
4204 c_tag, &tag_size,
4205 c_clients, &clients_size,
4206 c_cookies, &cookies_size,
4207 c_addrs, &addrs_size)
4208 if ret >= 0:
4209 break
4210 elif ret != -errno.ERANGE:
4211 raise make_ex(ret, 'error listing images')
4212 if ret == 0:
4213 return []
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'))
4217 return {
4218 'tag' : decode_cstr(c_tag),
4219 'exclusive' : exclusive == 1,
4220 'lockers' : list(zip(clients, cookies, addrs)),
4221 }
4222 finally:
4223 free(c_clients)
4224 free(c_cookies)
4225 free(c_addrs)
4226 free(c_tag)
4227
4228 @requires_not_closed
4229 def lock_acquire(self, lock_mode):
4230 """
4231 Acquire a managed lock on the image.
4232
4233 :param lock_mode: lock mode to set
4234 :type lock_mode: int
4235 :raises: :class:`ImageBusy` if the lock could not be acquired
4236 """
4237 cdef:
4238 rbd_lock_mode_t _lock_mode = lock_mode
4239 with nogil:
4240 ret = rbd_lock_acquire(self.image, _lock_mode)
4241 if ret < 0:
4242 raise make_ex(ret, 'error acquiring lock on image')
4243
4244 @requires_not_closed
4245 def lock_release(self):
4246 """
4247 Release a managed lock on the image that was previously acquired.
4248 """
4249 with nogil:
4250 ret = rbd_lock_release(self.image)
4251 if ret < 0:
4252 raise make_ex(ret, 'error releasing lock on image')
4253
4254 @requires_not_closed
4255 def lock_get_owners(self):
4256 """
4257 Iterate over the lock owners of an image.
4258
4259 :returns: :class:`LockOwnerIterator`
4260 """
4261 return LockOwnerIterator(self)
4262
4263 @requires_not_closed
4264 def lock_break(self, lock_mode, lock_owner):
4265 """
4266 Break the image lock held by a another client.
4267
4268 :param lock_owner: the owner of the lock to break
4269 :type lock_owner: str
4270 """
4271 lock_owner = cstr(lock_owner, 'lock_owner')
4272 cdef:
4273 rbd_lock_mode_t _lock_mode = lock_mode
4274 char *_lock_owner = lock_owner
4275 with nogil:
4276 ret = rbd_lock_break(self.image, _lock_mode, _lock_owner)
4277 if ret < 0:
4278 raise make_ex(ret, 'error breaking lock on image')
4279
4280 @requires_not_closed
4281 def lock_exclusive(self, cookie):
4282 """
4283 Take an exclusive lock on the image.
4284
4285 :raises: :class:`ImageBusy` if a different client or cookie locked it
4286 :class:`ImageExists` if the same client and cookie locked it
4287 """
4288 cookie = cstr(cookie, 'cookie')
4289 cdef char *_cookie = cookie
4290 with nogil:
4291 ret = rbd_lock_exclusive(self.image, _cookie)
4292 if ret < 0:
4293 raise make_ex(ret, 'error acquiring exclusive lock on image')
4294
4295 @requires_not_closed
4296 def lock_shared(self, cookie, tag):
4297 """
4298 Take a shared lock on the image. The tag must match
4299 that of the existing lockers, if any.
4300
4301 :raises: :class:`ImageBusy` if a different client or cookie locked it
4302 :class:`ImageExists` if the same client and cookie locked it
4303 """
4304 cookie = cstr(cookie, 'cookie')
4305 tag = cstr(tag, 'tag')
4306 cdef:
4307 char *_cookie = cookie
4308 char *_tag = tag
4309 with nogil:
4310 ret = rbd_lock_shared(self.image, _cookie, _tag)
4311 if ret < 0:
4312 raise make_ex(ret, 'error acquiring shared lock on image')
4313
4314 @requires_not_closed
4315 def unlock(self, cookie):
4316 """
4317 Release a lock on the image that was locked by this rados client.
4318 """
4319 cookie = cstr(cookie, 'cookie')
4320 cdef char *_cookie = cookie
4321 with nogil:
4322 ret = rbd_unlock(self.image, _cookie)
4323 if ret < 0:
4324 raise make_ex(ret, 'error unlocking image')
4325
4326 @requires_not_closed
4327 def break_lock(self, client, cookie):
4328 """
4329 Release a lock held by another rados client.
4330 """
4331 client = cstr(client, 'client')
4332 cookie = cstr(cookie, 'cookie')
4333 cdef:
4334 char *_client = client
4335 char *_cookie = cookie
4336 with nogil:
4337 ret = rbd_break_lock(self.image, _client, _cookie)
4338 if ret < 0:
4339 raise make_ex(ret, 'error unlocking image')
4340
4341 @requires_not_closed
4342 def mirror_image_enable(self, mode=RBD_MIRROR_IMAGE_MODE_JOURNAL):
4343 """
4344 Enable mirroring for the image.
4345 """
4346 cdef rbd_mirror_image_mode_t c_mode = mode
4347 with nogil:
4348 ret = rbd_mirror_image_enable2(self.image, c_mode)
4349 if ret < 0:
4350 raise make_ex(ret, 'error enabling mirroring for image %s' % self.name)
4351
4352 @requires_not_closed
4353 def mirror_image_disable(self, force):
4354 """
4355 Disable mirroring for the image.
4356
4357 :param force: force disabling
4358 :type force: bool
4359 """
4360 cdef bint c_force = force
4361 with nogil:
4362 ret = rbd_mirror_image_disable(self.image, c_force)
4363 if ret < 0:
4364 raise make_ex(ret, 'error disabling mirroring for image %s' % self.name)
4365
4366 @requires_not_closed
4367 def mirror_image_promote(self, force):
4368 """
4369 Promote the image to primary for mirroring.
4370
4371 :param force: force promoting
4372 :type force: bool
4373 """
4374 cdef bint c_force = force
4375 with nogil:
4376 ret = rbd_mirror_image_promote(self.image, c_force)
4377 if ret < 0:
4378 raise make_ex(ret, 'error promoting image %s to primary' % self.name)
4379
4380 @requires_not_closed
4381 def mirror_image_demote(self):
4382 """
4383 Demote the image to secondary for mirroring.
4384 """
4385 with nogil:
4386 ret = rbd_mirror_image_demote(self.image)
4387 if ret < 0:
4388 raise make_ex(ret, 'error demoting image %s to secondary' % self.name)
4389
4390 @requires_not_closed
4391 def mirror_image_resync(self):
4392 """
4393 Flag the image to resync.
4394 """
4395 with nogil:
4396 ret = rbd_mirror_image_resync(self.image)
4397 if ret < 0:
4398 raise make_ex(ret, 'error to resync image %s' % self.name)
4399
4400 @requires_not_closed
4401 def mirror_image_create_snapshot(self, flags=0):
4402 """
4403 Create mirror snapshot.
4404
4405 :param flags: create snapshot flags
4406 :type flags: int
4407 :returns: int - the snapshot Id
4408 """
4409 cdef:
4410 uint32_t _flags = flags
4411 uint64_t snap_id
4412 with nogil:
4413 ret = rbd_mirror_image_create_snapshot2(self.image, _flags,
4414 &snap_id)
4415 if ret < 0:
4416 raise make_ex(ret, 'error creating mirror snapshot for image %s' %
4417 self.name)
4418 return snap_id
4419
4420 @requires_not_closed
4421 def aio_mirror_image_create_snapshot(self, flags, oncomplete):
4422 """
4423 Asynchronously create mirror snapshot.
4424
4425 Raises :class:`InvalidArgument` if the image is not in mirror
4426 snapshot mode.
4427
4428 oncomplete will be called with the created snap ID as
4429 well as the completion:
4430
4431 oncomplete(completion, snap_id)
4432
4433 :param flags: create snapshot flags
4434 :type flags: int
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`
4439 """
4440 cdef:
4441 uint32_t _flags = flags
4442 Completion completion
4443
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)
4450
4451 completion = self.__get_completion(oncomplete_)
4452 completion.buf = PyBytes_FromStringAndSize(NULL, sizeof(uint64_t))
4453 try:
4454 completion.__persist()
4455 with nogil:
4456 ret = rbd_aio_mirror_image_create_snapshot(self.image, _flags,
4457 <uint64_t *>completion.buf,
4458 completion.rbd_comp)
4459 if ret < 0:
4460 raise make_ex(ret, 'error creating mirror snapshot for image %s' %
4461 self.name)
4462 except:
4463 completion.__unpersist()
4464 raise
4465
4466 return completion
4467
4468 @requires_not_closed
4469 def mirror_image_get_info(self):
4470 """
4471 Get mirror info for the image.
4472
4473 :returns: dict - contains the following keys:
4474
4475 * ``global_id`` (str) - image global id
4476
4477 * ``state`` (int) - mirror state
4478
4479 * ``primary`` (bool) - is image primary
4480 """
4481 cdef rbd_mirror_image_info_t c_info
4482 with nogil:
4483 ret = rbd_mirror_image_get_info(self.image, &c_info, sizeof(c_info))
4484 if ret != 0:
4485 raise make_ex(ret, 'error getting mirror info for image %s' % self.name)
4486 info = {
4487 'global_id' : decode_cstr(c_info.global_id),
4488 'state' : int(c_info.state),
4489 'primary' : c_info.primary,
4490 }
4491 rbd_mirror_image_get_info_cleanup(&c_info)
4492 return info
4493
4494 @requires_not_closed
4495 def aio_mirror_image_get_info(self, oncomplete):
4496 """
4497 Asynchronously get mirror info for the image.
4498
4499 oncomplete will be called with the returned info as
4500 well as the completion:
4501
4502 oncomplete(completion, info)
4503
4504 :param oncomplete: what to do when get info is complete
4505 :type oncomplete: completion
4506 :returns: :class:`Completion` - the completion object
4507 """
4508 cdef:
4509 Completion completion
4510
4511 def oncomplete_(completion_v):
4512 cdef:
4513 Completion _completion_v = completion_v
4514 rbd_mirror_image_info_t *c_info = <rbd_mirror_image_info_t *>_completion_v.buf
4515 info = {
4516 'global_id' : decode_cstr(c_info[0].global_id),
4517 'state' : int(c_info[0].state),
4518 'primary' : c_info[0].primary,
4519 }
4520 rbd_mirror_image_get_info_cleanup(c_info)
4521 return oncomplete(_completion_v, info)
4522
4523 completion = self.__get_completion(oncomplete_)
4524 completion.buf = PyBytes_FromStringAndSize(
4525 NULL, sizeof(rbd_mirror_image_info_t))
4526 try:
4527 completion.__persist()
4528 with nogil:
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)
4532 if ret != 0:
4533 raise make_ex(
4534 ret, 'error getting mirror info for image %s' % self.name)
4535 except:
4536 completion.__unpersist()
4537 raise
4538
4539 return completion
4540
4541 @requires_not_closed
4542 def mirror_image_get_mode(self):
4543 """
4544 Get mirror mode for the image.
4545
4546 :returns: int - mirror mode
4547 """
4548 cdef rbd_mirror_image_mode_t c_mode
4549 with nogil:
4550 ret = rbd_mirror_image_get_mode(self.image, &c_mode)
4551 if ret != 0:
4552 raise make_ex(ret, 'error getting mirror mode for image %s' % self.name)
4553 return int(c_mode)
4554
4555 @requires_not_closed
4556 def aio_mirror_image_get_mode(self, oncomplete):
4557 """
4558 Asynchronously get mirror mode for the image.
4559
4560 oncomplete will be called with the returned mode as
4561 well as the completion:
4562
4563 oncomplete(completion, mode)
4564
4565 :param oncomplete: what to do when get info is complete
4566 :type oncomplete: completion
4567 :returns: :class:`Completion` - the completion object
4568 """
4569 cdef:
4570 Completion completion
4571
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)
4578
4579 completion = self.__get_completion(oncomplete_)
4580 completion.buf = PyBytes_FromStringAndSize(
4581 NULL, sizeof(rbd_mirror_image_mode_t))
4582 try:
4583 completion.__persist()
4584 with nogil:
4585 ret = rbd_aio_mirror_image_get_mode(
4586 self.image, <rbd_mirror_image_mode_t *>completion.buf,
4587 completion.rbd_comp)
4588 if ret != 0:
4589 raise make_ex(
4590 ret, 'error getting mirror mode for image %s' % self.name)
4591 except:
4592 completion.__unpersist()
4593 raise
4594
4595 return completion
4596
4597 @requires_not_closed
4598 def mirror_image_get_status(self):
4599 """
4600 Get mirror status for the image.
4601
4602 :returns: dict - contains the following keys:
4603
4604 * ``name`` (str) - mirror image name
4605
4606 * ``id`` (str) - mirror image id
4607
4608 * ``info`` (dict) - mirror image info
4609
4610 * ``state`` (int) - status mirror state
4611
4612 * ``description`` (str) - status description
4613
4614 * ``last_update`` (datetime) - last status update time
4615
4616 * ``up`` (bool) - is mirroring agent up
4617
4618 * ``remote_statuses`` (array) -
4619
4620 * ``mirror_uuid`` (str) - remote mirror uuid
4621
4622 * ``state`` (int) - status mirror state
4623
4624 * ``description`` (str) - status description
4625
4626 * ``last_update`` (datetime) - last status update time
4627
4628 * ``up`` (bool) - is mirroring agent up
4629 """
4630 cdef:
4631 rbd_mirror_image_site_status_t *s_status
4632 rbd_mirror_image_global_status_t c_status
4633 try:
4634 with nogil:
4635 ret = rbd_mirror_image_get_global_status(self.image, &c_status,
4636 sizeof(c_status))
4637 if ret != 0:
4638 raise make_ex(ret, 'error getting mirror status for image %s' % self.name)
4639
4640 local_status = None
4641 site_statuses = []
4642 for i in range(c_status.site_statuses_count):
4643 s_status = &c_status.site_statuses[i]
4644 site_status = {
4645 'state' : s_status.state,
4646 'description' : decode_cstr(s_status.description),
4647 'last_update' : datetime.utcfromtimestamp(s_status.last_update),
4648 'up' : s_status.up,
4649 }
4650 mirror_uuid = decode_cstr(s_status.mirror_uuid)
4651 if mirror_uuid == '':
4652 local_status = site_status
4653 else:
4654 site_status['mirror_uuid'] = mirror_uuid
4655 site_statuses.append(site_status)
4656 status = {
4657 'name': decode_cstr(c_status.name),
4658 'id' : self.id(),
4659 'info': {
4660 'global_id' : decode_cstr(c_status.info.global_id),
4661 'state' : int(c_status.info.state),
4662 'primary' : c_status.info.primary,
4663 },
4664 'remote_statuses': site_statuses,
4665 }
4666 if local_status:
4667 status.update(local_status)
4668 finally:
4669 rbd_mirror_image_global_status_cleanup(&c_status)
4670 return status
4671
4672 @requires_not_closed
4673 def mirror_image_get_instance_id(self):
4674 """
4675 Get mirror instance id for the image.
4676
4677 :returns: str - instance id
4678 """
4679 cdef:
4680 int ret = -errno.ERANGE
4681 size_t size = 32
4682 char *instance_id = NULL
4683 try:
4684 while ret == -errno.ERANGE and size <= 4096:
4685 instance_id = <char *>realloc_chk(instance_id, size)
4686 with nogil:
4687 ret = rbd_mirror_image_get_instance_id(self.image,
4688 instance_id, &size)
4689 if ret != 0:
4690 raise make_ex(ret,
4691 'error getting mirror instance id for image %s' %
4692 self.name)
4693 return decode_cstr(instance_id)
4694 finally:
4695 free(instance_id)
4696
4697 @requires_not_closed
4698 def aio_read(self, offset, length, oncomplete, fadvise_flags=0):
4699 """
4700 Asynchronously read data from the image
4701
4702 Raises :class:`InvalidArgument` if part of the range specified is
4703 outside the image.
4704
4705 oncomplete will be called with the returned read value as
4706 well as the completion:
4707
4708 oncomplete(completion, data_read)
4709
4710 :param offset: the offset to start reading at
4711 :type offset: int
4712 :param length: how many bytes to read
4713 :type length: int
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`
4720 """
4721 cdef:
4722 char *ret_buf
4723 uint64_t _offset = offset
4724 size_t _length = length
4725 int _fadvise_flags = fadvise_flags
4726 Completion completion
4727
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)
4734
4735 completion = self.__get_completion(oncomplete_)
4736 completion.buf = PyBytes_FromStringAndSize(NULL, length)
4737 ret_buf = PyBytes_AsString(completion.buf)
4738 try:
4739 completion.__persist()
4740 with nogil:
4741 ret = rbd_aio_read2(self.image, _offset, _length, ret_buf,
4742 completion.rbd_comp, _fadvise_flags)
4743 if ret < 0:
4744 raise make_ex(ret, 'error reading %s %ld~%ld' %
4745 (self.name, offset, length))
4746 except:
4747 completion.__unpersist()
4748 raise
4749
4750 return completion
4751
4752 @requires_not_closed
4753 def aio_write(self, data, offset, oncomplete, fadvise_flags=0):
4754 """
4755 Asynchronously write data to the image
4756
4757 Raises :class:`InvalidArgument` if part of the write would fall outside
4758 the image.
4759
4760 oncomplete will be called with the completion:
4761
4762 oncomplete(completion)
4763
4764 :param data: the data to be written
4765 :type data: bytes
4766 :param offset: the offset to start writing at
4767 :type offset: int
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`
4774 """
4775 cdef:
4776 uint64_t _offset = offset
4777 char *_data = data
4778 size_t _length = len(data)
4779 int _fadvise_flags = fadvise_flags
4780 Completion completion
4781
4782 completion = self.__get_completion(oncomplete)
4783 try:
4784 completion.__persist()
4785 with nogil:
4786 ret = rbd_aio_write2(self.image, _offset, _length, _data,
4787 completion.rbd_comp, _fadvise_flags)
4788 if ret < 0:
4789 raise make_ex(ret, 'error writing %s %ld~%ld' %
4790 (self.name, offset, _length))
4791 except:
4792 completion.__unpersist()
4793 raise
4794
4795 return completion
4796
4797 @requires_not_closed
4798 def aio_discard(self, offset, length, oncomplete):
4799 """
4800 Asynchronously trim the range from the image. It will be logically
4801 filled with zeroes.
4802 """
4803 cdef:
4804 uint64_t _offset = offset
4805 size_t _length = length
4806 Completion completion
4807
4808 completion = self.__get_completion(oncomplete)
4809 try:
4810 completion.__persist()
4811 with nogil:
4812 ret = rbd_aio_discard(self.image, _offset, _length,
4813 completion.rbd_comp)
4814 if ret < 0:
4815 raise make_ex(ret, 'error discarding %s %ld~%ld' %
4816 (self.name, offset, _length))
4817 except:
4818 completion.__unpersist()
4819 raise
4820
4821 return completion
4822
4823 @requires_not_closed
4824 def aio_write_zeroes(self, offset, length, oncomplete, zero_flags = 0):
4825 """
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.
4829 """
4830 cdef:
4831 uint64_t _offset = offset
4832 size_t _length = length
4833 int _zero_flags = zero_flags
4834 Completion completion
4835
4836 completion = self.__get_completion(oncomplete)
4837 try:
4838 completion.__persist()
4839 with nogil:
4840 ret = rbd_aio_write_zeroes(self.image, _offset, _length,
4841 completion.rbd_comp, _zero_flags, 0)
4842 if ret < 0:
4843 raise make_ex(ret, 'error zeroing %s %ld~%ld' %
4844 (self.name, offset, length))
4845 except:
4846 completion.__unpersist()
4847 raise
4848
4849 return completion
4850
4851 @requires_not_closed
4852 def aio_flush(self, oncomplete):
4853 """
4854 Asynchronously wait until all writes are fully flushed if caching is
4855 enabled.
4856 """
4857 cdef Completion completion = self.__get_completion(oncomplete)
4858 try:
4859 completion.__persist()
4860 with nogil:
4861 ret = rbd_aio_flush(self.image, completion.rbd_comp)
4862 if ret < 0:
4863 raise make_ex(ret, 'error flushing')
4864 except:
4865 completion.__unpersist()
4866 raise
4867
4868 return completion
4869
4870 @requires_not_closed
4871 def metadata_get(self, key):
4872 """
4873 Get image metadata for the given key.
4874
4875 :param key: metadata key
4876 :type key: str
4877 :returns: str - metadata value
4878 """
4879 key = cstr(key, 'key')
4880 cdef:
4881 char *_key = key
4882 size_t size = 4096
4883 char *value = NULL
4884 int ret
4885 try:
4886 while True:
4887 value = <char *>realloc_chk(value, size)
4888 with nogil:
4889 ret = rbd_metadata_get(self.image, _key, value, &size)
4890 if ret != -errno.ERANGE:
4891 break
4892 if ret == -errno.ENOENT:
4893 raise KeyError('no metadata %s for image %s' % (key, self.name))
4894 if ret != 0:
4895 raise make_ex(ret, 'error getting metadata %s for image %s' %
4896 (key, self.name))
4897 return decode_cstr(value)
4898 finally:
4899 free(value)
4900
4901 @requires_not_closed
4902 def metadata_set(self, key, value):
4903 """
4904 Set image metadata for the given key.
4905
4906 :param key: metadata key
4907 :type key: str
4908 :param value: metadata value
4909 :type value: str
4910 """
4911 key = cstr(key, 'key')
4912 value = cstr(value, 'value')
4913 cdef:
4914 char *_key = key
4915 char *_value = value
4916 with nogil:
4917 ret = rbd_metadata_set(self.image, _key, _value)
4918
4919 if ret != 0:
4920 raise make_ex(ret, 'error setting metadata %s for image %s' %
4921 (key, self.name))
4922
4923 @requires_not_closed
4924 def metadata_remove(self, key):
4925 """
4926 Remove image metadata for the given key.
4927
4928 :param key: metadata key
4929 :type key: str
4930 """
4931 key = cstr(key, 'key')
4932 cdef:
4933 char *_key = key
4934 with nogil:
4935 ret = rbd_metadata_remove(self.image, _key)
4936
4937 if ret == -errno.ENOENT:
4938 raise KeyError('no metadata %s for image %s' % (key, self.name))
4939 if ret != 0:
4940 raise make_ex(ret, 'error removing metadata %s for image %s' %
4941 (key, self.name))
4942
4943 @requires_not_closed
4944 def metadata_list(self):
4945 """
4946 List image metadata.
4947
4948 :returns: :class:`MetadataIterator`
4949 """
4950 return MetadataIterator(self)
4951
4952 @requires_not_closed
4953 def watchers_list(self):
4954 """
4955 List image watchers.
4956
4957 :returns: :class:`WatcherIterator`
4958 """
4959 return WatcherIterator(self)
4960
4961 @requires_not_closed
4962 def config_list(self):
4963 """
4964 List image-level config overrides.
4965
4966 :returns: :class:`ConfigImageIterator`
4967 """
4968 return ConfigImageIterator(self)
4969
4970 @requires_not_closed
4971 def config_set(self, key, value):
4972 """
4973 Set an image-level configuration override.
4974
4975 :param key: key
4976 :type key: str
4977 :param value: value
4978 :type value: str
4979 """
4980 conf_key = 'conf_' + key
4981 conf_key = cstr(conf_key, 'key')
4982 value = cstr(value, 'value')
4983 cdef:
4984 char *_key = conf_key
4985 char *_value = value
4986 with nogil:
4987 ret = rbd_metadata_set(self.image, _key, _value)
4988
4989 if ret != 0:
4990 raise make_ex(ret, 'error setting config %s for image %s' %
4991 (key, self.name))
4992
4993 @requires_not_closed
4994 def config_get(self, key):
4995 """
4996 Get an image-level configuration override.
4997
4998 :param key: key
4999 :type key: str
5000 :returns: str - value
5001 """
5002 conf_key = 'conf_' + key
5003 conf_key = cstr(conf_key, 'key')
5004 cdef:
5005 char *_key = conf_key
5006 size_t size = 4096
5007 char *value = NULL
5008 int ret
5009 try:
5010 while True:
5011 value = <char *>realloc_chk(value, size)
5012 with nogil:
5013 ret = rbd_metadata_get(self.image, _key, value, &size)
5014 if ret != -errno.ERANGE:
5015 break
5016 if ret == -errno.ENOENT:
5017 raise KeyError('no config %s for image %s' % (key, self.name))
5018 if ret != 0:
5019 raise make_ex(ret, 'error getting config %s for image %s' %
5020 (key, self.name))
5021 return decode_cstr(value)
5022 finally:
5023 free(value)
5024
5025 @requires_not_closed
5026 def config_remove(self, key):
5027 """
5028 Remove an image-level configuration override.
5029
5030 :param key: key
5031 :type key: str
5032 """
5033 conf_key = 'conf_' + key
5034 conf_key = cstr(conf_key, 'key')
5035 cdef:
5036 char *_key = conf_key
5037 with nogil:
5038 ret = rbd_metadata_remove(self.image, _key)
5039
5040 if ret == -errno.ENOENT:
5041 raise KeyError('no config %s for image %s' % (key, self.name))
5042 if ret != 0:
5043 raise make_ex(ret, 'error removing config %s for image %s' %
5044 (key, self.name))
5045
5046 @requires_not_closed
5047 def snap_get_namespace_type(self, snap_id):
5048 """
5049 Get the snapshot namespace type.
5050 :param snap_id: the snapshot id of a snap shot
5051 :type key: int
5052 """
5053 cdef:
5054 rbd_snap_namespace_type_t namespace_type
5055 uint64_t _snap_id = snap_id
5056 with nogil:
5057 ret = rbd_snap_get_namespace_type(self.image, _snap_id, &namespace_type)
5058 if ret != 0:
5059 raise make_ex(ret, 'error getting snapshot namespace type for image: %s, snap_id: %d' % (self.name, snap_id))
5060
5061 return namespace_type
5062
5063 @requires_not_closed
5064 def snap_get_group_namespace(self, snap_id):
5065 """
5066 get the group namespace details.
5067 :param snap_id: the snapshot id of the group snapshot
5068 :type key: int
5069 :returns: dict - contains the following keys:
5070
5071 * ``pool`` (int) - pool id
5072
5073 * ``name`` (str) - group name
5074
5075 * ``snap_name`` (str) - group snap name
5076 """
5077 cdef:
5078 rbd_snap_group_namespace_t group_namespace
5079 uint64_t _snap_id = snap_id
5080 with nogil:
5081 ret = rbd_snap_get_group_namespace(self.image, _snap_id,
5082 &group_namespace,
5083 sizeof(rbd_snap_group_namespace_t))
5084 if ret != 0:
5085 raise make_ex(ret, 'error getting snapshot group namespace for image: %s, snap_id: %d' % (self.name, snap_id))
5086
5087 info = {
5088 'pool' : group_namespace.group_pool,
5089 'name' : decode_cstr(group_namespace.group_name),
5090 'snap_name' : decode_cstr(group_namespace.group_snap_name)
5091 }
5092 rbd_snap_group_namespace_cleanup(&group_namespace,
5093 sizeof(rbd_snap_group_namespace_t))
5094 return info
5095
5096 @requires_not_closed
5097 def snap_get_trash_namespace(self, snap_id):
5098 """
5099 get the trash namespace details.
5100 :param snap_id: the snapshot id of the trash snapshot
5101 :type key: int
5102 :returns: dict - contains the following keys:
5103
5104 * ``original_name`` (str) - original snap name
5105 """
5106 cdef:
5107 uint64_t _snap_id = snap_id
5108 size_t _size = 512
5109 char *_name = NULL
5110 try:
5111 while True:
5112 _name = <char*>realloc_chk(_name, _size);
5113 with nogil:
5114 ret = rbd_snap_get_trash_namespace(self.image, _snap_id,
5115 _name, _size)
5116 if ret >= 0:
5117 break
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))
5121 return {
5122 'original_name' : decode_cstr(_name)
5123 }
5124 finally:
5125 free(_name)
5126
5127 @requires_not_closed
5128 def snap_get_mirror_namespace(self, snap_id):
5129 """
5130 get the mirror namespace details.
5131 :param snap_id: the snapshot id of the mirror snapshot
5132 :type key: int
5133 :returns: dict - contains the following keys:
5134
5135 * ``state`` (int) - the snapshot state
5136
5137 * ``mirror_peer_uuids`` (list) - mirror peer uuids
5138
5139 * ``complete`` (bool) - True if snapshot is complete
5140
5141 * ``primary_mirror_uuid`` (str) - primary mirror uuid
5142
5143 * ``primary_snap_id`` (int) - primary snapshot Id
5144
5145 * ``last_copied_object_number`` (int) - last copied object number
5146 """
5147 cdef:
5148 rbd_snap_mirror_namespace_t sn
5149 uint64_t _snap_id = snap_id
5150 with nogil:
5151 ret = rbd_snap_get_mirror_namespace(
5152 self.image, _snap_id, &sn,
5153 sizeof(rbd_snap_mirror_namespace_t))
5154 if ret != 0:
5155 raise make_ex(ret, 'error getting snapshot mirror '
5156 'namespace for image: %s, snap_id: %d' %
5157 (self.name, snap_id))
5158 uuids = []
5159 cdef char *p = sn.mirror_peer_uuids
5160 for i in range(sn.mirror_peer_uuids_count):
5161 uuid = decode_cstr(p)
5162 uuids.append(uuid)
5163 p += len(uuid) + 1
5164 info = {
5165 'state' : sn.state,
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,
5171 }
5172 rbd_snap_mirror_namespace_cleanup(
5173 &sn, sizeof(rbd_snap_mirror_namespace_t))
5174 return info
5175
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
5184
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)
5189 with nogil:
5190 ret = rbd_encryption_format(self.image, _format, &_luks1_opts,
5191 sizeof(_luks1_opts))
5192 if ret != 0:
5193 raise make_ex(
5194 ret,
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)
5200 with nogil:
5201 ret = rbd_encryption_format(self.image, _format, &_luks2_opts,
5202 sizeof(_luks2_opts))
5203 if ret != 0:
5204 raise make_ex(
5205 ret,
5206 'error formatting image %s with format luks2' % self.name)
5207 else:
5208 raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
5209
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
5218
5219 if (format == RBD_ENCRYPTION_FORMAT_LUKS1):
5220 _luks1_opts.passphrase = _passphrase
5221 _luks1_opts.passphrase_size = len(passphrase)
5222 with nogil:
5223 ret = rbd_encryption_load(self.image, _format, &_luks1_opts,
5224 sizeof(_luks1_opts))
5225 if ret != 0:
5226 raise make_ex(
5227 ret,
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)
5233 with nogil:
5234 ret = rbd_encryption_load(self.image, _format, &_luks2_opts,
5235 sizeof(_luks2_opts))
5236 if ret != 0:
5237 raise make_ex(
5238 ret,
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)
5244 with nogil:
5245 ret = rbd_encryption_load(self.image, _format, &_luks_opts,
5246 sizeof(_luks_opts))
5247 if ret != 0:
5248 raise make_ex(
5249 ret,
5250 ('error loading encryption on image %s '
5251 'with format luks') % self.name)
5252 else:
5253 raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format')
5254
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)
5262
5263 _specs = <rbd_encryption_spec_t *>malloc(len(specs) *
5264 sizeof(rbd_encryption_spec_t))
5265 if _specs == NULL:
5266 raise MemoryError("malloc failed")
5267
5268 memset(<void *>_specs, 0, len(specs) * sizeof(rbd_encryption_spec_t))
5269 try:
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)
5304 else:
5305 raise make_ex(
5306 -errno.ENOTSUP,
5307 'specs[%d][1]: Unsupported encryption format' % i)
5308 with nogil:
5309 ret = rbd_encryption_load2(self.image, _specs, spec_count)
5310 if ret != 0:
5311 raise make_ex(
5312 ret,
5313 'error loading encryption on image %s' % self.name)
5314 finally:
5315 for i in range(len(specs)):
5316 if _specs[i].opts != NULL:
5317 free(_specs[i].opts)
5318 free(_specs)
5319
5320
5321 cdef class ImageIterator(object):
5322 """
5323 Iterator over RBD images in a pool
5324
5325 Yields a dictionary containing information about the images
5326
5327 Keys are:
5328
5329 * ``id`` (str) - image id
5330
5331 * ``name`` (str) - image name
5332 """
5333 cdef rados_ioctx_t ioctx
5334 cdef rbd_image_spec_t *images
5335 cdef size_t num_images
5336
5337 def __init__(self, ioctx):
5338 self.ioctx = convert_ioctx(ioctx)
5339 self.images = NULL
5340 self.num_images = 1024
5341 while True:
5342 self.images = <rbd_image_spec_t*>realloc_chk(
5343 self.images, self.num_images * sizeof(rbd_image_spec_t))
5344 with nogil:
5345 ret = rbd_list2(self.ioctx, self.images, &self.num_images)
5346 if ret >= 0:
5347 break
5348 elif ret == -errno.ERANGE:
5349 self.num_images *= 2
5350 else:
5351 raise make_ex(ret, 'error listing images.')
5352
5353 def __iter__(self):
5354 for i in range(self.num_images):
5355 yield {
5356 'id' : decode_cstr(self.images[i].id),
5357 'name' : decode_cstr(self.images[i].name)
5358 }
5359
5360 def __dealloc__(self):
5361 if self.images:
5362 rbd_image_spec_list_cleanup(self.images, self.num_images)
5363 free(self.images)
5364
5365
5366 cdef class LockOwnerIterator(object):
5367 """
5368 Iterator over managed lock owners for an image
5369
5370 Yields a dictionary containing information about the image's lock
5371
5372 Keys are:
5373
5374 * ``mode`` (int) - active lock mode
5375
5376 * ``owner`` (str) - lock owner name
5377 """
5378
5379 cdef:
5380 rbd_lock_mode_t lock_mode
5381 char **lock_owners
5382 size_t num_lock_owners
5383 object image
5384
5385 def __init__(self, Image image):
5386 image.require_not_closed()
5387
5388 self.image = image
5389 self.lock_owners = NULL
5390 self.num_lock_owners = 8
5391 while True:
5392 self.lock_owners = <char**>realloc_chk(self.lock_owners,
5393 self.num_lock_owners *
5394 sizeof(char*))
5395 with nogil:
5396 ret = rbd_lock_get_owners(image.image, &self.lock_mode,
5397 self.lock_owners,
5398 &self.num_lock_owners)
5399 if ret >= 0:
5400 break
5401 elif ret == -errno.ENOENT:
5402 self.num_lock_owners = 0
5403 break
5404 elif ret != -errno.ERANGE:
5405 raise make_ex(ret, 'error listing lock owners for image %s' % image.name)
5406
5407 def __iter__(self):
5408 for i in range(self.num_lock_owners):
5409 yield {
5410 'mode' : int(self.lock_mode),
5411 'owner' : decode_cstr(self.lock_owners[i]),
5412 }
5413
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)
5418
5419 cdef class MetadataIterator(object):
5420 """
5421 Iterator over metadata list for an image.
5422
5423 Yields ``(key, value)`` tuple.
5424
5425 * ``key`` (str) - metadata key
5426 * ``value`` (str) - metadata value
5427 """
5428
5429 cdef:
5430 cdef object image
5431 rbd_image_t c_image
5432 char *last_read
5433 uint64_t max_read
5434 object next_chunk
5435
5436 def __init__(self, Image image):
5437 image.require_not_closed()
5438
5439 self.image = image
5440 self.c_image = image.image
5441 self.last_read = strdup("")
5442 self.max_read = 32
5443 self.get_next_chunk()
5444
5445 def __iter__(self):
5446 while len(self.next_chunk) > 0:
5447 for pair in self.next_chunk:
5448 yield pair
5449 if len(self.next_chunk) < self.max_read:
5450 break
5451 self.get_next_chunk()
5452
5453 def __dealloc__(self):
5454 if self.last_read:
5455 free(self.last_read)
5456
5457 def get_next_chunk(self):
5458 self.image.require_not_closed()
5459
5460 cdef:
5461 char *c_keys = NULL
5462 size_t keys_size = 4096
5463 char *c_vals = NULL
5464 size_t vals_size = 4096
5465 try:
5466 while True:
5467 c_keys = <char *>realloc_chk(c_keys, keys_size)
5468 c_vals = <char *>realloc_chk(c_vals, vals_size)
5469 with nogil:
5470 ret = rbd_metadata_list(self.c_image, self.last_read,
5471 self.max_read, c_keys, &keys_size,
5472 c_vals, &vals_size)
5473 if ret >= 0:
5474 break
5475 elif ret != -errno.ERANGE:
5476 raise make_ex(ret, 'error listing metadata for image %s' %
5477 self.image.name)
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]
5482 if len(keys) > 0:
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))
5487 finally:
5488 free(c_keys)
5489 free(c_vals)
5490
5491 cdef class SnapIterator(object):
5492 """
5493 Iterator over snapshot info for an image.
5494
5495 Yields a dictionary containing information about a snapshot.
5496
5497 Keys are:
5498
5499 * ``id`` (int) - numeric identifier of the snapshot
5500
5501 * ``size`` (int) - size of the image at the time of snapshot (in bytes)
5502
5503 * ``name`` (str) - name of the snapshot
5504
5505 * ``namespace`` (int) - enum for snap namespace
5506
5507 * ``group`` (dict) - optional for group namespace snapshots
5508
5509 * ``trash`` (dict) - optional for trash namespace snapshots
5510
5511 * ``mirror`` (dict) - optional for mirror namespace snapshots
5512 """
5513
5514 cdef rbd_snap_info_t *snaps
5515 cdef int num_snaps
5516 cdef object image
5517
5518 def __init__(self, Image image):
5519 image.require_not_closed()
5520
5521 self.image = image
5522 self.snaps = NULL
5523 self.num_snaps = 10
5524 while True:
5525 self.snaps = <rbd_snap_info_t*>realloc_chk(self.snaps,
5526 self.num_snaps *
5527 sizeof(rbd_snap_info_t))
5528 with nogil:
5529 ret = rbd_snap_list(image.image, self.snaps, &self.num_snaps)
5530 if ret >= 0:
5531 self.num_snaps = ret
5532 break
5533 elif ret != -errno.ERANGE:
5534 raise make_ex(ret, 'error listing snapshots for image %s' % image.name)
5535
5536 def __iter__(self):
5537 for i in range(self.num_snaps):
5538 s = {
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)
5543 }
5544 if s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_GROUP:
5545 try:
5546 group = self.image.snap_get_group_namespace(self.snaps[i].id)
5547 except:
5548 group = None
5549 s['group'] = group
5550 elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_TRASH:
5551 try:
5552 trash = self.image.snap_get_trash_namespace(self.snaps[i].id)
5553 except:
5554 trash = None
5555 s['trash'] = trash
5556 elif s['namespace'] == RBD_SNAP_NAMESPACE_TYPE_MIRROR:
5557 try:
5558 mirror = self.image.snap_get_mirror_namespace(
5559 self.snaps[i].id)
5560 except:
5561 mirror = None
5562 s['mirror'] = mirror
5563 yield s
5564
5565 def __dealloc__(self):
5566 if self.snaps:
5567 rbd_snap_list_end(self.snaps)
5568 free(self.snaps)
5569
5570 cdef class TrashIterator(object):
5571 """
5572 Iterator over trash entries.
5573
5574 Yields a dictionary containing trash info of an image.
5575
5576 Keys are:
5577
5578 * `id` (str) - image id
5579
5580 * `name` (str) - image name
5581
5582 * `source` (str) - source of deletion
5583
5584 * `deletion_time` (datetime) - time of deletion
5585
5586 * `deferment_end_time` (datetime) - time that an image is allowed to be
5587 removed from trash
5588 """
5589
5590 cdef:
5591 rados_ioctx_t ioctx
5592 size_t num_entries
5593 rbd_trash_image_info_t *entries
5594
5595 def __init__(self, ioctx):
5596 self.ioctx = convert_ioctx(ioctx)
5597 self.num_entries = 1024
5598 self.entries = NULL
5599 while True:
5600 self.entries = <rbd_trash_image_info_t*>realloc_chk(self.entries,
5601 self.num_entries *
5602 sizeof(rbd_trash_image_info_t))
5603 with nogil:
5604 ret = rbd_trash_list(self.ioctx, self.entries, &self.num_entries)
5605 if ret >= 0:
5606 self.num_entries = ret
5607 break
5608 elif ret != -errno.ERANGE:
5609 raise make_ex(ret, 'error listing trash entries')
5610
5611 __source_string = ['USER', 'MIRRORING']
5612
5613 def __iter__(self):
5614 for i in range(self.num_entries):
5615 yield {
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)
5621 }
5622
5623 def __dealloc__(self):
5624 rbd_trash_list_cleanup(self.entries, self.num_entries)
5625 if self.entries:
5626 free(self.entries)
5627
5628 cdef class ChildIterator(object):
5629 """
5630 Iterator over child info for the image or its snapshot.
5631
5632 Yields a dictionary containing information about a child.
5633
5634 Keys are:
5635
5636 * ``pool`` (str) - name of the pool
5637
5638 * ``pool_namespace`` (str) - namespace of the pool
5639
5640 * ``image`` (str) - name of the child
5641
5642 * ``id`` (str) - id of the child
5643
5644 * ``trash`` (bool) - True if child is in trash bin
5645 """
5646
5647 cdef rbd_linked_image_spec_t *children
5648 cdef size_t num_children
5649 cdef object image
5650
5651 def __init__(self, Image image, descendants=False):
5652 image.require_not_closed()
5653
5654 self.image = image
5655 self.children = NULL
5656 self.num_children = 10
5657 while True:
5658 self.children = <rbd_linked_image_spec_t*>realloc_chk(
5659 self.children, self.num_children * sizeof(rbd_linked_image_spec_t))
5660 if descendants:
5661 with nogil:
5662 ret = rbd_list_descendants(image.image, self.children,
5663 &self.num_children)
5664 else:
5665 with nogil:
5666 ret = rbd_list_children3(image.image, self.children,
5667 &self.num_children)
5668 if ret >= 0:
5669 break
5670 elif ret != -errno.ERANGE:
5671 raise make_ex(ret, 'error listing children.')
5672
5673 def __iter__(self):
5674 for i in range(self.num_children):
5675 yield {
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
5681 }
5682
5683 def __dealloc__(self):
5684 if self.children:
5685 rbd_linked_image_spec_list_cleanup(self.children, self.num_children)
5686 free(self.children)
5687
5688 cdef class WatcherIterator(object):
5689 """
5690 Iterator over watchers of an image.
5691
5692 Yields a dictionary containing information about a watcher.
5693
5694 Keys are:
5695
5696 * ``addr`` (str) - address of the watcher
5697
5698 * ``id`` (int) - id of the watcher
5699
5700 * ``cookie`` (int) - the watcher's cookie
5701 """
5702
5703 cdef rbd_image_watcher_t *watchers
5704 cdef size_t num_watchers
5705 cdef object image
5706
5707 def __init__(self, Image image):
5708 image.require_not_closed()
5709
5710 self.image = image
5711 self.watchers = NULL
5712 self.num_watchers = 10
5713 while True:
5714 self.watchers = <rbd_image_watcher_t*>realloc_chk(self.watchers,
5715 self.num_watchers *
5716 sizeof(rbd_image_watcher_t))
5717 with nogil:
5718 ret = rbd_watchers_list(image.image, self.watchers, &self.num_watchers)
5719 if ret >= 0:
5720 break
5721 elif ret != -errno.ERANGE:
5722 raise make_ex(ret, 'error listing watchers.')
5723
5724 def __iter__(self):
5725 for i in range(self.num_watchers):
5726 yield {
5727 'addr' : decode_cstr(self.watchers[i].addr),
5728 'id' : self.watchers[i].id,
5729 'cookie' : self.watchers[i].cookie
5730 }
5731
5732 def __dealloc__(self):
5733 if self.watchers:
5734 rbd_watchers_list_cleanup(self.watchers, self.num_watchers)
5735 free(self.watchers)
5736
5737 cdef class ConfigImageIterator(object):
5738 """
5739 Iterator over image-level overrides for an image.
5740
5741 Yields a dictionary containing information about an override.
5742
5743 Keys are:
5744
5745 * ``name`` (str) - override name
5746
5747 * ``value`` (str) - override value
5748
5749 * ``source`` (str) - override source
5750 """
5751
5752 cdef:
5753 rbd_config_option_t *options
5754 int num_options
5755
5756 def __init__(self, Image image):
5757 image.require_not_closed()
5758
5759 self.options = NULL
5760 self.num_options = 32
5761 while True:
5762 self.options = <rbd_config_option_t *>realloc_chk(
5763 self.options, self.num_options * sizeof(rbd_config_option_t))
5764 with nogil:
5765 ret = rbd_config_image_list(image.image, self.options,
5766 &self.num_options)
5767 if ret < 0:
5768 if ret == -errno.ERANGE:
5769 continue
5770 self.num_options = 0
5771 raise make_ex(ret, 'error listing config options')
5772 break
5773
5774 def __iter__(self):
5775 for i in range(self.num_options):
5776 yield {
5777 'name' : decode_cstr(self.options[i].name),
5778 'value' : decode_cstr(self.options[i].value),
5779 'source' : self.options[i].source,
5780 }
5781
5782 def __dealloc__(self):
5783 if self.options:
5784 rbd_config_image_list_cleanup(self.options, self.num_options)
5785 free(self.options)
5786
5787 cdef class GroupImageIterator(object):
5788 """
5789 Iterator over image info for a group.
5790
5791 Yields a dictionary containing information about an image.
5792
5793 Keys are:
5794
5795 * ``name`` (str) - name of the image
5796
5797 * ``pool`` (int) - id of the pool this image belongs to
5798
5799 * ``state`` (int) - state of the image
5800 """
5801
5802 cdef rbd_group_image_info_t *images
5803 cdef size_t num_images
5804 cdef object group
5805
5806 def __init__(self, Group group):
5807 self.group = group
5808 self.images = NULL
5809 self.num_images = 10
5810 while True:
5811 self.images = <rbd_group_image_info_t*>realloc_chk(self.images,
5812 self.num_images *
5813 sizeof(rbd_group_image_info_t))
5814 with nogil:
5815 ret = rbd_group_image_list(group._ioctx, group._name,
5816 self.images,
5817 sizeof(rbd_group_image_info_t),
5818 &self.num_images)
5819
5820 if ret >= 0:
5821 break
5822 elif ret != -errno.ERANGE:
5823 raise make_ex(ret, 'error listing images for group %s' % group.name, group_errno_to_exception)
5824
5825 def __iter__(self):
5826 for i in range(self.num_images):
5827 yield {
5828 'name' : decode_cstr(self.images[i].name),
5829 'pool' : self.images[i].pool,
5830 'state' : self.images[i].state,
5831 }
5832
5833 def __dealloc__(self):
5834 if self.images:
5835 rbd_group_image_list_cleanup(self.images,
5836 sizeof(rbd_group_image_info_t),
5837 self.num_images)
5838 free(self.images)
5839
5840 cdef class GroupSnapIterator(object):
5841 """
5842 Iterator over snaps specs for a group.
5843
5844 Yields a dictionary containing information about a snapshot.
5845
5846 Keys are:
5847
5848 * ``name`` (str) - name of the snapshot
5849
5850 * ``state`` (int) - state of the snapshot
5851 """
5852
5853 cdef rbd_group_snap_info_t *snaps
5854 cdef size_t num_snaps
5855 cdef object group
5856
5857 def __init__(self, Group group):
5858 self.group = group
5859 self.snaps = NULL
5860 self.num_snaps = 10
5861 while True:
5862 self.snaps = <rbd_group_snap_info_t*>realloc_chk(self.snaps,
5863 self.num_snaps *
5864 sizeof(rbd_group_snap_info_t))
5865 with nogil:
5866 ret = rbd_group_snap_list(group._ioctx, group._name, self.snaps,
5867 sizeof(rbd_group_snap_info_t),
5868 &self.num_snaps)
5869
5870 if ret >= 0:
5871 break
5872 elif ret != -errno.ERANGE:
5873 raise make_ex(ret, 'error listing snapshots for group %s' % group.name, group_errno_to_exception)
5874
5875 def __iter__(self):
5876 for i in range(self.num_snaps):
5877 yield {
5878 'name' : decode_cstr(self.snaps[i].name),
5879 'state' : self.snaps[i].state,
5880 }
5881
5882 def __dealloc__(self):
5883 if self.snaps:
5884 rbd_group_snap_list_cleanup(self.snaps,
5885 sizeof(rbd_group_snap_info_t),
5886 self.num_snaps)
5887 free(self.snaps)