]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/rbd/rbd.pyx
update sources to 12.2.7
[ceph.git] / ceph / src / pybind / rbd / rbd.pyx
CommitLineData
7c673cae
FG
1# cython: embedsignature=True
2"""
3This module is a thin wrapper around librbd.
4
5It currently provides all the synchronous methods of librbd that do
6not use callbacks.
7
8Error 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`
11and :class:`IOError`, in addition to those documented for the
12method.
13"""
14# Copyright 2011 Josh Durgin
15# Copyright 2015 Hector Martin <marcan@marcan.st>
16
17import cython
18import sys
19
20from cpython cimport PyObject, ref, exc
21from libc cimport errno
22from libc.stdint cimport *
23from libc.stdlib cimport realloc, free
24from libc.string cimport strdup
25
26from collections import Iterable
27from datetime import datetime
28
29cimport rados
30
31
32cdef extern from "Python.h":
33 # These are in cpython/string.pxd, but use "object" types instead of
34 # PyObject*, which invokes assumptions in cpython that we need to
35 # legitimately break to implement zero-copy string buffers in Image.read().
36 # This is valid use of the Python API and documented as a special case.
37 PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL
38 char* PyBytes_AsString(PyObject *string) except NULL
39 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
40
41cdef extern from "time.h":
42 ctypedef long int time_t
43 cdef struct timespec:
44 time_t tv_sec
45 long tv_nsec
46
47cdef extern from "limits.h":
48 cdef uint64_t INT64_MAX
49
50cdef extern from "rbd/librbd.h" nogil:
51 enum:
52 _RBD_FEATURE_LAYERING "RBD_FEATURE_LAYERING"
53 _RBD_FEATURE_STRIPINGV2 "RBD_FEATURE_STRIPINGV2"
54 _RBD_FEATURE_EXCLUSIVE_LOCK "RBD_FEATURE_EXCLUSIVE_LOCK"
55 _RBD_FEATURE_OBJECT_MAP "RBD_FEATURE_OBJECT_MAP"
56 _RBD_FEATURE_FAST_DIFF "RBD_FEATURE_FAST_DIFF"
57 _RBD_FEATURE_DEEP_FLATTEN "RBD_FEATURE_DEEP_FLATTEN"
58 _RBD_FEATURE_JOURNALING "RBD_FEATURE_JOURNALING"
59 _RBD_FEATURE_DATA_POOL "RBD_FEATURE_DATA_POOL"
60
61 _RBD_FEATURES_INCOMPATIBLE "RBD_FEATURES_INCOMPATIBLE"
62 _RBD_FEATURES_RW_INCOMPATIBLE "RBD_FEATURES_RW_INCOMPATIBLE"
63 _RBD_FEATURES_MUTABLE "RBD_FEATURES_MUTABLE"
64 _RBD_FEATURES_SINGLE_CLIENT "RBD_FEATURES_SINGLE_CLIENT"
65 _RBD_FEATURES_ALL "RBD_FEATURES_ALL"
66
67 _RBD_FLAG_OBJECT_MAP_INVALID "RBD_FLAG_OBJECT_MAP_INVALID"
68 _RBD_FLAG_FAST_DIFF_INVALID "RBD_FLAG_FAST_DIFF_INVALID"
69
70 _RBD_IMAGE_OPTION_FORMAT "RBD_IMAGE_OPTION_FORMAT"
71 _RBD_IMAGE_OPTION_FEATURES "RBD_IMAGE_OPTION_FEATURES"
72 _RBD_IMAGE_OPTION_ORDER "RBD_IMAGE_OPTION_ORDER"
73 _RBD_IMAGE_OPTION_STRIPE_UNIT "RBD_IMAGE_OPTION_STRIPE_UNIT"
74 _RBD_IMAGE_OPTION_STRIPE_COUNT "RBD_IMAGE_OPTION_STRIPE_COUNT"
75 _RBD_IMAGE_OPTION_DATA_POOL "RBD_IMAGE_OPTION_DATA_POOL"
76
77 RBD_MAX_BLOCK_NAME_SIZE
78 RBD_MAX_IMAGE_NAME_SIZE
79
80 ctypedef void* rados_ioctx_t
81 ctypedef void* rbd_image_t
82 ctypedef void* rbd_image_options_t
83 ctypedef void *rbd_completion_t
84
85 ctypedef struct rbd_image_info_t:
86 uint64_t size
87 uint64_t obj_size
88 uint64_t num_objs
89 int order
90 char block_name_prefix[RBD_MAX_BLOCK_NAME_SIZE]
91 uint64_t parent_pool
92 char parent_name[RBD_MAX_IMAGE_NAME_SIZE]
93
94 ctypedef struct rbd_snap_info_t:
95 uint64_t id
96 uint64_t size
97 char *name
98
99 ctypedef enum rbd_mirror_mode_t:
100 _RBD_MIRROR_MODE_DISABLED "RBD_MIRROR_MODE_DISABLED"
101 _RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE"
102 _RBD_MIRROR_MODE_POOL "RBD_MIRROR_MODE_POOL"
103
104 ctypedef struct rbd_mirror_peer_t:
105 char *uuid
106 char *cluster_name
107 char *client_name
108
109 ctypedef enum rbd_mirror_image_state_t:
110 _RBD_MIRROR_IMAGE_DISABLING "RBD_MIRROR_IMAGE_DISABLING"
111 _RBD_MIRROR_IMAGE_ENABLED "RBD_MIRROR_IMAGE_ENABLED"
112 _RBD_MIRROR_IMAGE_DISABLED "RBD_MIRROR_IMAGE_DISABLED"
113
114 ctypedef struct rbd_mirror_image_info_t:
115 char *global_id
116 rbd_mirror_image_state_t state
117 bint primary
118
119 ctypedef enum rbd_mirror_image_status_state_t:
120 _MIRROR_IMAGE_STATUS_STATE_UNKNOWN "MIRROR_IMAGE_STATUS_STATE_UNKNOWN"
121 _MIRROR_IMAGE_STATUS_STATE_ERROR "MIRROR_IMAGE_STATUS_STATE_ERROR"
122 _MIRROR_IMAGE_STATUS_STATE_SYNCING "MIRROR_IMAGE_STATUS_STATE_SYNCING"
123 _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY"
124 _MIRROR_IMAGE_STATUS_STATE_REPLAYING "MIRROR_IMAGE_STATUS_STATE_REPLAYING"
125 _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY"
126 _MIRROR_IMAGE_STATUS_STATE_STOPPED "MIRROR_IMAGE_STATUS_STATE_STOPPED"
127
128 ctypedef struct rbd_mirror_image_status_t:
129 char *name
130 rbd_mirror_image_info_t info
131 rbd_mirror_image_status_state_t state
132 char *description
133 time_t last_update
134 bint up
135
136 ctypedef enum rbd_lock_mode_t:
137 _RBD_LOCK_MODE_EXCLUSIVE "RBD_LOCK_MODE_EXCLUSIVE"
138 _RBD_LOCK_MODE_SHARED "RBD_LOCK_MODE_SHARED"
139
140 ctypedef enum rbd_trash_image_source_t:
141 _RBD_TRASH_IMAGE_SOURCE_USER "RBD_TRASH_IMAGE_SOURCE_USER",
142 _RBD_TRASH_IMAGE_SOURCE_MIRRORING "RBD_TRASH_IMAGE_SOURCE_MIRRORING"
143
144 ctypedef struct rbd_trash_image_info_t:
145 char *id
146 char *name
147 rbd_trash_image_source_t source
148 time_t deletion_time
149 time_t deferment_end_time
150
151 ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg)
152 ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr)
153
154 void rbd_version(int *major, int *minor, int *extra)
155
156 void rbd_image_options_create(rbd_image_options_t* opts)
157 void rbd_image_options_destroy(rbd_image_options_t opts)
158 int rbd_image_options_set_string(rbd_image_options_t opts, int optname,
159 const char* optval)
160 int rbd_image_options_set_uint64(rbd_image_options_t opts, int optname,
161 uint64_t optval)
162 int rbd_image_options_get_string(rbd_image_options_t opts, int optname,
163 char* optval, size_t maxlen)
164 int rbd_image_options_get_uint64(rbd_image_options_t opts, int optname,
165 uint64_t* optval)
166 int rbd_image_options_unset(rbd_image_options_t opts, int optname)
167 void rbd_image_options_clear(rbd_image_options_t opts)
168 int rbd_image_options_is_empty(rbd_image_options_t opts)
169
170 int rbd_list(rados_ioctx_t io, char *names, size_t *size)
171 int rbd_create(rados_ioctx_t io, const char *name, uint64_t size,
172 int *order)
173 int rbd_create4(rados_ioctx_t io, const char *name, uint64_t size,
174 rbd_image_options_t opts)
175 int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
176 const char *p_snapname, rados_ioctx_t c_ioctx,
177 const char *c_name, rbd_image_options_t c_opts)
178 int rbd_remove(rados_ioctx_t io, const char *name)
179 int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname,
180 const char *destname)
181
182 int rbd_trash_move(rados_ioctx_t io, const char *name, uint64_t delay)
183 int rbd_trash_get(rados_ioctx_t io, const char *id,
184 rbd_trash_image_info_t *info)
185 void rbd_trash_get_cleanup(rbd_trash_image_info_t *info)
186 int rbd_trash_list(rados_ioctx_t io, rbd_trash_image_info_t *trash_entries,
187 size_t *num_entries)
188 void rbd_trash_list_cleanup(rbd_trash_image_info_t *trash_entries,
189 size_t num_entries)
190 int rbd_trash_remove(rados_ioctx_t io, const char *id, int force)
191 int rbd_trash_restore(rados_ioctx_t io, const char *id, const char *name)
192
193 int rbd_mirror_mode_get(rados_ioctx_t io, rbd_mirror_mode_t *mirror_mode)
194 int rbd_mirror_mode_set(rados_ioctx_t io, rbd_mirror_mode_t mirror_mode)
195 int rbd_mirror_peer_add(rados_ioctx_t io, char *uuid,
196 size_t uuid_max_length, const char *cluster_name,
197 const char *client_name)
198 int rbd_mirror_peer_remove(rados_ioctx_t io, const char *uuid)
199 int rbd_mirror_peer_list(rados_ioctx_t io_ctx, rbd_mirror_peer_t *peers,
200 int *max_peers)
201 void rbd_mirror_peer_list_cleanup(rbd_mirror_peer_t *peers, int max_peers)
202 int rbd_mirror_peer_set_client(rados_ioctx_t io, const char *uuid,
203 const char *client_name)
204 int rbd_mirror_peer_set_cluster(rados_ioctx_t io_ctx, const char *uuid,
205 const char *cluster_name)
206 int rbd_mirror_image_status_list(rados_ioctx_t io, const char *start_id,
207 size_t max, char **image_ids,
208 rbd_mirror_image_status_t *images,
209 size_t *len)
210 void rbd_mirror_image_status_list_cleanup(char **image_ids,
211 rbd_mirror_image_status_t *images,
212 size_t len)
213 int rbd_mirror_image_status_summary(rados_ioctx_t io,
214 rbd_mirror_image_status_state_t *states,
215 int *counts, size_t *maxlen)
216
217 int rbd_open(rados_ioctx_t io, const char *name,
218 rbd_image_t *image, const char *snap_name)
219 int rbd_open_read_only(rados_ioctx_t io, const char *name,
220 rbd_image_t *image, const char *snap_name)
221 int rbd_close(rbd_image_t image)
222 int rbd_resize(rbd_image_t image, uint64_t size)
223 int rbd_stat(rbd_image_t image, rbd_image_info_t *info, size_t infosize)
224 int rbd_get_old_format(rbd_image_t image, uint8_t *old)
225 int rbd_get_size(rbd_image_t image, uint64_t *size)
226 int rbd_get_features(rbd_image_t image, uint64_t *features)
227 int rbd_update_features(rbd_image_t image, uint64_t features,
228 uint8_t enabled)
229 int rbd_get_stripe_unit(rbd_image_t image, uint64_t *stripe_unit)
230 int rbd_get_stripe_count(rbd_image_t image, uint64_t *stripe_count)
31f18b77 231 int rbd_get_create_timestamp(rbd_image_t image, timespec *timestamp)
7c673cae
FG
232 int rbd_get_overlap(rbd_image_t image, uint64_t *overlap)
233 int rbd_get_id(rbd_image_t image, char *id, size_t id_len)
234 int rbd_get_block_name_prefix(rbd_image_t image, char *prefix,
235 size_t prefix_len)
28e407b8 236 int64_t rbd_get_data_pool_id(rbd_image_t image)
7c673cae
FG
237 int rbd_get_parent_info2(rbd_image_t image,
238 char *parent_poolname, size_t ppoolnamelen,
239 char *parent_name, size_t pnamelen,
240 char *parent_id, size_t pidlen,
241 char *parent_snapname, size_t psnapnamelen)
242 int rbd_get_flags(rbd_image_t image, uint64_t *flags)
243 ssize_t rbd_read2(rbd_image_t image, uint64_t ofs, size_t len,
244 char *buf, int op_flags)
245 ssize_t rbd_write2(rbd_image_t image, uint64_t ofs, size_t len,
246 const char *buf, int op_flags)
247 int rbd_discard(rbd_image_t image, uint64_t ofs, uint64_t len)
248 int rbd_copy3(rbd_image_t src, rados_ioctx_t dest_io_ctx,
249 const char *destname, rbd_image_options_t dest_opts)
250 int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps,
251 int *max_snaps)
252 void rbd_snap_list_end(rbd_snap_info_t *snaps)
253 int rbd_snap_create(rbd_image_t image, const char *snapname)
254 int rbd_snap_remove(rbd_image_t image, const char *snapname)
255 int rbd_snap_remove2(rbd_image_t image, const char *snapname, uint32_t flags,
256 librbd_progress_fn_t cb, void *cbdata)
257 int rbd_snap_rollback(rbd_image_t image, const char *snapname)
258 int rbd_snap_rename(rbd_image_t image, const char *snapname,
259 const char* dstsnapsname)
260 int rbd_snap_protect(rbd_image_t image, const char *snap_name)
261 int rbd_snap_unprotect(rbd_image_t image, const char *snap_name)
262 int rbd_snap_is_protected(rbd_image_t image, const char *snap_name,
263 int *is_protected)
264 int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit)
265 int rbd_snap_set_limit(rbd_image_t image, uint64_t limit)
266 int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, timespec *timestamp)
267 int rbd_snap_set(rbd_image_t image, const char *snapname)
268 int rbd_flatten(rbd_image_t image)
269 int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb,
270 void *cbdata)
271 ssize_t rbd_list_children(rbd_image_t image, char *pools, size_t *pools_len,
272 char *images, size_t *images_len)
273 ssize_t rbd_list_lockers(rbd_image_t image, int *exclusive,
274 char *tag, size_t *tag_len,
275 char *clients, size_t *clients_len,
276 char *cookies, size_t *cookies_len,
277 char *addrs, size_t *addrs_len)
278 int rbd_lock_exclusive(rbd_image_t image, const char *cookie)
279 int rbd_lock_shared(rbd_image_t image, const char *cookie,
280 const char *tag)
281 int rbd_unlock(rbd_image_t image, const char *cookie)
282 int rbd_break_lock(rbd_image_t image, const char *client,
283 const char *cookie)
284
285 int rbd_is_exclusive_lock_owner(rbd_image_t image, int *is_owner)
286 int rbd_lock_acquire(rbd_image_t image, rbd_lock_mode_t lock_mode)
287 int rbd_lock_release(rbd_image_t image)
288 int rbd_lock_get_owners(rbd_image_t image, rbd_lock_mode_t *lock_mode,
289 char **lock_owners, size_t *max_lock_owners)
290 void rbd_lock_get_owners_cleanup(char **lock_owners,
291 size_t lock_owner_count)
292 int rbd_lock_break(rbd_image_t image, rbd_lock_mode_t lock_mode,
293 char *lock_owner)
294
295 # We use -9000 to propagate Python exceptions. We use except? to make sure
296 # things still work as intended if -9000 happens to be a valid errno value
297 # somewhere.
298 int rbd_diff_iterate2(rbd_image_t image, const char *fromsnapname,
299 uint64_t ofs, uint64_t len,
300 uint8_t include_parent, uint8_t whole_object,
301 int (*cb)(uint64_t, size_t, int, void *)
302 nogil except? -9000,
303 void *arg) except? -9000
304
305 int rbd_flush(rbd_image_t image)
306 int rbd_invalidate_cache(rbd_image_t image)
307
308 int rbd_mirror_image_enable(rbd_image_t image)
309 int rbd_mirror_image_disable(rbd_image_t image, bint force)
310 int rbd_mirror_image_promote(rbd_image_t image, bint force)
311 int rbd_mirror_image_demote(rbd_image_t image)
312 int rbd_mirror_image_resync(rbd_image_t image)
313 int rbd_mirror_image_get_info(rbd_image_t image,
314 rbd_mirror_image_info_t *mirror_image_info,
315 size_t info_size)
316 int rbd_mirror_image_get_status(rbd_image_t image,
317 rbd_mirror_image_status_t *mirror_image_status,
318 size_t status_size)
319
320 int rbd_aio_write2(rbd_image_t image, uint64_t off, size_t len,
321 const char *buf, rbd_completion_t c, int op_flags)
322 int rbd_aio_read2(rbd_image_t image, uint64_t off, size_t len,
323 char *buf, rbd_completion_t c, int op_flags)
324 int rbd_aio_discard(rbd_image_t image, uint64_t off, uint64_t len,
325 rbd_completion_t c)
326
327 int rbd_aio_create_completion(void *cb_arg, rbd_callback_t complete_cb,
328 rbd_completion_t *c)
329 int rbd_aio_is_complete(rbd_completion_t c)
330 int rbd_aio_wait_for_complete(rbd_completion_t c)
331 ssize_t rbd_aio_get_return_value(rbd_completion_t c)
332 void rbd_aio_release(rbd_completion_t c)
333 int rbd_aio_flush(rbd_image_t image, rbd_completion_t c)
334
335 int rbd_metadata_get(rbd_image_t image, const char *key, char *value,
336 size_t *val_len)
337 int rbd_metadata_set(rbd_image_t image, const char *key, const char *value)
338 int rbd_metadata_remove(rbd_image_t image, const char *key)
339 int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
340 char *keys, size_t *key_len, char *values,
341 size_t *vals_len)
342
343RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING
344RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2
345RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK
346RBD_FEATURE_OBJECT_MAP = _RBD_FEATURE_OBJECT_MAP
347RBD_FEATURE_FAST_DIFF = _RBD_FEATURE_FAST_DIFF
348RBD_FEATURE_DEEP_FLATTEN = _RBD_FEATURE_DEEP_FLATTEN
349RBD_FEATURE_JOURNALING = _RBD_FEATURE_JOURNALING
350RBD_FEATURE_DATA_POOL = _RBD_FEATURE_DATA_POOL
351
352RBD_FEATURES_INCOMPATIBLE = _RBD_FEATURES_INCOMPATIBLE
353RBD_FEATURES_RW_INCOMPATIBLE = _RBD_FEATURES_RW_INCOMPATIBLE
354RBD_FEATURES_MUTABLE = _RBD_FEATURES_MUTABLE
355RBD_FEATURES_SINGLE_CLIENT = _RBD_FEATURES_SINGLE_CLIENT
356RBD_FEATURES_ALL = _RBD_FEATURES_ALL
357
358RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID
359
360RBD_MIRROR_MODE_DISABLED = _RBD_MIRROR_MODE_DISABLED
361RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE
362RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL
363
364RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING
365RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED
366RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED
367
368MIRROR_IMAGE_STATUS_STATE_UNKNOWN = _MIRROR_IMAGE_STATUS_STATE_UNKNOWN
369MIRROR_IMAGE_STATUS_STATE_ERROR = _MIRROR_IMAGE_STATUS_STATE_ERROR
370MIRROR_IMAGE_STATUS_STATE_SYNCING = _MIRROR_IMAGE_STATUS_STATE_SYNCING
371MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
372MIRROR_IMAGE_STATUS_STATE_REPLAYING = _MIRROR_IMAGE_STATUS_STATE_REPLAYING
373MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
374MIRROR_IMAGE_STATUS_STATE_STOPPED = _MIRROR_IMAGE_STATUS_STATE_STOPPED
375
376RBD_LOCK_MODE_EXCLUSIVE = _RBD_LOCK_MODE_EXCLUSIVE
377RBD_LOCK_MODE_SHARED = _RBD_LOCK_MODE_SHARED
378
379RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT
380RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES
381RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER
382RBD_IMAGE_OPTION_STRIPE_UNIT = _RBD_IMAGE_OPTION_STRIPE_UNIT
383RBD_IMAGE_OPTION_STRIPE_COUNT = _RBD_IMAGE_OPTION_STRIPE_COUNT
384RBD_IMAGE_OPTION_DATA_POOL = _RBD_IMAGE_OPTION_DATA_POOL
385
386class Error(Exception):
387 pass
388
389
390class OSError(Error):
391 """ `OSError` class, derived from `Error` """
224ce89b
WB
392 def __init__(self, message, errno=None):
393 super(OSError, self).__init__(message)
7c673cae 394 self.errno = errno
7c673cae
FG
395
396 def __str__(self):
224ce89b
WB
397 msg = super(OSError, self).__str__()
398 if self.errno is None:
399 return msg
400 return '[errno {0}] {1}'.format(self.errno, msg)
7c673cae 401
31f18b77 402 def __reduce__(self):
224ce89b 403 return (self.__class__, (self.message, self.errno))
7c673cae
FG
404
405class PermissionError(OSError):
406 pass
407
408
409class ImageNotFound(OSError):
410 pass
411
412
413class ImageExists(OSError):
414 pass
415
416
417class IOError(OSError):
418 pass
419
420
421class NoSpace(OSError):
422 pass
423
424
425class IncompleteWriteError(OSError):
426 pass
427
428
429class InvalidArgument(OSError):
430 pass
431
432
433class LogicError(Error):
434 pass
435
436
437class ReadOnlyImage(OSError):
438 pass
439
440
441class ImageBusy(OSError):
442 pass
443
444
445class ImageHasSnapshots(OSError):
446 pass
447
448
449class FunctionNotSupported(OSError):
450 pass
451
452
453class ArgumentOutOfRange(OSError):
454 pass
455
456
457class ConnectionShutdown(OSError):
458 pass
459
460
461class Timeout(OSError):
462 pass
463
464class DiskQuotaExceeded(OSError):
465 pass
466
467
468cdef errno_to_exception = {
469 errno.EPERM : PermissionError,
470 errno.ENOENT : ImageNotFound,
471 errno.EIO : IOError,
472 errno.ENOSPC : NoSpace,
473 errno.EEXIST : ImageExists,
474 errno.EINVAL : InvalidArgument,
475 errno.EROFS : ReadOnlyImage,
476 errno.EBUSY : ImageBusy,
477 errno.ENOTEMPTY : ImageHasSnapshots,
478 errno.ENOSYS : FunctionNotSupported,
479 errno.EDOM : ArgumentOutOfRange,
480 errno.ESHUTDOWN : ConnectionShutdown,
481 errno.ETIMEDOUT : Timeout,
482 errno.EDQUOT : DiskQuotaExceeded,
483}
484
485cdef make_ex(ret, msg):
486 """
487 Translate a librbd return code into an exception.
488
489 :param ret: the return code
490 :type ret: int
491 :param msg: the error message to use
492 :type msg: str
493 :returns: a subclass of :class:`Error`
494 """
495 ret = abs(ret)
496 if ret in errno_to_exception:
224ce89b 497 return errno_to_exception[ret](msg, errno=ret)
7c673cae 498 else:
224ce89b 499 return OSError(msg, errno=ret)
7c673cae
FG
500
501
502cdef rados_ioctx_t convert_ioctx(rados.Ioctx ioctx) except? NULL:
503 return <rados_ioctx_t>ioctx.io
504
505cdef int no_op_progress_callback(uint64_t offset, uint64_t total, void* ptr) nogil:
506 return 0
507
508def cstr(val, name, encoding="utf-8", opt=False):
509 """
510 Create a byte string from a Python string
511
512 :param basestring val: Python string
513 :param str name: Name of the string parameter, for exceptions
514 :param str encoding: Encoding to use
515 :param bool opt: If True, None is allowed
516 :rtype: bytes
517 :raises: :class:`InvalidArgument`
518 """
519 if opt and val is None:
520 return None
521 if isinstance(val, bytes):
522 return val
28e407b8
AA
523 elif isinstance(val, str):
524 return val.encode(encoding)
525 elif sys.version_info < (3, 0) and isinstance(val, unicode):
7c673cae
FG
526 return val.encode(encoding)
527 else:
528 raise InvalidArgument('%s must be a string' % name)
529
530def decode_cstr(val, encoding="utf-8"):
531 """
532 Decode a byte string into a Python string.
533
534 :param bytes val: byte string
535 :rtype: unicode or None
536 """
537 if val is None:
538 return None
539
540 return val.decode(encoding)
541
542
543cdef char* opt_str(s) except? NULL:
544 if s is None:
545 return NULL
546 return s
547
548cdef void* realloc_chk(void* ptr, size_t size) except NULL:
549 cdef void *ret = realloc(ptr, size)
550 if ret == NULL:
551 raise MemoryError("realloc failed")
552 return ret
553
554cdef class Completion
555
556cdef void __aio_complete_cb(rbd_completion_t completion, void *args) with gil:
557 """
558 Callback to oncomplete() for asynchronous operations
559 """
560 cdef Completion cb = <Completion>args
561 cb._complete()
562
563
564cdef class Completion(object):
565 """completion object"""
566
567 cdef:
568 object image
569 object oncomplete
570 rbd_completion_t rbd_comp
571 PyObject* buf
572 bint persisted
573 object exc_info
574
575 def __cinit__(self, image, object oncomplete):
576 self.oncomplete = oncomplete
577 self.image = image
578 self.persisted = False
579
580 def is_complete(self):
581 """
582 Has an asynchronous operation completed?
583
584 This does not imply that the callback has finished.
585
586 :returns: True if the operation is completed
587 """
588 with nogil:
589 ret = rbd_aio_is_complete(self.rbd_comp)
590 return ret == 1
591
592 def wait_for_complete_and_cb(self):
593 """
594 Wait for an asynchronous operation to complete
595
596 This method waits for the callback to execute, if one was provided.
597 It will also re-raise any exceptions raised by the callback. You
598 should call this to "reap" asynchronous completions and ensure that
599 any exceptions in the callbacks are handled, as an exception internal
600 to this module may have occurred.
601 """
602 with nogil:
603 rbd_aio_wait_for_complete(self.rbd_comp)
604
605 if self.exc_info:
606 raise self.exc_info[0], self.exc_info[1], self.exc_info[2]
607
608 def get_return_value(self):
609 """
610 Get the return value of an asychronous operation
611
612 The return value is set when the operation is complete.
613
614 :returns: int - return value of the operation
615 """
616 with nogil:
617 ret = rbd_aio_get_return_value(self.rbd_comp)
618 return ret
619
620 def __dealloc__(self):
621 """
622 Release a completion
623
624 This is automatically called when the completion object is freed.
625 """
626 ref.Py_XDECREF(self.buf)
627 self.buf = NULL
628 if self.rbd_comp != NULL:
629 with nogil:
630 rbd_aio_release(self.rbd_comp)
631 self.rbd_comp = NULL
632
633 cdef void _complete(self):
634 try:
635 self.__unpersist()
636 if self.oncomplete:
637 self.oncomplete(self)
638 # In the event that something raises an exception during the next 2
639 # lines of code, we will not be able to catch it, and this may result
640 # in the app not noticing a failed callback. However, this should only
641 # happen in extreme circumstances (OOM, etc.). KeyboardInterrupt
642 # should not be a problem because the callback thread from librbd
643 # ought to have SIGINT blocked.
644 except:
645 self.exc_info = sys.exc_info()
646
647 cdef __persist(self):
648 if self.oncomplete is not None and not self.persisted:
649 # Increment our own reference count to make sure the completion
650 # is not freed until the callback is called. The completion is
651 # allowed to be freed if there is no callback.
652 ref.Py_INCREF(self)
653 self.persisted = True
654
655 cdef __unpersist(self):
656 if self.persisted:
657 ref.Py_DECREF(self)
658 self.persisted = False
659
660
661class RBD(object):
662 """
663 This class wraps librbd CRUD functions.
664 """
665 def version(self):
666 """
667 Get the version number of the ``librbd`` C library.
668
669 :returns: a tuple of ``(major, minor, extra)`` components of the
670 librbd version
671 """
672 cdef int major = 0
673 cdef int minor = 0
674 cdef int extra = 0
675 rbd_version(&major, &minor, &extra)
676 return (major, minor, extra)
677
678 def create(self, ioctx, name, size, order=None, old_format=True,
679 features=None, stripe_unit=None, stripe_count=None,
680 data_pool=None):
681 """
682 Create an rbd image.
683
684 :param ioctx: the context in which to create the image
685 :type ioctx: :class:`rados.Ioctx`
686 :param name: what the image is called
687 :type name: str
688 :param size: how big the image is in bytes
689 :type size: int
690 :param order: the image is split into (2**order) byte objects
691 :type order: int
692 :param old_format: whether to create an old-style image that
693 is accessible by old clients, but can't
694 use more advanced features like layering.
695 :type old_format: bool
696 :param features: bitmask of features to enable
697 :type features: int
698 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
699 :type stripe_unit: int
700 :param stripe_count: objects to stripe over before looping
701 :type stripe_count: int
702 :param data_pool: optional separate pool for data blocks
703 :type data_pool: str
704 :raises: :class:`ImageExists`
705 :raises: :class:`TypeError`
706 :raises: :class:`InvalidArgument`
707 :raises: :class:`FunctionNotSupported`
708 """
709 name = cstr(name, 'name')
28e407b8 710 data_pool = cstr(data_pool, 'data_pool', opt=True)
7c673cae
FG
711 cdef:
712 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
713 char *_name = name
714 uint64_t _size = size
715 int _order = 0
716 rbd_image_options_t opts
717 if order is not None:
718 _order = order
719 if old_format:
720 if (features or
721 ((stripe_unit is not None) and stripe_unit != 0) or
722 ((stripe_count is not None) and stripe_count != 0) or
723 data_pool):
724 raise InvalidArgument('format 1 images do not support feature '
725 'masks, non-default striping, nor data '
726 'pool')
727 with nogil:
728 ret = rbd_create(_ioctx, _name, _size, &_order)
729 else:
730 rbd_image_options_create(&opts)
731 try:
732 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FORMAT,
733 1 if old_format else 2)
734 if features is not None:
735 rbd_image_options_set_uint64(opts,
736 RBD_IMAGE_OPTION_FEATURES,
737 features)
738 if order is not None:
739 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
740 _order)
741 if stripe_unit is not None:
742 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
743 stripe_unit)
744 if stripe_count is not None:
745 rbd_image_options_set_uint64(opts,
746 RBD_IMAGE_OPTION_STRIPE_COUNT,
747 stripe_count)
748 if data_pool is not None:
749 rbd_image_options_set_string(opts,
750 RBD_IMAGE_OPTION_DATA_POOL,
751 data_pool)
752 with nogil:
753 ret = rbd_create4(_ioctx, _name, _size, opts)
754 finally:
755 rbd_image_options_destroy(opts)
756 if ret < 0:
757 raise make_ex(ret, 'error creating image')
758
759 def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name,
760 features=None, order=None, stripe_unit=None, stripe_count=None,
761 data_pool=None):
762 """
763 Clone a parent rbd snapshot into a COW sparse child.
764
765 :param p_ioctx: the parent context that represents the parent snap
766 :type ioctx: :class:`rados.Ioctx`
767 :param p_name: the parent image name
768 :type name: str
769 :param p_snapname: the parent image snapshot name
770 :type name: str
771 :param c_ioctx: the child context that represents the new clone
772 :type ioctx: :class:`rados.Ioctx`
773 :param c_name: the clone (child) name
774 :type name: str
775 :param features: bitmask of features to enable; if set, must include layering
776 :type features: int
777 :param order: the image is split into (2**order) byte objects
778 :type order: int
779 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
780 :type stripe_unit: int
781 :param stripe_count: objects to stripe over before looping
782 :type stripe_count: int
783 :param data_pool: optional separate pool for data blocks
784 :type data_pool: str
785 :raises: :class:`TypeError`
786 :raises: :class:`InvalidArgument`
787 :raises: :class:`ImageExists`
788 :raises: :class:`FunctionNotSupported`
789 :raises: :class:`ArgumentOutOfRange`
790 """
791 p_snapname = cstr(p_snapname, 'p_snapname')
792 p_name = cstr(p_name, 'p_name')
793 c_name = cstr(c_name, 'c_name')
28e407b8 794 data_pool = cstr(data_pool, 'data_pool', opt=True)
7c673cae
FG
795 cdef:
796 rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx)
797 rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx)
798 char *_p_name = p_name
799 char *_p_snapname = p_snapname
800 char *_c_name = c_name
801 rbd_image_options_t opts
802
803 rbd_image_options_create(&opts)
804 try:
805 if features is not None:
806 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
807 features)
808 if order is not None:
809 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
810 order)
811 if stripe_unit is not None:
812 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
813 stripe_unit)
814 if stripe_count is not None:
815 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
816 stripe_count)
817 if data_pool is not None:
818 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
819 data_pool)
820 with nogil:
821 ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname,
822 _c_ioctx, _c_name, opts)
823 finally:
824 rbd_image_options_destroy(opts)
825 if ret < 0:
826 raise make_ex(ret, 'error creating clone')
827
828 def list(self, ioctx):
829 """
830 List image names.
831
832 :param ioctx: determines which RADOS pool is read
833 :type ioctx: :class:`rados.Ioctx`
834 :returns: list -- a list of image names
835 """
836 cdef:
837 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
838 size_t size = 512
839 char *c_names = NULL
840 try:
841 while True:
842 c_names = <char *>realloc_chk(c_names, size)
843 with nogil:
844 ret = rbd_list(_ioctx, c_names, &size)
845 if ret >= 0:
846 break
847 elif ret != -errno.ERANGE:
848 raise make_ex(ret, 'error listing images')
849 return [decode_cstr(name) for name in c_names[:ret].split(b'\0')
850 if name]
851 finally:
852 free(c_names)
853
854 def remove(self, ioctx, name):
855 """
856 Delete an RBD image. This may take a long time, since it does
857 not return until every object that comprises the image has
858 been deleted. Note that all snapshots must be deleted before
859 the image can be removed. If there are snapshots left,
860 :class:`ImageHasSnapshots` is raised. If the image is still
861 open, or the watch from a crashed client has not expired,
862 :class:`ImageBusy` is raised.
863
864 :param ioctx: determines which RADOS pool the image is in
865 :type ioctx: :class:`rados.Ioctx`
866 :param name: the name of the image to remove
867 :type name: str
868 :raises: :class:`ImageNotFound`, :class:`ImageBusy`,
869 :class:`ImageHasSnapshots`
870 """
871 name = cstr(name, 'name')
872 cdef:
873 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
874 char *_name = name
875 with nogil:
876 ret = rbd_remove(_ioctx, _name)
877 if ret != 0:
878 raise make_ex(ret, 'error removing image')
879
880 def rename(self, ioctx, src, dest):
881 """
882 Rename an RBD image.
883
884 :param ioctx: determines which RADOS pool the image is in
885 :type ioctx: :class:`rados.Ioctx`
886 :param src: the current name of the image
887 :type src: str
888 :param dest: the new name of the image
889 :type dest: str
890 :raises: :class:`ImageNotFound`, :class:`ImageExists`
891 """
892 src = cstr(src, 'src')
893 dest = cstr(dest, 'dest')
894 cdef:
895 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
896 char *_src = src
897 char *_dest = dest
898 with nogil:
899 ret = rbd_rename(_ioctx, _src, _dest)
900 if ret != 0:
901 raise make_ex(ret, 'error renaming image')
902
903 def trash_move(self, ioctx, name, delay=0):
904 """
c07f9fc5 905 Move an RBD image to the trash.
7c673cae
FG
906 :param ioctx: determines which RADOS pool the image is in
907 :type ioctx: :class:`rados.Ioctx`
908 :param name: the name of the image to remove
909 :type name: str
910 :param delay: time delay in seconds before the image can be deleted
911 from trash
912 :type delay: int
913 :raises: :class:`ImageNotFound`
914 """
915 name = cstr(name, 'name')
916 cdef:
917 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
918 char *_name = name
919 uint64_t _delay = delay
920 with nogil:
921 ret = rbd_trash_move(_ioctx, _name, _delay)
922 if ret != 0:
923 raise make_ex(ret, 'error moving image to trash')
924
925 def trash_remove(self, ioctx, image_id, force=False):
926 """
c07f9fc5 927 Delete an RBD image from trash. If image deferment time has not
7c673cae
FG
928 expired :class:`PermissionError` is raised.
929 :param ioctx: determines which RADOS pool the image is in
930 :type ioctx: :class:`rados.Ioctx`
931 :param image_id: the id of the image to remove
932 :type image_id: str
933 :param force: force remove even if deferment time has not expired
934 :type force: bool
935 :raises: :class:`ImageNotFound`, :class:`PermissionError`
936 """
937 image_id = cstr(image_id, 'image_id')
938 cdef:
939 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
940 char *_image_id = image_id
941 int _force = force
942 with nogil:
943 ret = rbd_trash_remove(_ioctx, _image_id, _force)
944 if ret != 0:
945 raise make_ex(ret, 'error deleting image from trash')
946
947 def trash_get(self, ioctx, image_id):
948 """
949 Retrieve RBD image info from trash
950 :param ioctx: determines which RADOS pool the image is in
951 :type ioctx: :class:`rados.Ioctx`
952 :param image_id: the id of the image to restore
953 :type image_id: str
954 :returns: dict - contains the following keys:
955
956 * ``id`` (str) - image id
957
958 * ``name`` (str) - image name
959
960 * ``source`` (str) - source of deletion
961
962 * ``deletion_time`` (datetime) - time of deletion
963
964 * ``deferment_end_time`` (datetime) - time that an image is allowed
965 to be removed from trash
966
967 :raises: :class:`ImageNotFound`
968 """
969 image_id = cstr(image_id, 'image_id')
970 cdef:
971 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
972 char *_image_id = image_id
973 rbd_trash_image_info_t c_info
974 with nogil:
975 ret = rbd_trash_get(_ioctx, _image_id, &c_info)
976 if ret != 0:
977 raise make_ex(ret, 'error restoring image from trash')
978
979 __source_string = ['USER', 'MIRRORING']
980 info = {
981 'id' : decode_cstr(c_info.id),
982 'name' : decode_cstr(c_info.name),
983 'source' : __source_string[c_info.source],
28e407b8
AA
984 'deletion_time' : datetime.utcfromtimestamp(c_info.deletion_time),
985 'deferment_end_time' : datetime.utcfromtimestamp(c_info.deferment_end_time)
7c673cae
FG
986 }
987 rbd_trash_get_cleanup(&c_info)
988 return info
989
990 def trash_list(self, ioctx):
991 """
c07f9fc5 992 List all entries from trash.
7c673cae
FG
993 :param ioctx: determines which RADOS pool the image is in
994 :type ioctx: :class:`rados.Ioctx`
995 :returns: :class:`TrashIterator`
996 """
997 return TrashIterator(ioctx)
998
999 def trash_restore(self, ioctx, image_id, name):
1000 """
c07f9fc5 1001 Restore an RBD image from trash.
7c673cae
FG
1002 :param ioctx: determines which RADOS pool the image is in
1003 :type ioctx: :class:`rados.Ioctx`
1004 :param image_id: the id of the image to restore
1005 :type image_id: str
1006 :param name: the new name of the restored image
1007 :type name: str
1008 :raises: :class:`ImageNotFound`
1009 """
1010 image_id = cstr(image_id, 'image_id')
1011 name = cstr(name, 'name')
1012 cdef:
1013 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1014 char *_image_id = image_id
1015 char *_name = name
1016 with nogil:
1017 ret = rbd_trash_restore(_ioctx, _image_id, _name)
1018 if ret != 0:
1019 raise make_ex(ret, 'error restoring image from trash')
1020
1021 def mirror_mode_get(self, ioctx):
1022 """
1023 Get pool mirror mode.
1024
1025 :param ioctx: determines which RADOS pool is read
1026 :type ioctx: :class:`rados.Ioctx`
1027 :returns: int - pool mirror mode
1028 """
1029 cdef:
1030 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1031 rbd_mirror_mode_t mirror_mode
1032 with nogil:
1033 ret = rbd_mirror_mode_get(_ioctx, &mirror_mode)
1034 if ret != 0:
1035 raise make_ex(ret, 'error getting mirror mode')
1036 return mirror_mode
1037
1038 def mirror_mode_set(self, ioctx, mirror_mode):
1039 """
1040 Set pool mirror mode.
1041
1042 :param ioctx: determines which RADOS pool is written
1043 :type ioctx: :class:`rados.Ioctx`
1044 :param mirror_mode: mirror mode to set
1045 :type mirror_mode: int
1046 """
1047 cdef:
1048 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1049 rbd_mirror_mode_t _mirror_mode = mirror_mode
1050 with nogil:
1051 ret = rbd_mirror_mode_set(_ioctx, _mirror_mode)
1052 if ret != 0:
1053 raise make_ex(ret, 'error setting mirror mode')
1054
1055 def mirror_peer_add(self, ioctx, cluster_name, client_name):
1056 """
1057 Add mirror peer.
1058
1059 :param ioctx: determines which RADOS pool is used
1060 :type ioctx: :class:`rados.Ioctx`
1061 :param cluster_name: mirror peer cluster name
1062 :type cluster_name: str
1063 :param client_name: mirror peer client name
1064 :type client_name: str
1065 :returns: str - peer uuid
1066 """
1067 cluster_name = cstr(cluster_name, 'cluster_name')
1068 client_name = cstr(client_name, 'client_name')
1069 cdef:
1070 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1071 char *_uuid = NULL
1072 size_t _uuid_max_length = 512
1073 char *_cluster_name = cluster_name
1074 char *_client_name = client_name
1075 try:
1076 _uuid = <char *>realloc_chk(_uuid, _uuid_max_length)
1077 ret = rbd_mirror_peer_add(_ioctx, _uuid, _uuid_max_length,
1078 _cluster_name, _client_name)
1079 if ret != 0:
1080 raise make_ex(ret, 'error adding mirror peer')
1081 return decode_cstr(_uuid)
1082 finally:
1083 free(_uuid)
1084
1085 def mirror_peer_remove(self, ioctx, uuid):
1086 """
1087 Remove mirror peer.
1088
1089 :param ioctx: determines which RADOS pool is used
1090 :type ioctx: :class:`rados.Ioctx`
1091 :param uuid: peer uuid
1092 :type uuid: str
1093 """
1094 uuid = cstr(uuid, 'uuid')
1095 cdef:
1096 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1097 char *_uuid = uuid
1098 with nogil:
1099 ret = rbd_mirror_peer_remove(_ioctx, _uuid)
1100 if ret != 0:
1101 raise make_ex(ret, 'error removing mirror peer')
1102
1103 def mirror_peer_list(self, ioctx):
1104 """
1105 Iterate over the peers of a pool.
1106
1107 :param ioctx: determines which RADOS pool is read
1108 :type ioctx: :class:`rados.Ioctx`
1109 :returns: :class:`MirrorPeerIterator`
1110 """
1111 return MirrorPeerIterator(ioctx)
1112
1113 def mirror_peer_set_client(self, ioctx, uuid, client_name):
1114 """
1115 Set mirror peer client name
1116
1117 :param ioctx: determines which RADOS pool is written
1118 :type ioctx: :class:`rados.Ioctx`
1119 :param uuid: uuid of the mirror peer
1120 :type uuid: str
1121 :param client_name: client name of the mirror peer to set
1122 :type client_name: str
1123 """
1124 uuid = cstr(uuid, 'uuid')
1125 client_name = cstr(client_name, 'client_name')
1126 cdef:
1127 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1128 char *_uuid = uuid
1129 char *_client_name = client_name
1130 with nogil:
1131 ret = rbd_mirror_peer_set_client(_ioctx, _uuid, _client_name)
1132 if ret != 0:
1133 raise make_ex(ret, 'error setting mirror peer client')
1134
1135 def mirror_peer_set_cluster(self, ioctx, uuid, cluster_name):
1136 """
1137 Set mirror peer cluster name
1138
1139 :param ioctx: determines which RADOS pool is written
1140 :type ioctx: :class:`rados.Ioctx`
1141 :param uuid: uuid of the mirror peer
1142 :type uuid: str
1143 :param cluster_name: cluster name of the mirror peer to set
1144 :type cluster_name: str
1145 """
1146 uuid = cstr(uuid, 'uuid')
1147 cluster_name = cstr(cluster_name, 'cluster_name')
1148 cdef:
1149 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1150 char *_uuid = uuid
1151 char *_cluster_name = cluster_name
1152 with nogil:
1153 ret = rbd_mirror_peer_set_cluster(_ioctx, _uuid, _cluster_name)
1154 if ret != 0:
1155 raise make_ex(ret, 'error setting mirror peer cluster')
1156
1157 def mirror_image_status_list(self, ioctx):
1158 """
1159 Iterate over the mirror image statuses of a pool.
1160
1161 :param ioctx: determines which RADOS pool is read
1162 :type ioctx: :class:`rados.Ioctx`
1163 :returns: :class:`MirrorImageStatus`
1164 """
1165 return MirrorImageStatusIterator(ioctx)
1166
1167 def mirror_image_status_summary(self, ioctx):
1168 """
1169 Get mirror image status summary of a pool.
1170
1171 :param ioctx: determines which RADOS pool is read
1172 :type ioctx: :class:`rados.Ioctx`
1173 :returns: list - a list of (state, count) tuples
1174 """
1175 cdef:
1176 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1177 rbd_mirror_image_status_state_t *states = NULL
1178 int *counts = NULL
1179 size_t maxlen = 32
1180 try:
1181 states = <rbd_mirror_image_status_state_t *>realloc_chk(states,
1182 sizeof(rbd_mirror_image_status_state_t) * maxlen)
1183 counts = <int *>realloc_chk(counts, sizeof(int) * maxlen)
1184 with nogil:
1185 ret = rbd_mirror_image_status_summary(_ioctx, states, counts,
1186 &maxlen)
1187 if ret < 0:
1188 raise make_ex(ret, 'error getting mirror image status summary')
1189 return [(states[i], counts[i]) for i in range(maxlen)]
1190 finally:
1191 free(states)
1192 free(counts)
1193
1194cdef class MirrorPeerIterator(object):
1195 """
1196 Iterator over mirror peer info for a pool.
1197
1198 Yields a dictionary containing information about a peer.
1199
1200 Keys are:
1201
1202 * ``uuid`` (str) - uuid of the peer
1203
1204 * ``cluster_name`` (str) - cluster name of the peer
1205
1206 * ``client_name`` (str) - client name of the peer
1207 """
1208
1209 cdef:
1210 rbd_mirror_peer_t *peers
1211 int num_peers
1212
1213 def __init__(self, ioctx):
1214 cdef:
1215 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1216 self.peers = NULL
1217 self.num_peers = 10
1218 while True:
1219 self.peers = <rbd_mirror_peer_t *>realloc_chk(
1220 self.peers, self.num_peers * sizeof(rbd_mirror_peer_t))
1221 with nogil:
1222 ret = rbd_mirror_peer_list(_ioctx, self.peers, &self.num_peers)
1223 if ret < 0:
1224 if ret == -errno.ERANGE:
1225 continue
1226 self.num_peers = 0
1227 raise make_ex(ret, 'error listing peers')
1228 break
1229
1230 def __iter__(self):
1231 for i in range(self.num_peers):
1232 yield {
1233 'uuid' : decode_cstr(self.peers[i].uuid),
1234 'cluster_name' : decode_cstr(self.peers[i].cluster_name),
1235 'client_name' : decode_cstr(self.peers[i].client_name),
1236 }
1237
1238 def __dealloc__(self):
1239 if self.peers:
1240 rbd_mirror_peer_list_cleanup(self.peers, self.num_peers)
1241 free(self.peers)
1242
1243cdef class MirrorImageStatusIterator(object):
1244 """
1245 Iterator over mirror image status for a pool.
1246
1247 Yields a dictionary containing mirror status of an image.
1248
1249 Keys are:
1250
1251 * ``name`` (str) - mirror image name
1252
1253 * `info` (dict) - mirror image info
1254
1255 * `state` (int) - mirror state
1256
1257 * `description` (str) - status description
1258
1259 * `last_update` (datetime) - last status update time
1260
1261 * ``up`` (bool) - is mirroring agent up
1262 """
1263
1264 cdef:
1265 rados_ioctx_t ioctx
1266 size_t max_read
1267 char *last_read
1268 char **image_ids
1269 rbd_mirror_image_status_t *images
1270 size_t size
1271
1272 def __init__(self, ioctx):
1273 self.ioctx = convert_ioctx(ioctx)
1274 self.max_read = 1024
1275 self.last_read = strdup("")
1276 self.image_ids = <char **>realloc_chk(NULL,
1277 sizeof(char *) * self.max_read)
1278 self.images = <rbd_mirror_image_status_t *>realloc_chk(NULL,
1279 sizeof(rbd_mirror_image_status_t) * self.max_read)
1280 self.size = 0
1281 self.get_next_chunk()
1282
1283 def __iter__(self):
1284 while self.size > 0:
1285 for i in range(self.size):
1286 yield {
1287 'name' : decode_cstr(self.images[i].name),
1288 'info' : {
1289 'global_id' : decode_cstr(self.images[i].info.global_id),
1290 'state' : self.images[i].info.state,
1291 },
1292 'state' : self.images[i].state,
1293 'description' : decode_cstr(self.images[i].description),
28e407b8 1294 'last_update' : datetime.utcfromtimestamp(self.images[i].last_update),
7c673cae
FG
1295 'up' : self.images[i].up,
1296 }
1297 if self.size < self.max_read:
1298 break
1299 self.get_next_chunk()
1300
1301 def __dealloc__(self):
1302 rbd_mirror_image_status_list_cleanup(self.image_ids, self.images,
1303 self.size)
1304 if self.last_read:
1305 free(self.last_read)
1306 if self.image_ids:
1307 free(self.image_ids)
1308 if self.images:
1309 free(self.images)
1310
1311 def get_next_chunk(self):
1312 if self.size > 0:
1313 rbd_mirror_image_status_list_cleanup(self.image_ids, self.images,
1314 self.size)
1315 self.size = 0
1316 with nogil:
1317 ret = rbd_mirror_image_status_list(self.ioctx, self.last_read,
1318 self.max_read, self.image_ids,
1319 self.images, &self.size)
1320 if ret < 0:
1321 raise make_ex(ret, 'error listing mirror images status')
1322 if self.size > 0:
1323 free(self.last_read)
1324 last_read = decode_cstr(self.image_ids[self.size - 1])
1325 self.last_read = strdup(last_read)
1326 else:
1327 free(self.last_read)
1328 self.last_read = strdup("")
1329
1330cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \
1331 except? -9000 with gil:
1332 # Make sure that if we wound up with an exception from a previous callback,
1333 # we stop calling back (just in case librbd ever fails to bail out on the
1334 # first negative return, as older versions did)
1335 if exc.PyErr_Occurred():
1336 return -9000
1337 ret = (<object>cb)(offset, length, bool(write))
1338 if ret is None:
1339 return 0
1340 return ret
1341
1342
1343cdef class Image(object):
1344 """
1345 This class represents an RBD image. It is used to perform I/O on
1346 the image and interact with snapshots.
1347
1348 **Note**: Any method of this class may raise :class:`ImageNotFound`
1349 if the image has been deleted.
1350 """
1351 cdef rbd_image_t image
1352 cdef bint closed
1353 cdef object name
1354 cdef object ioctx
1355 cdef rados_ioctx_t _ioctx
1356
1357 def __init__(self, ioctx, name, snapshot=None, read_only=False):
1358 """
1359 Open the image at the given snapshot.
1360 If a snapshot is specified, the image will be read-only, unless
1361 :func:`Image.set_snap` is called later.
1362
1363 If read-only mode is used, metadata for the :class:`Image`
1364 object (such as which snapshots exist) may become obsolete. See
1365 the C api for more details.
1366
1367 To clean up from opening the image, :func:`Image.close` should
1368 be called. For ease of use, this is done automatically when
1369 an :class:`Image` is used as a context manager (see :pep:`343`).
1370
1371 :param ioctx: determines which RADOS pool the image is in
1372 :type ioctx: :class:`rados.Ioctx`
1373 :param name: the name of the image
1374 :type name: str
1375 :param snapshot: which snapshot to read from
1376 :type snaphshot: str
1377 :param read_only: whether to open the image in read-only mode
1378 :type read_only: bool
1379 """
1380 name = cstr(name, 'name')
1381 snapshot = cstr(snapshot, 'snapshot', opt=True)
1382 self.closed = True
1383 self.name = name
1384 # Keep around a reference to the ioctx, so it won't get deleted
1385 self.ioctx = ioctx
1386 cdef:
1387 rados_ioctx_t _ioctx = convert_ioctx(ioctx)
1388 char *_name = name
1389 char *_snapshot = opt_str(snapshot)
1390 if read_only:
1391 with nogil:
1392 ret = rbd_open_read_only(_ioctx, _name, &self.image, _snapshot)
1393 else:
1394 with nogil:
1395 ret = rbd_open(_ioctx, _name, &self.image, _snapshot)
1396 if ret != 0:
1397 raise make_ex(ret, 'error opening image %s at snapshot %s' % (name, snapshot))
1398 self.closed = False
1399
1400 def __enter__(self):
1401 return self
1402
1403 def __exit__(self, type_, value, traceback):
1404 """
1405 Closes the image. See :func:`close`
1406 """
1407 self.close()
1408 return False
1409
1410 def __get_completion(self, oncomplete):
1411 """
1412 Constructs a completion to use with asynchronous operations
1413
1414 :param oncomplete: callback for the completion
1415
1416 :raises: :class:`Error`
1417 :returns: completion object
1418 """
1419
1420 completion_obj = Completion(self, oncomplete)
1421
1422 cdef:
1423 rbd_completion_t completion
1424 PyObject* p_completion_obj= <PyObject*>completion_obj
1425
1426 with nogil:
1427 ret = rbd_aio_create_completion(p_completion_obj, __aio_complete_cb,
1428 &completion)
1429 if ret < 0:
1430 raise make_ex(ret, "error getting a completion")
1431
1432 completion_obj.rbd_comp = completion
1433 return completion_obj
1434
1435 def close(self):
1436 """
1437 Release the resources used by this image object.
1438
1439 After this is called, this object should not be used.
1440 """
1441 if not self.closed:
1442 self.closed = True
1443 with nogil:
1444 ret = rbd_close(self.image)
1445 if ret < 0:
1446 raise make_ex(ret, 'error while closing image %s' % (
1447 self.name,))
1448
1449 def __dealloc__(self):
1450 self.close()
1451
1452 def __repr__(self):
1453 return "rbd.Image(ioctx, %r)" % self.name
1454
1455 def resize(self, size):
1456 """
1457 Change the size of the image.
1458
1459 :param size: the new size of the image
1460 :type size: int
1461 """
1462 cdef uint64_t _size = size
1463 with nogil:
1464 ret = rbd_resize(self.image, _size)
1465 if ret < 0:
1466 raise make_ex(ret, 'error resizing image %s' % (self.name,))
1467
1468 def stat(self):
1469 """
1470 Get information about the image. Currently parent pool and
1471 parent name are always -1 and ''.
1472
1473 :returns: dict - contains the following keys:
1474
1475 * ``size`` (int) - the size of the image in bytes
1476
1477 * ``obj_size`` (int) - the size of each object that comprises the
1478 image
1479
1480 * ``num_objs`` (int) - the number of objects in the image
1481
1482 * ``order`` (int) - log_2(object_size)
1483
1484 * ``block_name_prefix`` (str) - the prefix of the RADOS objects used
1485 to store the image
1486
1487 * ``parent_pool`` (int) - deprecated
1488
1489 * ``parent_name`` (str) - deprecated
1490
1491 See also :meth:`format` and :meth:`features`.
1492
1493 """
1494 cdef rbd_image_info_t info
1495 with nogil:
1496 ret = rbd_stat(self.image, &info, sizeof(info))
1497 if ret != 0:
1498 raise make_ex(ret, 'error getting info for image %s' % (self.name,))
1499 return {
1500 'size' : info.size,
1501 'obj_size' : info.obj_size,
1502 'num_objs' : info.num_objs,
1503 'order' : info.order,
1504 'block_name_prefix' : decode_cstr(info.block_name_prefix),
1505 'parent_pool' : info.parent_pool,
1506 'parent_name' : info.parent_name
1507 }
1508
1509 def id(self):
1510 """
1511 Get the RBD v2 internal image id
1512
1513 :returns: str - image id
1514 """
1515 cdef:
1516 int ret = -errno.ERANGE
1517 size_t size = 32
1518 char *image_id = NULL
1519 try:
1520 while ret == -errno.ERANGE and size <= 4096:
1521 image_id = <char *>realloc_chk(image_id, size)
1522 with nogil:
1523 ret = rbd_get_id(self.image, image_id, size)
1524 if ret == -errno.ERANGE:
1525 size *= 2
1526
1527 if ret != 0:
1528 raise make_ex(ret, 'error getting id for image %s' % (self.name,))
1529 return decode_cstr(image_id)
1530 finally:
1531 free(image_id)
1532
1533 def block_name_prefix(self):
1534 """
1535 Get the RBD block name prefix
1536
1537 :returns: str - block name prefix
1538 """
1539 cdef:
1540 int ret = -errno.ERANGE
1541 size_t size = 32
1542 char *prefix = NULL
1543 try:
1544 while ret == -errno.ERANGE and size <= 4096:
1545 prefix = <char *>realloc_chk(prefix, size)
1546 with nogil:
1547 ret = rbd_get_block_name_prefix(self.image, prefix, size)
1548 if ret == -errno.ERANGE:
1549 size *= 2
1550
1551 if ret != 0:
1552 raise make_ex(ret, 'error getting block name prefix for image %s' % (self.name,))
1553 return decode_cstr(prefix)
1554 finally:
1555 free(prefix)
1556
28e407b8
AA
1557 def data_pool_id(self):
1558 """
1559 Get the pool id of the pool where the data of this RBD image is stored.
1560
1561 :returns: int - the pool id
1562 """
1563 return rbd_get_data_pool_id(self.image)
1564
7c673cae
FG
1565 def parent_info(self):
1566 """
1567 Get information about a cloned image's parent (if any)
1568
1569 :returns: tuple - ``(pool name, image name, snapshot name)`` components
1570 of the parent image
1571 :raises: :class:`ImageNotFound` if the image doesn't have a parent
1572 """
1573 cdef:
1574 int ret = -errno.ERANGE
1575 size_t size = 8
1576 char *pool = NULL
1577 char *name = NULL
1578 char *snapname = NULL
1579 try:
1580 while ret == -errno.ERANGE and size <= 4096:
1581 pool = <char *>realloc_chk(pool, size)
1582 name = <char *>realloc_chk(name, size)
1583 snapname = <char *>realloc_chk(snapname, size)
1584 with nogil:
1585 ret = rbd_get_parent_info2(self.image, pool, size, name,
1586 size, NULL, 0, snapname, size)
1587 if ret == -errno.ERANGE:
1588 size *= 2
1589
1590 if ret != 0:
1591 raise make_ex(ret, 'error getting parent info for image %s' % (self.name,))
1592 return (decode_cstr(pool), decode_cstr(name), decode_cstr(snapname))
1593 finally:
1594 free(pool)
1595 free(name)
1596 free(snapname)
1597
1598 def parent_id(self):
1599 """
1600 Get image id of a cloned image's parent (if any)
1601
1602 :returns: str - the parent id
1603 :raises: :class:`ImageNotFound` if the image doesn't have a parent
1604 """
1605 cdef:
1606 int ret = -errno.ERANGE
1607 size_t size = 32
1608 char *parent_id = NULL
1609 try:
1610 while ret == -errno.ERANGE and size <= 4096:
1611 parent_id = <char *>realloc_chk(parent_id, size)
1612 with nogil:
1613 ret = rbd_get_parent_info2(self.image, NULL, 0, NULL, 0,
1614 parent_id, size, NULL, 0)
1615 if ret == -errno.ERANGE:
1616 size *= 2
1617
1618 if ret != 0:
1619 raise make_ex(ret, 'error getting parent id for image %s' % (self.name,))
1620 return decode_cstr(parent_id)
1621 finally:
1622 free(parent_id)
1623
1624 def old_format(self):
1625 """
1626 Find out whether the image uses the old RBD format.
1627
1628 :returns: bool - whether the image uses the old RBD format
1629 """
1630 cdef uint8_t old
1631 with nogil:
1632 ret = rbd_get_old_format(self.image, &old)
1633 if ret != 0:
1634 raise make_ex(ret, 'error getting old_format for image %s' % (self.name))
1635 return old != 0
1636
1637 def size(self):
1638 """
1639 Get the size of the image. If open to a snapshot, returns the
1640 size of that snapshot.
1641
1642 :returns: the size of the image in bytes
1643 """
1644 cdef uint64_t image_size
1645 with nogil:
1646 ret = rbd_get_size(self.image, &image_size)
1647 if ret != 0:
1648 raise make_ex(ret, 'error getting size for image %s' % (self.name))
1649 return image_size
1650
1651 def features(self):
1652 """
c07f9fc5 1653 Get the features bitmask of the image.
7c673cae
FG
1654
1655 :returns: int - the features bitmask of the image
1656 """
1657 cdef uint64_t features
1658 with nogil:
1659 ret = rbd_get_features(self.image, &features)
1660 if ret != 0:
1661 raise make_ex(ret, 'error getting features for image %s' % (self.name))
1662 return features
1663
1664 def update_features(self, features, enabled):
1665 """
c07f9fc5 1666 Update the features bitmask of the image by enabling/disabling
7c673cae
FG
1667 a single feature. The feature must support the ability to be
1668 dynamically enabled/disabled.
1669
1670 :param features: feature bitmask to enable/disable
1671 :type features: int
1672 :param enabled: whether to enable/disable the feature
1673 :type enabled: bool
1674 :raises: :class:`InvalidArgument`
1675 """
1676 cdef:
1677 uint64_t _features = features
1678 uint8_t _enabled = bool(enabled)
1679 with nogil:
1680 ret = rbd_update_features(self.image, _features, _enabled)
1681 if ret != 0:
1682 raise make_ex(ret, 'error updating features for image %s' %
1683 (self.name))
1684
1685 def overlap(self):
1686 """
c07f9fc5 1687 Get the number of overlapping bytes between the image and its parent
7c673cae
FG
1688 image. If open to a snapshot, returns the overlap between the snapshot
1689 and the parent image.
1690
1691 :returns: int - the overlap in bytes
1692 :raises: :class:`ImageNotFound` if the image doesn't have a parent
1693 """
1694 cdef uint64_t overlap
1695 with nogil:
1696 ret = rbd_get_overlap(self.image, &overlap)
1697 if ret != 0:
1698 raise make_ex(ret, 'error getting overlap for image %s' % (self.name))
1699 return overlap
1700
1701 def flags(self):
1702 """
c07f9fc5 1703 Get the flags bitmask of the image.
7c673cae
FG
1704
1705 :returns: int - the flags bitmask of the image
1706 """
1707 cdef uint64_t flags
1708 with nogil:
1709 ret = rbd_get_flags(self.image, &flags)
1710 if ret != 0:
1711 raise make_ex(ret, 'error getting flags for image %s' % (self.name))
1712 return flags
1713
1714 def is_exclusive_lock_owner(self):
1715 """
c07f9fc5 1716 Get the status of the image exclusive lock.
7c673cae
FG
1717
1718 :returns: bool - true if the image is exclusively locked
1719 """
1720 cdef int owner
1721 with nogil:
1722 ret = rbd_is_exclusive_lock_owner(self.image, &owner)
1723 if ret != 0:
1724 raise make_ex(ret, 'error getting lock status for image %s' % (self.name))
1725 return owner == 1
1726
1727 def copy(self, dest_ioctx, dest_name, features=None, order=None,
1728 stripe_unit=None, stripe_count=None, data_pool=None):
1729 """
1730 Copy the image to another location.
1731
1732 :param dest_ioctx: determines which pool to copy into
1733 :type dest_ioctx: :class:`rados.Ioctx`
1734 :param dest_name: the name of the copy
1735 :type dest_name: str
1736 :param features: bitmask of features to enable; if set, must include layering
1737 :type features: int
1738 :param order: the image is split into (2**order) byte objects
1739 :type order: int
1740 :param stripe_unit: stripe unit in bytes (default None to let librbd decide)
1741 :type stripe_unit: int
1742 :param stripe_count: objects to stripe over before looping
1743 :type stripe_count: int
1744 :param data_pool: optional separate pool for data blocks
1745 :type data_pool: str
1746 :raises: :class:`TypeError`
1747 :raises: :class:`InvalidArgument`
1748 :raises: :class:`ImageExists`
1749 :raises: :class:`FunctionNotSupported`
1750 :raises: :class:`ArgumentOutOfRange`
1751 """
1752 dest_name = cstr(dest_name, 'dest_name')
28e407b8 1753 data_pool = cstr(data_pool, 'data_pool', opt=True)
7c673cae
FG
1754 cdef:
1755 rados_ioctx_t _dest_ioctx = convert_ioctx(dest_ioctx)
1756 char *_dest_name = dest_name
1757 rbd_image_options_t opts
1758
1759 rbd_image_options_create(&opts)
1760 try:
1761 if features is not None:
1762 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_FEATURES,
1763 features)
1764 if order is not None:
1765 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_ORDER,
1766 order)
1767 if stripe_unit is not None:
1768 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_UNIT,
1769 stripe_unit)
1770 if stripe_count is not None:
1771 rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_STRIPE_COUNT,
1772 stripe_count)
1773 if data_pool is not None:
1774 rbd_image_options_set_string(opts, RBD_IMAGE_OPTION_DATA_POOL,
1775 data_pool)
1776 with nogil:
1777 ret = rbd_copy3(self.image, _dest_ioctx, _dest_name, opts)
1778 finally:
1779 rbd_image_options_destroy(opts)
1780 if ret < 0:
1781 raise make_ex(ret, 'error copying image %s to %s' % (self.name, dest_name))
1782
1783 def list_snaps(self):
1784 """
1785 Iterate over the snapshots of an image.
1786
1787 :returns: :class:`SnapIterator`
1788 """
1789 return SnapIterator(self)
1790
1791 def create_snap(self, name):
1792 """
1793 Create a snapshot of the image.
1794
1795 :param name: the name of the snapshot
1796 :type name: str
1797 :raises: :class:`ImageExists`
1798 """
1799 name = cstr(name, 'name')
1800 cdef char *_name = name
1801 with nogil:
1802 ret = rbd_snap_create(self.image, _name)
1803 if ret != 0:
1804 raise make_ex(ret, 'error creating snapshot %s from %s' % (name, self.name))
1805
1806 def rename_snap(self, srcname, dstname):
1807 """
1808 rename a snapshot of the image.
1809
1810 :param srcname: the src name of the snapshot
1811 :type srcname: str
1812 :param dstname: the dst name of the snapshot
1813 :type dstname: str
1814 :raises: :class:`ImageExists`
1815 """
1816 srcname = cstr(srcname, 'srcname')
1817 dstname = cstr(dstname, 'dstname')
1818 cdef:
1819 char *_srcname = srcname
1820 char *_dstname = dstname
1821 with nogil:
1822 ret = rbd_snap_rename(self.image, _srcname, _dstname)
1823 if ret != 0:
1824 raise make_ex(ret, 'error renaming snapshot of %s from %s to %s' % (self.name, srcname, dstname))
1825
1826 def remove_snap(self, name):
1827 """
1828 Delete a snapshot of the image.
1829
1830 :param name: the name of the snapshot
1831 :type name: str
1832 :raises: :class:`IOError`, :class:`ImageBusy`
1833 """
1834 name = cstr(name, 'name')
1835 cdef char *_name = name
1836 with nogil:
1837 ret = rbd_snap_remove(self.image, _name)
1838 if ret != 0:
1839 raise make_ex(ret, 'error removing snapshot %s from %s' % (name, self.name))
1840
1841 def remove_snap2(self, name, flags):
1842 """
1843 Delete a snapshot of the image.
1844
1845 :param name: the name of the snapshot
1846 :param flags: the flags for removal
1847 :type name: str
1848 :raises: :class:`IOError`, :class:`ImageBusy`
1849 """
1850 name = cstr(name, 'name')
1851 cdef:
1852 char *_name = name
1853 uint32_t _flags = flags
1854 librbd_progress_fn_t prog_cb = &no_op_progress_callback
1855 with nogil:
1856 ret = rbd_snap_remove2(self.image, _name, _flags, prog_cb, NULL)
1857 if ret != 0:
1858 raise make_ex(ret, 'error removing snapshot %s from %s with flags %llx' % (name, self.name, flags))
1859
1860 def rollback_to_snap(self, name):
1861 """
1862 Revert the image to its contents at a snapshot. This is a
1863 potentially expensive operation, since it rolls back each
1864 object individually.
1865
1866 :param name: the snapshot to rollback to
1867 :type name: str
1868 :raises: :class:`IOError`
1869 """
1870 name = cstr(name, 'name')
1871 cdef char *_name = name
1872 with nogil:
1873 ret = rbd_snap_rollback(self.image, _name)
1874 if ret != 0:
1875 raise make_ex(ret, 'error rolling back image %s to snapshot %s' % (self.name, name))
1876
1877 def protect_snap(self, name):
1878 """
1879 Mark a snapshot as protected. This means it can't be deleted
1880 until it is unprotected.
1881
1882 :param name: the snapshot to protect
1883 :type name: str
1884 :raises: :class:`IOError`, :class:`ImageNotFound`
1885 """
1886 name = cstr(name, 'name')
1887 cdef char *_name = name
1888 with nogil:
1889 ret = rbd_snap_protect(self.image, _name)
1890 if ret != 0:
1891 raise make_ex(ret, 'error protecting snapshot %s@%s' % (self.name, name))
1892
1893 def unprotect_snap(self, name):
1894 """
1895 Mark a snapshot unprotected. This allows it to be deleted if
1896 it was protected.
1897
1898 :param name: the snapshot to unprotect
1899 :type name: str
1900 :raises: :class:`IOError`, :class:`ImageNotFound`
1901 """
1902 name = cstr(name, 'name')
1903 cdef char *_name = name
1904 with nogil:
1905 ret = rbd_snap_unprotect(self.image, _name)
1906 if ret != 0:
1907 raise make_ex(ret, 'error unprotecting snapshot %s@%s' % (self.name, name))
1908
1909 def is_protected_snap(self, name):
1910 """
1911 Find out whether a snapshot is protected from deletion.
1912
1913 :param name: the snapshot to check
1914 :type name: str
1915 :returns: bool - whether the snapshot is protected
1916 :raises: :class:`IOError`, :class:`ImageNotFound`
1917 """
1918 name = cstr(name, 'name')
1919 cdef:
1920 char *_name = name
1921 int is_protected
1922 with nogil:
1923 ret = rbd_snap_is_protected(self.image, _name, &is_protected)
1924 if ret != 0:
1925 raise make_ex(ret, 'error checking if snapshot %s@%s is protected' % (self.name, name))
1926 return is_protected == 1
1927
1928 def get_snap_limit(self):
1929 """
1930 Get the snapshot limit for an image.
1931 """
1932
1933 cdef:
1934 uint64_t limit
1935 with nogil:
1936 ret = rbd_snap_get_limit(self.image, &limit)
1937 if ret != 0:
1938 raise make_ex(ret, 'error getting snapshot limit for %s' % self.name)
1939 return limit
1940
1941 def set_snap_limit(self, limit):
1942 """
1943 Set the snapshot limit for an image.
1944
1945 :param limit: the new limit to set
1946 """
1947
1948 cdef:
1949 uint64_t _limit = limit
1950 with nogil:
1951 ret = rbd_snap_set_limit(self.image, _limit)
1952 if ret != 0:
1953 raise make_ex(ret, 'error setting snapshot limit for %s' % self.name)
1954 return ret
1955
1956 def get_snap_timestamp(self, snap_id):
1957 """
1958 Get the snapshot timestamp for an image.
1959 :param snap_id: the snapshot id of a snap shot
1960 """
1961 cdef:
1962 timespec timestamp
1963 uint64_t _snap_id = snap_id
1964 with nogil:
1965 ret = rbd_snap_get_timestamp(self.image, _snap_id, &timestamp)
1966 if ret != 0:
1967 raise make_ex(ret, 'error getting snapshot timestamp for image: %s, snap_id: %d' % (self.name, snap_id))
28e407b8 1968 return datetime.utcfromtimestamp(timestamp.tv_sec)
7c673cae
FG
1969
1970 def remove_snap_limit(self):
1971 """
1972 Remove the snapshot limit for an image, essentially setting
1973 the limit to the maximum size allowed by the implementation.
1974 """
1975 with nogil:
1976 ret = rbd_snap_set_limit(self.image, UINT64_MAX)
1977 if ret != 0:
1978 raise make_ex(ret, 'error removing snapshot limit for %s' % self.name)
1979 return ret
1980
1981 def set_snap(self, name):
1982 """
1983 Set the snapshot to read from. Writes will raise ReadOnlyImage
1984 while a snapshot is set. Pass None to unset the snapshot
1985 (reads come from the current image) , and allow writing again.
1986
1987 :param name: the snapshot to read from, or None to unset the snapshot
1988 :type name: str or None
1989 """
1990 name = cstr(name, 'name', opt=True)
1991 cdef char *_name = opt_str(name)
1992 with nogil:
1993 ret = rbd_snap_set(self.image, _name)
1994 if ret != 0:
1995 raise make_ex(ret, 'error setting image %s to snapshot %s' % (self.name, name))
1996
1997 def read(self, offset, length, fadvise_flags=0):
1998 """
1999 Read data from the image. Raises :class:`InvalidArgument` if
2000 part of the range specified is outside the image.
2001
2002 :param offset: the offset to start reading at
2003 :type offset: int
2004 :param length: how many bytes to read
2005 :type length: int
2006 :param fadvise_flags: fadvise flags for this read
2007 :type fadvise_flags: int
2008 :returns: str - the data read
2009 :raises: :class:`InvalidArgument`, :class:`IOError`
2010 """
2011
2012 # This usage of the Python API allows us to construct a string
2013 # that librbd directly reads into, avoiding an extra copy. Although
2014 # strings are normally immutable, this usage is explicitly supported
2015 # for freshly created string objects.
2016 cdef:
2017 char *ret_buf
2018 uint64_t _offset = offset
2019 size_t _length = length
2020 int _fadvise_flags = fadvise_flags
2021 PyObject* ret_s = NULL
2022 ret_s = PyBytes_FromStringAndSize(NULL, length)
2023 try:
2024 ret_buf = PyBytes_AsString(ret_s)
2025 with nogil:
2026 ret = rbd_read2(self.image, _offset, _length, ret_buf,
2027 _fadvise_flags)
2028 if ret < 0:
2029 raise make_ex(ret, 'error reading %s %ld~%ld' % (self.name, offset, length))
2030
2031 if ret != <ssize_t>length:
2032 _PyBytes_Resize(&ret_s, ret)
2033
2034 return <object>ret_s
2035 finally:
2036 # We DECREF unconditionally: the cast to object above will have
2037 # INCREFed if necessary. This also takes care of exceptions,
2038 # including if _PyString_Resize fails (that will free the string
2039 # itself and set ret_s to NULL, hence XDECREF).
2040 ref.Py_XDECREF(ret_s)
2041
2042 def diff_iterate(self, offset, length, from_snapshot, iterate_cb,
2043 include_parent = True, whole_object = False):
2044 """
2045 Iterate over the changed extents of an image.
2046
2047 This will call iterate_cb with three arguments:
2048
2049 (offset, length, exists)
2050
2051 where the changed extent starts at offset bytes, continues for
2052 length bytes, and is full of data (if exists is True) or zeroes
2053 (if exists is False).
2054
2055 If from_snapshot is None, it is interpreted as the beginning
2056 of time and this generates all allocated extents.
2057
2058 The end version is whatever is currently selected (via set_snap)
2059 for the image.
2060
2061 iterate_cb may raise an exception, which will abort the diff and will be
2062 propagated to the caller.
2063
2064 Raises :class:`InvalidArgument` if from_snapshot is after
2065 the currently set snapshot.
2066
2067 Raises :class:`ImageNotFound` if from_snapshot is not the name
2068 of a snapshot of the image.
2069
2070 :param offset: start offset in bytes
2071 :type offset: int
2072 :param length: size of region to report on, in bytes
2073 :type length: int
2074 :param from_snapshot: starting snapshot name, or None
2075 :type from_snapshot: str or None
2076 :param iterate_cb: function to call for each extent
2077 :type iterate_cb: function acception arguments for offset,
2078 length, and exists
2079 :param include_parent: True if full history diff should include parent
2080 :type include_parent: bool
2081 :param whole_object: True if diff extents should cover whole object
2082 :type whole_object: bool
2083 :raises: :class:`InvalidArgument`, :class:`IOError`,
2084 :class:`ImageNotFound`
2085 """
2086 from_snapshot = cstr(from_snapshot, 'from_snapshot', opt=True)
2087 cdef:
2088 char *_from_snapshot = opt_str(from_snapshot)
2089 uint64_t _offset = offset, _length = length
2090 uint8_t _include_parent = include_parent
2091 uint8_t _whole_object = whole_object
2092 with nogil:
2093 ret = rbd_diff_iterate2(self.image, _from_snapshot, _offset,
2094 _length, _include_parent, _whole_object,
2095 &diff_iterate_cb, <void *>iterate_cb)
2096 if ret < 0:
2097 msg = 'error generating diff from snapshot %s' % from_snapshot
2098 raise make_ex(ret, msg)
2099
2100 def write(self, data, offset, fadvise_flags=0):
2101 """
2102 Write data to the image. Raises :class:`InvalidArgument` if
2103 part of the write would fall outside the image.
2104
2105 :param data: the data to be written
2106 :type data: bytes
2107 :param offset: where to start writing data
2108 :type offset: int
2109 :param fadvise_flags: fadvise flags for this write
2110 :type fadvise_flags: int
2111 :returns: int - the number of bytes written
2112 :raises: :class:`IncompleteWriteError`, :class:`LogicError`,
2113 :class:`InvalidArgument`, :class:`IOError`
2114 """
2115 if not isinstance(data, bytes):
2116 raise TypeError('data must be a byte string')
2117 cdef:
2118 uint64_t _offset = offset, length = len(data)
2119 char *_data = data
2120 int _fadvise_flags = fadvise_flags
2121 with nogil:
2122 ret = rbd_write2(self.image, _offset, length, _data, _fadvise_flags)
2123
2124 if ret == <ssize_t>length:
2125 return ret
2126 elif ret < 0:
2127 raise make_ex(ret, "error writing to %s" % (self.name,))
2128 elif ret < <ssize_t>length:
2129 raise IncompleteWriteError("Wrote only %ld out of %ld bytes" % (ret, length))
2130 else:
2131 raise LogicError("logic error: rbd_write(%s) \
2132returned %d, but %d was the maximum number of bytes it could have \
2133written." % (self.name, ret, length))
2134
2135 def discard(self, offset, length):
2136 """
2137 Trim the range from the image. It will be logically filled
2138 with zeroes.
2139 """
2140 cdef uint64_t _offset = offset, _length = length
2141 with nogil:
2142 ret = rbd_discard(self.image, _offset, _length)
2143 if ret < 0:
2144 msg = 'error discarding region %d~%d' % (offset, length)
2145 raise make_ex(ret, msg)
2146
2147 def flush(self):
2148 """
2149 Block until all writes are fully flushed if caching is enabled.
2150 """
2151 with nogil:
2152 ret = rbd_flush(self.image)
2153 if ret < 0:
2154 raise make_ex(ret, 'error flushing image')
2155
2156 def invalidate_cache(self):
2157 """
2158 Drop any cached data for the image.
2159 """
2160 with nogil:
2161 ret = rbd_invalidate_cache(self.image)
2162 if ret < 0:
2163 raise make_ex(ret, 'error invalidating cache')
2164
2165 def stripe_unit(self):
2166 """
c07f9fc5 2167 Return the stripe unit used for the image.
7c673cae
FG
2168 """
2169 cdef uint64_t stripe_unit
2170 with nogil:
2171 ret = rbd_get_stripe_unit(self.image, &stripe_unit)
2172 if ret != 0:
2173 raise make_ex(ret, 'error getting stripe unit for image %s' % (self.name))
2174 return stripe_unit
2175
2176 def stripe_count(self):
2177 """
c07f9fc5 2178 Return the stripe count used for the image.
7c673cae
FG
2179 """
2180 cdef uint64_t stripe_count
2181 with nogil:
2182 ret = rbd_get_stripe_count(self.image, &stripe_count)
2183 if ret != 0:
2184 raise make_ex(ret, 'error getting stripe count for image %s' % (self.name))
2185 return stripe_count
2186
31f18b77
FG
2187 def create_timestamp(self):
2188 """
c07f9fc5 2189 Return the create timestamp for the image.
31f18b77
FG
2190 """
2191 cdef:
2192 timespec timestamp
2193 with nogil:
2194 ret = rbd_get_create_timestamp(self.image, &timestamp)
2195 if ret != 0:
2196 raise make_ex(ret, 'error getting create timestamp for image: %s' % (self.name))
28e407b8 2197 return datetime.utcfromtimestamp(timestamp.tv_sec)
31f18b77 2198
7c673cae
FG
2199 def flatten(self):
2200 """
2201 Flatten clone image (copy all blocks from parent to child)
2202 """
2203 with nogil:
2204 ret = rbd_flatten(self.image)
2205 if ret < 0:
2206 raise make_ex(ret, "error flattening %s" % self.name)
2207
2208 def rebuild_object_map(self):
2209 """
c07f9fc5 2210 Rebuild the object map for the image HEAD or currently set snapshot
7c673cae
FG
2211 """
2212 cdef librbd_progress_fn_t prog_cb = &no_op_progress_callback
2213 with nogil:
2214 ret = rbd_rebuild_object_map(self.image, prog_cb, NULL)
2215 if ret < 0:
2216 raise make_ex(ret, "error rebuilding object map %s" % self.name)
2217
2218 def list_children(self):
2219 """
2220 List children of the currently set snapshot (set via set_snap()).
2221
2222 :returns: list - a list of (pool name, image name) tuples
2223 """
2224 cdef:
2225 size_t pools_size = 512, images_size = 512
2226 char *c_pools = NULL
2227 char *c_images = NULL
2228 try:
2229 while True:
2230 c_pools = <char *>realloc_chk(c_pools, pools_size)
2231 c_images = <char *>realloc_chk(c_images, images_size)
2232 with nogil:
2233 ret = rbd_list_children(self.image, c_pools, &pools_size,
2234 c_images, &images_size)
2235 if ret >= 0:
2236 break
2237 elif ret != -errno.ERANGE:
2238 raise make_ex(ret, 'error listing images')
2239 if ret == 0:
2240 return []
2241 pools = map(decode_cstr, c_pools[:pools_size - 1].split(b'\0'))
2242 images = map(decode_cstr, c_images[:images_size - 1].split(b'\0'))
2243 return list(zip(pools, images))
2244 finally:
2245 free(c_pools)
2246 free(c_images)
2247
2248 def list_lockers(self):
2249 """
2250 List clients that have locked the image and information
2251 about the lock.
2252
2253 :returns: dict - contains the following keys:
2254
2255 * ``tag`` - the tag associated with the lock (every
2256 additional locker must use the same tag)
2257 * ``exclusive`` - boolean indicating whether the
2258 lock is exclusive or shared
2259 * ``lockers`` - a list of (client, cookie, address)
2260 tuples
2261 """
2262 cdef:
2263 size_t clients_size = 512, cookies_size = 512
2264 size_t addrs_size = 512, tag_size = 512
2265 int exclusive = 0
2266 char *c_clients = NULL
2267 char *c_cookies = NULL
2268 char *c_addrs = NULL
2269 char *c_tag = NULL
2270
2271 try:
2272 while True:
2273 c_clients = <char *>realloc_chk(c_clients, clients_size)
2274 c_cookies = <char *>realloc_chk(c_cookies, cookies_size)
2275 c_addrs = <char *>realloc_chk(c_addrs, addrs_size)
2276 c_tag = <char *>realloc_chk(c_tag, tag_size)
2277 with nogil:
2278 ret = rbd_list_lockers(self.image, &exclusive,
2279 c_tag, &tag_size,
2280 c_clients, &clients_size,
2281 c_cookies, &cookies_size,
2282 c_addrs, &addrs_size)
2283 if ret >= 0:
2284 break
2285 elif ret != -errno.ERANGE:
2286 raise make_ex(ret, 'error listing images')
2287 if ret == 0:
2288 return []
2289 clients = map(decode_cstr, c_clients[:clients_size - 1].split(b'\0'))
2290 cookies = map(decode_cstr, c_cookies[:cookies_size - 1].split(b'\0'))
2291 addrs = map(decode_cstr, c_addrs[:addrs_size - 1].split(b'\0'))
2292 return {
2293 'tag' : decode_cstr(c_tag),
2294 'exclusive' : exclusive == 1,
2295 'lockers' : list(zip(clients, cookies, addrs)),
2296 }
2297 finally:
2298 free(c_clients)
2299 free(c_cookies)
2300 free(c_addrs)
2301 free(c_tag)
2302
2303 def lock_acquire(self, lock_mode):
2304 """
2305 Acquire a managed lock on the image.
2306
2307 :param lock_mode: lock mode to set
2308 :type lock_mode: int
2309 :raises: :class:`ImageBusy` if the lock could not be acquired
2310 """
2311 cdef:
2312 rbd_lock_mode_t _lock_mode = lock_mode
2313 with nogil:
2314 ret = rbd_lock_acquire(self.image, _lock_mode)
2315 if ret < 0:
2316 raise make_ex(ret, 'error acquiring lock on image')
2317
2318 def lock_release(self):
2319 """
2320 Release a managed lock on the image that was previously acquired.
2321 """
2322 with nogil:
2323 ret = rbd_lock_release(self.image)
2324 if ret < 0:
2325 raise make_ex(ret, 'error releasing lock on image')
2326
2327 def lock_get_owners(self):
2328 """
2329 Iterate over the lock owners of an image.
2330
2331 :returns: :class:`LockOwnerIterator`
2332 """
2333 return LockOwnerIterator(self)
2334
2335 def lock_break(self, lock_mode, lock_owner):
2336 """
2337 Break the image lock held by a another client.
2338
2339 :param lock_owner: the owner of the lock to break
2340 :type lock_owner: str
2341 """
2342 lock_owner = cstr(lock_owner, 'lock_owner')
2343 cdef:
2344 rbd_lock_mode_t _lock_mode = lock_mode
2345 char *_lock_owner = lock_owner
2346 with nogil:
2347 ret = rbd_lock_break(self.image, _lock_mode, _lock_owner)
2348 if ret < 0:
2349 raise make_ex(ret, 'error breaking lock on image')
2350
2351 def lock_exclusive(self, cookie):
2352 """
2353 Take an exclusive lock on the image.
2354
2355 :raises: :class:`ImageBusy` if a different client or cookie locked it
2356 :class:`ImageExists` if the same client and cookie locked it
2357 """
2358 cookie = cstr(cookie, 'cookie')
2359 cdef char *_cookie = cookie
2360 with nogil:
2361 ret = rbd_lock_exclusive(self.image, _cookie)
2362 if ret < 0:
2363 raise make_ex(ret, 'error acquiring exclusive lock on image')
2364
2365 def lock_shared(self, cookie, tag):
2366 """
2367 Take a shared lock on the image. The tag must match
2368 that of the existing lockers, if any.
2369
2370 :raises: :class:`ImageBusy` if a different client or cookie locked it
2371 :class:`ImageExists` if the same client and cookie locked it
2372 """
2373 cookie = cstr(cookie, 'cookie')
2374 tag = cstr(tag, 'tag')
2375 cdef:
2376 char *_cookie = cookie
2377 char *_tag = tag
2378 with nogil:
2379 ret = rbd_lock_shared(self.image, _cookie, _tag)
2380 if ret < 0:
2381 raise make_ex(ret, 'error acquiring shared lock on image')
2382
2383 def unlock(self, cookie):
2384 """
2385 Release a lock on the image that was locked by this rados client.
2386 """
2387 cookie = cstr(cookie, 'cookie')
2388 cdef char *_cookie = cookie
2389 with nogil:
2390 ret = rbd_unlock(self.image, _cookie)
2391 if ret < 0:
2392 raise make_ex(ret, 'error unlocking image')
2393
2394 def break_lock(self, client, cookie):
2395 """
2396 Release a lock held by another rados client.
2397 """
2398 client = cstr(client, 'client')
2399 cookie = cstr(cookie, 'cookie')
2400 cdef:
2401 char *_client = client
2402 char *_cookie = cookie
2403 with nogil:
2404 ret = rbd_break_lock(self.image, _client, _cookie)
2405 if ret < 0:
2406 raise make_ex(ret, 'error unlocking image')
2407
2408 def mirror_image_enable(self):
2409 """
2410 Enable mirroring for the image.
2411 """
2412 with nogil:
2413 ret = rbd_mirror_image_enable(self.image)
2414 if ret < 0:
2415 raise make_ex(ret, 'error enabling mirroring for image %s'
2416 % (self.name,))
2417
2418 def mirror_image_disable(self, force):
2419 """
2420 Disable mirroring for the image.
2421
2422 :param force: force disabling
2423 :type force: bool
2424 """
2425 cdef bint c_force = force
2426 with nogil:
2427 ret = rbd_mirror_image_disable(self.image, c_force)
2428 if ret < 0:
2429 raise make_ex(ret, 'error disabling mirroring for image %s' %
2430 (self.name,))
2431
2432 def mirror_image_promote(self, force):
2433 """
2434 Promote the image to primary for mirroring.
2435
2436 :param force: force promoting
2437 :type force: bool
2438 """
2439 cdef bint c_force = force
2440 with nogil:
2441 ret = rbd_mirror_image_promote(self.image, c_force)
2442 if ret < 0:
2443 raise make_ex(ret, 'error promoting image %s to primary' %
2444 (self.name,))
2445
2446 def mirror_image_demote(self):
2447 """
2448 Demote the image to secondary for mirroring.
2449 """
2450 with nogil:
2451 ret = rbd_mirror_image_demote(self.image)
2452 if ret < 0:
2453 raise make_ex(ret, 'error demoting image %s to secondary' %
2454 (self.name,))
2455
2456 def mirror_image_resync(self):
2457 """
2458 Flag the image to resync.
2459 """
2460 with nogil:
2461 ret = rbd_mirror_image_resync(self.image)
2462 if ret < 0:
2463 raise make_ex(ret, 'error to resync image %s' % (self.name,))
2464
2465 def mirror_image_get_info(self):
2466 """
2467 Get mirror info for the image.
2468
2469 :returns: dict - contains the following keys:
2470
2471 * ``global_id`` (str) - image global id
2472
2473 * ``state`` (int) - mirror state
2474
2475 * ``primary`` (bool) - is image primary
2476 """
2477 cdef rbd_mirror_image_info_t c_info
2478 with nogil:
2479 ret = rbd_mirror_image_get_info(self.image, &c_info, sizeof(c_info))
2480 if ret != 0:
2481 raise make_ex(ret, 'error getting mirror info for image %s' %
2482 (self.name,))
2483 info = {
2484 'global_id' : decode_cstr(c_info.global_id),
2485 'state' : int(c_info.state),
2486 'primary' : c_info.primary,
2487 }
2488 free(c_info.global_id)
2489 return info
2490
2491 def mirror_image_get_status(self):
2492 """
2493 Get mirror status for the image.
2494
2495 :returns: dict - contains the following keys:
2496
2497 * ``name`` (str) - mirror image name
2498
2499 * `info` (dict) - mirror image info
2500
2501 * ``state`` (int) - status mirror state
2502
2503 * ``description`` (str) - status description
2504
2505 * ``last_update`` (datetime) - last status update time
2506
2507 * ``up`` (bool) - is mirroring agent up
2508 """
2509 cdef rbd_mirror_image_status_t c_status
2510 with nogil:
2511 ret = rbd_mirror_image_get_status(self.image, &c_status,
2512 sizeof(c_status))
2513 if ret != 0:
2514 raise make_ex(ret, 'error getting mirror status for image %s' %
2515 (self.name,))
2516 status = {
2517 'name' : decode_cstr(c_status.name),
2518 'info' : {
2519 'global_id' : decode_cstr(c_status.info.global_id),
2520 'state' : int(c_status.info.state),
2521 'primary' : c_status.info.primary,
2522 },
2523 'state' : c_status.state,
2524 'description' : decode_cstr(c_status.description),
28e407b8 2525 'last_update' : datetime.utcfromtimestamp(c_status.last_update),
7c673cae
FG
2526 'up' : c_status.up,
2527 }
2528 free(c_status.name)
2529 free(c_status.info.global_id)
2530 free(c_status.description)
2531 return status
2532
2533 def aio_read(self, offset, length, oncomplete, fadvise_flags=0):
2534 """
2535 Asynchronously read data from the image
2536
2537 Raises :class:`InvalidArgument` if part of the range specified is
2538 outside the image.
2539
2540 oncomplete will be called with the returned read value as
2541 well as the completion:
2542
2543 oncomplete(completion, data_read)
2544
2545 :param offset: the offset to start reading at
2546 :type offset: int
2547 :param length: how many bytes to read
2548 :type length: int
2549 :param oncomplete: what to do when the read is complete
2550 :type oncomplete: completion
2551 :param fadvise_flags: fadvise flags for this read
2552 :type fadvise_flags: int
2553 :returns: :class:`Completion` - the completion object
2554 :raises: :class:`InvalidArgument`, :class:`IOError`
2555 """
2556
2557 cdef:
2558 char *ret_buf
2559 uint64_t _offset = offset
2560 size_t _length = length
2561 int _fadvise_flags = fadvise_flags
2562 Completion completion
2563
2564 def oncomplete_(completion_v):
2565 cdef Completion _completion_v = completion_v
2566 return_value = _completion_v.get_return_value()
2567 if return_value > 0 and return_value != length:
2568 _PyBytes_Resize(&_completion_v.buf, return_value)
2569 return oncomplete(_completion_v, <object>_completion_v.buf if return_value >= 0 else None)
2570
2571 completion = self.__get_completion(oncomplete_)
2572 completion.buf = PyBytes_FromStringAndSize(NULL, length)
2573 ret_buf = PyBytes_AsString(completion.buf)
2574 try:
2575 completion.__persist()
2576 with nogil:
2577 ret = rbd_aio_read2(self.image, _offset, _length, ret_buf,
2578 completion.rbd_comp, _fadvise_flags)
2579 if ret < 0:
2580 raise make_ex(ret, 'error reading %s %ld~%ld' %
2581 (self.name, offset, length))
2582 except:
2583 completion.__unpersist()
2584 raise
2585
2586 return completion
2587
2588 def aio_write(self, data, offset, oncomplete, fadvise_flags=0):
2589 """
2590 Asynchronously write data to the image
2591
2592 Raises :class:`InvalidArgument` if part of the write would fall outside
2593 the image.
2594
2595 oncomplete will be called with the completion:
2596
2597 oncomplete(completion)
2598
2599 :param data: the data to be written
2600 :type data: bytes
2601 :param offset: the offset to start writing at
2602 :type offset: int
2603 :param oncomplete: what to do when the write is complete
2604 :type oncomplete: completion
2605 :param fadvise_flags: fadvise flags for this write
2606 :type fadvise_flags: int
2607 :returns: :class:`Completion` - the completion object
2608 :raises: :class:`InvalidArgument`, :class:`IOError`
2609 """
2610
2611 cdef:
2612 uint64_t _offset = offset
2613 char *_data = data
2614 size_t _length = len(data)
2615 int _fadvise_flags = fadvise_flags
2616 Completion completion
2617
2618 completion = self.__get_completion(oncomplete)
2619 try:
2620 completion.__persist()
2621 with nogil:
2622 ret = rbd_aio_write2(self.image, _offset, _length, _data,
2623 completion.rbd_comp, _fadvise_flags)
2624 if ret < 0:
2625 raise make_ex(ret, 'error writing %s %ld~%ld' %
2626 (self.name, offset, _length))
2627 except:
2628 completion.__unpersist()
2629 raise
2630
2631 return completion
2632
2633 def aio_discard(self, offset, length, oncomplete):
2634 """
2635 Asynchronously trim the range from the image. It will be logically
2636 filled with zeroes.
2637 """
2638
2639 cdef:
2640 uint64_t _offset = offset
2641 size_t _length = length
2642 Completion completion
2643
2644 completion = self.__get_completion(oncomplete)
2645 try:
2646 completion.__persist()
2647 with nogil:
2648 ret = rbd_aio_discard(self.image, _offset, _length,
2649 completion.rbd_comp)
2650 if ret < 0:
2651 raise make_ex(ret, 'error discarding %s %ld~%ld' %
2652 (self.name, offset, _length))
2653 except:
2654 completion.__unpersist()
2655 raise
2656
2657 return completion
2658
2659 def aio_flush(self, oncomplete):
2660 """
2661 Asyncronously wait until all writes are fully flushed if caching is
2662 enabled.
2663 """
2664
2665 cdef Completion completion = self.__get_completion(oncomplete)
2666 try:
2667 completion.__persist()
2668 with nogil:
2669 ret = rbd_aio_flush(self.image, completion.rbd_comp)
2670 if ret < 0:
2671 raise make_ex(ret, 'error flushing')
2672 except:
2673 completion.__unpersist()
2674 raise
2675
2676 return completion
2677
2678 def metadata_get(self, key):
2679 """
2680 Get image metadata for the given key.
2681
2682 :param key: metadata key
2683 :type key: str
2684 :returns: str - image id
2685 """
2686 key = cstr(key, 'key')
2687 cdef:
2688 char *_key = key
2689 size_t size = 4096
2690 char *value = NULL
2691 int ret
2692 try:
2693 while True:
2694 value = <char *>realloc_chk(value, size)
2695 with nogil:
2696 ret = rbd_metadata_get(self.image, _key, value, &size)
2697 if ret != -errno.ERANGE:
2698 break
b32b8144
FG
2699 if ret == -errno.ENOENT:
2700 raise KeyError('no metadata %s for image %s' % (key, self.name))
7c673cae
FG
2701 if ret != 0:
2702 raise make_ex(ret, 'error getting metadata %s for image %s' %
b32b8144 2703 (key, self.name,))
7c673cae
FG
2704 return decode_cstr(value)
2705 finally:
2706 free(value)
2707
2708 def metadata_set(self, key, value):
2709 """
2710 Set image metadata for the given key.
2711
2712 :param key: metadata key
2713 :type key: str
2714 :param value: metadata value
2715 :type value: str
2716 """
2717 key = cstr(key, 'key')
2718 value = cstr(value, 'value')
2719 cdef:
2720 char *_key = key
2721 char *_value = value
2722 with nogil:
2723 ret = rbd_metadata_set(self.image, _key, _value)
2724
2725 if ret != 0:
2726 raise make_ex(ret, 'error setting metadata %s for image %s' %
b32b8144 2727 (key, self.name,))
7c673cae
FG
2728
2729
2730 def metadata_remove(self, key):
2731 """
2732 Remove image metadata for the given key.
2733
2734 :param key: metadata key
2735 :type key: str
2736 """
2737 key = cstr(key, 'key')
2738 cdef:
2739 char *_key = key
2740 with nogil:
2741 ret = rbd_metadata_remove(self.image, _key)
2742
b32b8144
FG
2743 if ret == -errno.ENOENT:
2744 raise KeyError('no metadata %s for image %s' % (key, self.name))
7c673cae
FG
2745 if ret != 0:
2746 raise make_ex(ret, 'error removing metadata %s for image %s' %
b32b8144 2747 (key, self.name,))
7c673cae
FG
2748
2749 def metadata_list(self):
2750 """
2751 List image metadata.
2752
2753 :returns: :class:`MetadataIterator`
2754 """
2755 return MetadataIterator(self)
2756
2757cdef class LockOwnerIterator(object):
2758 """
2759 Iterator over managed lock owners for an image
2760
2761 Yields a dictionary containing information about the image's lock
2762
2763 Keys are:
2764
2765 * ``mode`` (int) - active lock mode
2766
2767 * ``owner`` (str) - lock owner name
2768 """
2769
2770 cdef:
2771 rbd_lock_mode_t lock_mode
2772 char **lock_owners
2773 size_t num_lock_owners
2774 object image
2775
2776 def __init__(self, Image image):
2777 self.image = image
2778 self.lock_owners = NULL
2779 self.num_lock_owners = 8
2780 while True:
2781 self.lock_owners = <char**>realloc_chk(self.lock_owners,
2782 self.num_lock_owners *
2783 sizeof(char*))
2784 with nogil:
2785 ret = rbd_lock_get_owners(image.image, &self.lock_mode,
2786 self.lock_owners,
2787 &self.num_lock_owners)
2788 if ret >= 0:
2789 break
2790 elif ret != -errno.ERANGE:
2791 raise make_ex(ret, 'error listing lock owners for image %s' % (image.name,))
2792
2793 def __iter__(self):
2794 for i in range(self.num_lock_owners):
2795 yield {
2796 'mode' : int(self.lock_mode),
2797 'owner' : decode_cstr(self.lock_owners[i]),
2798 }
2799
2800 def __dealloc__(self):
2801 if self.lock_owners:
2802 rbd_lock_get_owners_cleanup(self.lock_owners, self.num_lock_owners)
2803 free(self.lock_owners)
2804
2805cdef class MetadataIterator(object):
2806 """
2807 Iterator over metadata list for an image.
2808
2809 Yields ``(key, value)`` tuple.
2810
2811 * ``key`` (str) - metadata key
2812 * ``value`` (str) - metadata value
2813 """
2814
2815 cdef:
2816 object image_name
2817 rbd_image_t image
2818 char *last_read
2819 uint64_t max_read
2820 object next_chunk
2821
2822 def __init__(self, Image image):
2823 self.image_name = image.name
2824 self.image = image.image
2825 self.last_read = strdup("")
2826 self.max_read = 32
2827 self.get_next_chunk()
2828
2829 def __iter__(self):
2830 while len(self.next_chunk) > 0:
2831 for pair in self.next_chunk:
2832 yield pair
2833 if len(self.next_chunk) < self.max_read:
2834 break
2835 self.get_next_chunk()
2836
31f18b77
FG
2837 def __dealloc__(self):
2838 if self.last_read:
2839 free(self.last_read)
2840
7c673cae
FG
2841 def get_next_chunk(self):
2842 cdef:
2843 char *c_keys = NULL
2844 size_t keys_size = 4096
2845 char *c_vals = NULL
2846 size_t vals_size = 4096
2847 try:
2848 while True:
2849 c_keys = <char *>realloc_chk(c_keys, keys_size)
2850 c_vals = <char *>realloc_chk(c_vals, vals_size)
2851 with nogil:
2852 ret = rbd_metadata_list(self.image, self.last_read,
2853 self.max_read, c_keys, &keys_size,
2854 c_vals, &vals_size)
2855 if ret >= 0:
2856 break
2857 elif ret != -errno.ERANGE:
2858 raise make_ex(ret, 'error listing metadata for image %s' %
2859 (self.image_name,))
2860 keys = [decode_cstr(key) for key in
2861 c_keys[:keys_size].split(b'\0') if key]
2862 vals = [decode_cstr(val) for val in
2863 c_vals[:vals_size].split(b'\0') if val]
2864 if len(keys) > 0:
2865 free(self.last_read)
2866 self.last_read = strdup(keys[-1])
2867 self.next_chunk = zip(keys, vals)
2868 finally:
2869 free(c_keys)
2870 free(c_vals)
2871
2872cdef class SnapIterator(object):
2873 """
2874 Iterator over snapshot info for an image.
2875
2876 Yields a dictionary containing information about a snapshot.
2877
2878 Keys are:
2879
2880 * ``id`` (int) - numeric identifier of the snapshot
2881
2882 * ``size`` (int) - size of the image at the time of snapshot (in bytes)
2883
2884 * ``name`` (str) - name of the snapshot
2885 """
2886
2887 cdef rbd_snap_info_t *snaps
2888 cdef int num_snaps
2889 cdef object image
2890
2891 def __init__(self, Image image):
2892 self.image = image
2893 self.snaps = NULL
2894 self.num_snaps = 10
2895 while True:
2896 self.snaps = <rbd_snap_info_t*>realloc_chk(self.snaps,
31f18b77
FG
2897 self.num_snaps *
2898 sizeof(rbd_snap_info_t))
7c673cae
FG
2899 with nogil:
2900 ret = rbd_snap_list(image.image, self.snaps, &self.num_snaps)
2901 if ret >= 0:
2902 self.num_snaps = ret
2903 break
2904 elif ret != -errno.ERANGE:
2905 raise make_ex(ret, 'error listing snapshots for image %s' % (image.name,))
2906
2907 def __iter__(self):
2908 for i in range(self.num_snaps):
2909 yield {
2910 'id' : self.snaps[i].id,
2911 'size' : self.snaps[i].size,
2912 'name' : decode_cstr(self.snaps[i].name),
2913 }
2914
2915 def __dealloc__(self):
2916 if self.snaps:
2917 rbd_snap_list_end(self.snaps)
2918 free(self.snaps)
2919
2920cdef class TrashIterator(object):
2921 """
2922 Iterator over trash entries.
2923
2924 Yields a dictionary containing trash info of an image.
2925
2926 Keys are:
2927
2928 * `id` (str) - image id
2929
2930 * `name` (str) - image name
2931
2932 * `source` (str) - source of deletion
2933
2934 * `deletion_time` (datetime) - time of deletion
2935
2936 * `deferment_end_time` (datetime) - time that an image is allowed to be
2937 removed from trash
2938 """
2939
2940 cdef:
2941 rados_ioctx_t ioctx
2942 size_t num_entries
2943 rbd_trash_image_info_t *entries
2944
2945 def __init__(self, ioctx):
2946 self.ioctx = convert_ioctx(ioctx)
2947 self.num_entries = 1024
31f18b77
FG
2948 self.entries = NULL
2949 while True:
2950 self.entries = <rbd_trash_image_info_t*>realloc_chk(self.entries,
2951 self.num_entries *
2952 sizeof(rbd_trash_image_info_t))
2953 with nogil:
2954 ret = rbd_trash_list(self.ioctx, self.entries, &self.num_entries)
2955 if ret >= 0:
2956 self.num_entries = ret
2957 break
2958 elif ret != -errno.ERANGE:
2959 raise make_ex(ret, 'error listing trash entries')
7c673cae
FG
2960
2961 __source_string = ['USER', 'MIRRORING']
2962
2963 def __iter__(self):
2964 for i in range(self.num_entries):
2965 yield {
2966 'id' : decode_cstr(self.entries[i].id),
2967 'name' : decode_cstr(self.entries[i].name),
2968 'source' : TrashIterator.__source_string[self.entries[i].source],
28e407b8
AA
2969 'deletion_time' : datetime.utcfromtimestamp(self.entries[i].deletion_time),
2970 'deferment_end_time' : datetime.utcfromtimestamp(self.entries[i].deferment_end_time)
7c673cae
FG
2971 }
2972
2973 def __dealloc__(self):
2974 rbd_trash_list_cleanup(self.entries, self.num_entries)
2975 if self.entries:
2976 free(self.entries)
2977