~tribaal/ubuntu/trusty/ceph/ceph-zap-in-two-phases

« back to all changes in this revision

Viewing changes to .pc/python_rados_cluster_stat.patch/src/pybind/rados.py

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-05-19 21:04:18 UTC
  • Revision ID: package-import@ubuntu.com-20130519210418-053rk70dzn5z0a6r
Tags: 0.61.2-0ubuntu2
* Fix ceph-test package:
  - d/ceph-test.install: Add missing install file.
  - d/rules: Enable --with-debug option to build test executables.
  - d/p/fix_test_ftbfs.patch: Fix format-security errors.
* Fix rbd-fuse package:
  - d/rbd-fuse.install: Add missing install file.
* d/ceph.install: Include missing udev rule for ceph-osd disks.
* d/rules: Drop --with-system-leveldb as this is now the default.
* d/control,rules: Review and further re-sync with upstream packaging.
* d/ceph.{postinst,prerm}: start/stop ceph-all upstart configuration
  if upstart is in use.
* d/ceph-mds.{postinst,prerm}: start/stop ceph-mds-all upstart configuration
  if upstart is in use.
* d/p/python_rados_cluster_stat.patch: Dropped - included upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""librados Python ctypes wrapper
2
 
Copyright 2011, Hannu Valtonen <hannu.valtonen@ormod.com>
3
 
"""
4
 
from ctypes import CDLL, c_char_p, c_size_t, c_void_p, c_int, c_long, \
5
 
    create_string_buffer, byref, Structure, c_uint64, c_ubyte, pointer, \
6
 
    CFUNCTYPE
7
 
import threading
8
 
import ctypes
9
 
import errno
10
 
import time
11
 
from datetime import datetime
12
 
 
13
 
ANONYMOUS_AUID = 0xffffffffffffffff
14
 
ADMIN_AUID = 0
15
 
 
16
 
class Error(Exception):
17
 
    pass
18
 
 
19
 
class PermissionError(Error):
20
 
    pass
21
 
 
22
 
class ObjectNotFound(Error):
23
 
    pass
24
 
 
25
 
class NoData(Error):
26
 
    pass
27
 
 
28
 
class ObjectExists(Error):
29
 
    pass
30
 
 
31
 
class IOError(Error):
32
 
    pass
33
 
 
34
 
class NoSpace(Error):
35
 
    pass
36
 
 
37
 
class IncompleteWriteError(Error):
38
 
    pass
39
 
 
40
 
class RadosStateError(Error):
41
 
    pass
42
 
 
43
 
class IoctxStateError(Error):
44
 
    pass
45
 
 
46
 
class ObjectStateError(Error):
47
 
    pass
48
 
 
49
 
class LogicError(Error):
50
 
    pass
51
 
 
52
 
def make_ex(ret, msg):
53
 
    ret = abs(ret)
54
 
    if (ret == errno.EPERM):
55
 
        return PermissionError(msg)
56
 
    elif (ret == errno.ENOENT):
57
 
        return ObjectNotFound(msg)
58
 
    elif (ret == errno.EIO):
59
 
        return IOError(msg)
60
 
    elif (ret == errno.ENOSPC):
61
 
        return NoSpace(msg)
62
 
    elif (ret == errno.EEXIST):
63
 
        return ObjectExists(msg)
64
 
    elif (ret == errno.ENODATA):
65
 
        return NoData(msg)
66
 
    else:
67
 
        return Error(msg + (": error code %d" % ret))
68
 
 
69
 
class rados_pool_stat_t(Structure):
70
 
    _fields_ = [("num_bytes", c_uint64),
71
 
                ("num_kb", c_uint64),
72
 
                ("num_objects", c_uint64),
73
 
                ("num_object_clones", c_uint64),
74
 
                ("num_object_copies", c_uint64),
75
 
                ("num_objects_missing_on_primary", c_uint64),
76
 
                ("num_objects_unfound", c_uint64),
77
 
                ("num_objects_degraded", c_uint64),
78
 
                ("num_rd", c_uint64),
79
 
                ("num_rd_kb", c_uint64),
80
 
                ("num_wr", c_uint64),
81
 
                ("num_wr_kb", c_uint64)]
82
 
 
83
 
class rados_cluster_stat_t(Structure):
84
 
    _fields_ = [("kb", c_uint64),
85
 
                ("kb_used", c_uint64),
86
 
                ("kb_avail", c_uint64),
87
 
                ("num_objects", c_uint64)]
88
 
 
89
 
class Version(object):
90
 
    def __init__(self, major, minor, extra):
91
 
        self.major = major
92
 
        self.minor = minor
93
 
        self.extra = extra
94
 
 
95
 
    def __str__(self):
96
 
        return "%d.%d.%d" % (self.major, self.minor, self.extra)
97
 
 
98
 
class Rados(object):
99
 
    """librados python wrapper"""
100
 
    def require_state(self, *args):
101
 
        for a in args:
102
 
            if self.state == a:
103
 
                return
104
 
        raise RadosStateError("You cannot perform that operation on a \
105
 
Rados object in state %s." % (self.state))
106
 
 
107
 
    def __init__(self, rados_id=None, conf=None, conffile=None):
108
 
        self.librados = CDLL('librados.so.2')
109
 
        self.cluster = c_void_p()
110
 
        self.rados_id = rados_id
111
 
        if rados_id is not None and not isinstance(rados_id, str):
112
 
            raise TypeError('rados_id must be a string or None')
113
 
        if conffile is not None and not isinstance(conffile, str):
114
 
            raise TypeError('conffile must be a string or None')
115
 
        ret = self.librados.rados_create(byref(self.cluster), c_char_p(rados_id))
116
 
        if ret != 0:
117
 
            raise Error("rados_initialize failed with error code: %d" % ret)
118
 
        self.state = "configuring"
119
 
        if conffile is not None:
120
 
            # read the default conf file when '' is given
121
 
            if conffile == '':
122
 
                conffile = None
123
 
            self.conf_read_file(conffile)
124
 
        if conf is not None:
125
 
            for key, value in conf.iteritems():
126
 
                self.conf_set(key, value)
127
 
 
128
 
    def shutdown(self):
129
 
        if (self.__dict__.has_key("state") and self.state != "shutdown"):
130
 
            self.librados.rados_shutdown(self.cluster)
131
 
            self.state = "shutdown"
132
 
 
133
 
    def __enter__(self):
134
 
        self.connect()
135
 
        return self
136
 
 
137
 
    def __exit__(self, type_, value, traceback):
138
 
        self.shutdown()
139
 
        return False
140
 
 
141
 
    def __del__(self):
142
 
        self.shutdown()
143
 
 
144
 
    def version(self):
145
 
        major = c_int(0)
146
 
        minor = c_int(0)
147
 
        extra = c_int(0)
148
 
        self.librados.rados_version(byref(major), byref(minor), byref(extra))
149
 
        return Version(major.value, minor.value, extra.value)
150
 
 
151
 
    def conf_read_file(self, path=None):
152
 
        self.require_state("configuring", "connected")
153
 
        if path is not None and not isinstance(path, str):
154
 
            raise TypeError('path must be a string')
155
 
        ret = self.librados.rados_conf_read_file(self.cluster, c_char_p(path))
156
 
        if (ret != 0):
157
 
            raise make_ex(ret, "error calling conf_read_file")
158
 
 
159
 
    def conf_get(self, option):
160
 
        self.require_state("configuring", "connected")
161
 
        if not isinstance(option, str):
162
 
            raise TypeError('option must be a string')
163
 
        length = 20
164
 
        while True:
165
 
            ret_buf = create_string_buffer(length)
166
 
            ret = self.librados.rados_conf_get(self.cluster, option,
167
 
                                                ret_buf, c_size_t(length))
168
 
            if (ret == 0):
169
 
                return ret_buf.value
170
 
            elif (ret == -errno.ENAMETOOLONG):
171
 
                length = length * 2
172
 
            elif (ret == -errno.ENOENT):
173
 
                return None
174
 
            else:
175
 
                raise make_ex(ret, "error calling conf_get")
176
 
 
177
 
    def conf_set(self, option, val):
178
 
        self.require_state("configuring", "connected")
179
 
        if not isinstance(option, str):
180
 
            raise TypeError('option must be a string')
181
 
        if not isinstance(val, str):
182
 
            raise TypeError('val must be a string')
183
 
        ret = self.librados.rados_conf_set(self.cluster, c_char_p(option),
184
 
                                            c_char_p(val))
185
 
        if (ret != 0):
186
 
            raise make_ex(ret, "error calling conf_set")
187
 
 
188
 
    def connect(self):
189
 
        self.require_state("configuring")
190
 
        ret = self.librados.rados_connect(self.cluster)
191
 
        if (ret != 0):
192
 
            raise make_ex(ret, "error calling connect")
193
 
        self.state = "connected"
194
 
 
195
 
    def get_cluster_stats(self):
196
 
        stats = rados_cluster_stat_t()
197
 
        ret = self.librados.rados_cluster_stat(self.cluster, byref(stats))
198
 
        if ret < 0:
199
 
            raise make_ex(
200
 
                ret, "Rados.get_cluster_stats(%s): get_stats failed" % self.rados_id)
201
 
        return {'kb': stats.kb,
202
 
                'kb_used': stats.kb_used,
203
 
                'kb_avail': stats.kb_avail,
204
 
                'num_objects': stats.num_objects}
205
 
 
206
 
    # Returns true if the pool exists; false otherwise.
207
 
    def pool_exists(self, pool_name):
208
 
        self.require_state("connected")
209
 
        if not isinstance(pool_name, str):
210
 
            raise TypeError('pool_name must be a string')
211
 
        ret = self.librados.rados_pool_lookup(self.cluster, c_char_p(pool_name))
212
 
        if (ret >= 0):
213
 
            return True
214
 
        elif (ret == -errno.ENOENT):
215
 
            return False
216
 
        else:
217
 
            raise make_ex(ret, "error looking up pool '%s'" % pool_name)
218
 
 
219
 
    def create_pool(self, pool_name, auid=None, crush_rule=None):
220
 
        self.require_state("connected")
221
 
        if not isinstance(pool_name, str):
222
 
            raise TypeError('pool_name must be a string')
223
 
        if crush_rule is not None and not isinstance(crush_rule, str):
224
 
            raise TypeError('cruse_rule must be a string')
225
 
        if (auid == None):
226
 
            if (crush_rule == None):
227
 
                ret = self.librados.rados_pool_create(
228
 
                            self.cluster, c_char_p(pool_name))
229
 
            else:
230
 
                ret = self.librados.rados_pool_create_with_all(
231
 
                            self.cluster, c_char_p(pool_name), c_uint64(auid),
232
 
                            c_ubyte(crush_rule))
233
 
        elif (crush_rule == None):
234
 
            ret = self.librados.rados_pool_create_with_auid(
235
 
                        self.cluster, c_char_p(pool_name), c_uint64(auid))
236
 
        else:
237
 
            ret = self.librados.rados_pool_create_with_crush_rule(
238
 
                        self.cluster, c_char_p(pool_name), c_ubyte(crush_rule))
239
 
        if ret < 0:
240
 
            raise make_ex(ret, "error creating pool '%s'" % pool_name)
241
 
 
242
 
    def delete_pool(self, pool_name):
243
 
        self.require_state("connected")
244
 
        if not isinstance(pool_name, str):
245
 
            raise TypeError('pool_name must be a string')
246
 
        ret = self.librados.rados_pool_delete(self.cluster, c_char_p(pool_name))
247
 
        if ret < 0:
248
 
            raise make_ex(ret, "error deleting pool '%s'" % pool_name)
249
 
 
250
 
    def list_pools(self):
251
 
        self.require_state("connected")
252
 
        size = c_size_t(512)
253
 
        while True:
254
 
            c_names = create_string_buffer(size.value)
255
 
            ret = self.librados.rados_pool_list(self.cluster,
256
 
                                                byref(c_names), size)
257
 
            if ret > size.value:
258
 
                size = c_size_t(ret)
259
 
            else:
260
 
                break
261
 
        return filter(lambda name: name != '', c_names.raw.split('\0'))
262
 
 
263
 
    def get_fsid(self):
264
 
        self.require_state("connected")
265
 
        fsid_len = 36
266
 
        fsid = create_string_buffer(fsid_len + 1)
267
 
        ret = self.librados.rados_cluster_fsid(self.cluster,
268
 
                                               byref(fsid),
269
 
                                               fsid_len + 1)
270
 
        if ret < 0:
271
 
            raise make_ex(ret, "error getting cluster fsid")
272
 
        return fsid.value
273
 
 
274
 
    def open_ioctx(self, ioctx_name):
275
 
        self.require_state("connected")
276
 
        if not isinstance(ioctx_name, str):
277
 
            raise TypeError('ioctx_name must be a string')
278
 
        ioctx = c_void_p()
279
 
        ret = self.librados.rados_ioctx_create(self.cluster, c_char_p(ioctx_name), byref(ioctx))
280
 
        if ret < 0:
281
 
            raise make_ex(ret, "error opening ioctx '%s'" % ioctx_name)
282
 
        return Ioctx(ioctx_name, self.librados, ioctx)
283
 
 
284
 
class ObjectIterator(object):
285
 
    """rados.Ioctx Object iterator"""
286
 
    def __init__(self, ioctx):
287
 
        self.ioctx = ioctx
288
 
        self.ctx = c_void_p()
289
 
        ret = self.ioctx.librados.\
290
 
            rados_objects_list_open(self.ioctx.io, byref(self.ctx))
291
 
        if ret < 0:
292
 
            raise make_ex(ret, "error iterating over the objects in ioctx '%s'" \
293
 
                % self.ioctx.name)
294
 
 
295
 
    def __iter__(self):
296
 
        return self
297
 
 
298
 
    def next(self):
299
 
        key = c_char_p()
300
 
        locator = c_char_p()
301
 
        ret = self.ioctx.librados.rados_objects_list_next(self.ctx, byref(key),
302
 
                                                          byref(locator))
303
 
        if ret < 0:
304
 
            raise StopIteration()
305
 
        return Object(self.ioctx, key.value, locator.value)
306
 
 
307
 
    def __del__(self):
308
 
        self.ioctx.librados.rados_objects_list_close(self.ctx)
309
 
 
310
 
class XattrIterator(object):
311
 
    """Extended attribute iterator"""
312
 
    def __init__(self, ioctx, it, oid):
313
 
        self.ioctx = ioctx
314
 
        self.it = it
315
 
        self.oid = oid
316
 
    def __iter__(self):
317
 
        return self
318
 
    def next(self):
319
 
        name_ = c_char_p(0)
320
 
        val_ = c_char_p(0)
321
 
        len_ = c_int(0)
322
 
        ret = self.ioctx.librados.\
323
 
            rados_getxattrs_next(self.it, byref(name_), byref(val_), byref(len_))
324
 
        if (ret != 0):
325
 
            raise make_ex(ret, "error iterating over the extended attributes \
326
 
in '%s'" % self.oid)
327
 
        if name_.value == None:
328
 
            raise StopIteration()
329
 
        name = ctypes.string_at(name_)
330
 
        val = ctypes.string_at(val_, len_)
331
 
        return (name, val)
332
 
    def __del__(self):
333
 
        self.ioctx.librados.rados_getxattrs_end(self.it)
334
 
 
335
 
class SnapIterator(object):
336
 
    """Snapshot iterator"""
337
 
    def __init__(self, ioctx):
338
 
        self.ioctx = ioctx
339
 
        # We don't know how big a buffer we need until we've called the
340
 
        # function. So use the exponential doubling strategy.
341
 
        num_snaps = 10
342
 
        while True:
343
 
            self.snaps = (ctypes.c_uint64 * num_snaps)()
344
 
            ret = self.ioctx.librados.rados_ioctx_snap_list(self.ioctx.io,
345
 
                                self.snaps, num_snaps)
346
 
            if (ret >= 0):
347
 
                self.max_snap = ret
348
 
                break
349
 
            elif (ret != -errno.ERANGE):
350
 
                raise make_ex(ret, "error calling rados_snap_list for \
351
 
ioctx '%s'" % self.ioctx.name)
352
 
            num_snaps = num_snaps * 2
353
 
        self.cur_snap = 0
354
 
 
355
 
    def __iter__(self):
356
 
        return self
357
 
 
358
 
    def next(self):
359
 
        if (self.cur_snap >= self.max_snap):
360
 
            raise StopIteration
361
 
        snap_id = self.snaps[self.cur_snap]
362
 
        name_len = 10
363
 
        while True:
364
 
            name = create_string_buffer(name_len)
365
 
            ret = self.ioctx.librados.rados_ioctx_snap_get_name(self.ioctx.io, \
366
 
                                snap_id, byref(name), name_len)
367
 
            if (ret == 0):
368
 
                name_len = ret
369
 
                break
370
 
            elif (ret != -errno.ERANGE):
371
 
                raise make_ex(ret, "rados_snap_get_name error")
372
 
            name_len = name_len * 2
373
 
        snap = Snap(self.ioctx, name.value, snap_id)
374
 
        self.cur_snap = self.cur_snap + 1
375
 
        return snap
376
 
 
377
 
class Snap(object):
378
 
    """Snapshot object"""
379
 
    def __init__(self, ioctx, name, snap_id):
380
 
        self.ioctx = ioctx
381
 
        self.name = name
382
 
        self.snap_id = snap_id
383
 
 
384
 
    def __str__(self):
385
 
        return "rados.Snap(ioctx=%s,name=%s,snap_id=%d)" \
386
 
            % (str(self.ioctx), self.name, self.snap_id)
387
 
 
388
 
    def get_timestamp(self):
389
 
        snap_time = c_long(0)
390
 
        ret = self.ioctx.librados.rados_ioctx_snap_get_stamp(
391
 
            self.ioctx.io, self.snap_id,
392
 
            byref(snap_time))
393
 
        if (ret != 0):
394
 
            raise make_ex(ret, "rados_ioctx_snap_get_stamp error")
395
 
        return datetime.fromtimestamp(snap_time.value)
396
 
 
397
 
class Completion(object):
398
 
    """completion object"""
399
 
    def __init__(self, ioctx, rados_comp, oncomplete, onsafe):
400
 
        self.rados_comp = rados_comp
401
 
        self.oncomplete = oncomplete
402
 
        self.onsafe = onsafe
403
 
        self.ioctx = ioctx
404
 
 
405
 
    def wait_for_safe(self):
406
 
        return self.ioctx.librados.rados_aio_is_safe(
407
 
            self.rados_comp
408
 
            )
409
 
 
410
 
    def wait_for_complete(self):
411
 
        return self.ioctx.librados.rados_aio_is_complete(
412
 
            self.rados_comp
413
 
            )
414
 
 
415
 
    def get_return_value(self):
416
 
        return self.ioctx.librados.rados_aio_get_return_value(
417
 
            self.rados_comp)
418
 
 
419
 
    def __del__(self):
420
 
        self.ioctx.librados.rados_aio_release(
421
 
            self.rados_comp
422
 
            )
423
 
 
424
 
class Ioctx(object):
425
 
    """rados.Ioctx object"""
426
 
    def __init__(self, name, librados, io):
427
 
        self.name = name
428
 
        self.librados = librados
429
 
        self.io = io
430
 
        self.state = "open"
431
 
        self.locator_key = ""
432
 
        self.safe_cbs = {}
433
 
        self.complete_cbs = {}
434
 
        RADOS_CB = CFUNCTYPE(c_int, c_void_p, c_void_p)
435
 
        self.__aio_safe_cb_c = RADOS_CB(self.__aio_safe_cb)
436
 
        self.__aio_complete_cb_c = RADOS_CB(self.__aio_complete_cb)
437
 
        self.lock = threading.Lock()
438
 
 
439
 
    def __enter__(self):
440
 
        return self
441
 
 
442
 
    def __exit__(self, type_, value, traceback):
443
 
        self.close()
444
 
        return False
445
 
 
446
 
    def __del__(self):
447
 
        self.close()
448
 
 
449
 
    def __aio_safe_cb(self, completion, _):
450
 
        cb = None
451
 
        with self.lock:
452
 
            cb = self.safe_cbs[completion]
453
 
            del self.safe_cbs[completion]
454
 
        cb.onsafe(cb)
455
 
        return 0
456
 
 
457
 
    def __aio_complete_cb(self, completion, _):
458
 
        cb = None
459
 
        with self.lock:
460
 
            cb = self.complete_cbs[completion]
461
 
            del self.complete_cbs[completion]
462
 
        cb.oncomplete(cb)
463
 
        return 0
464
 
 
465
 
    def __get_completion(self, oncomplete, onsafe):
466
 
        completion = c_void_p(0)
467
 
        complete_cb = None
468
 
        safe_cb = None
469
 
        if oncomplete:
470
 
            complete_cb = self.__aio_complete_cb_c
471
 
        if onsafe:
472
 
            safe_cb = self.__aio_safe_cb_c
473
 
        ret = self.librados.rados_aio_create_completion(
474
 
            c_void_p(0),
475
 
            complete_cb,
476
 
            safe_cb,
477
 
            byref(completion)
478
 
            )
479
 
        if ret < 0:
480
 
            raise make_ex(ret, "error getting a completion")
481
 
        with self.lock:
482
 
            completion_obj = Completion(self, completion, oncomplete, onsafe)
483
 
            if oncomplete:
484
 
                self.complete_cbs[completion.value] = completion_obj
485
 
            if onsafe:
486
 
                self.safe_cbs[completion.value] = completion_obj
487
 
        return completion_obj
488
 
 
489
 
    def aio_write(self, object_name, to_write, offset=0,
490
 
                  oncomplete=None, onsafe=None):
491
 
        completion = self.__get_completion(oncomplete, onsafe)
492
 
        ret = self.librados.rados_aio_write(
493
 
            self.io,
494
 
            c_char_p(object_name),
495
 
            completion.rados_comp,
496
 
            c_char_p(to_write),
497
 
            c_size_t(len(to_write)),
498
 
            c_uint64(offset))
499
 
        if ret < 0:
500
 
            raise make_ex(ret, "error writing object %s" % object_name)
501
 
        return completion
502
 
 
503
 
    def aio_write_full(self, object_name, to_write,
504
 
                       oncomplete=None, onsafe=None):
505
 
        completion = self.__get_completion(oncomplete, onsafe)
506
 
        ret = self.librados.rados_aio_write_full(
507
 
            self.io,
508
 
            c_char_p(object_name),
509
 
            completion.rados_comp,
510
 
            c_char_p(to_write),
511
 
            c_size_t(len(to_write)))
512
 
        if ret < 0:
513
 
            raise make_ex(ret, "error writing object %s" % object_name)
514
 
        return completion
515
 
 
516
 
    def aio_append(self, object_name, to_append, oncomplete=None, onsafe=None):
517
 
        completion = self.__get_completion(oncomplete, onsafe)
518
 
        ret = self.librados.rados_aio_append(
519
 
            self.io,
520
 
            c_char_p(object_name),
521
 
            completion.rados_comp,
522
 
            c_char_p(to_append),
523
 
            c_size_t(len(to_append)))
524
 
        if ret < 0:
525
 
            raise make_ex(ret, "error appending to object %s" % object_name)
526
 
        return completion
527
 
 
528
 
    def aio_flush(self):
529
 
        ret = self.librados.rados_aio_flush(
530
 
            self.io)
531
 
        if ret < 0:
532
 
            raise make_ex(ret, "error flushing")
533
 
 
534
 
    def aio_read(self, object_name, length, offset, oncomplete):
535
 
        """
536
 
        oncomplete will be called with the returned read value as
537
 
        well as the completion:
538
 
 
539
 
        oncomplete(completion, data_read)
540
 
        """
541
 
        buf = create_string_buffer(length)
542
 
        def oncomplete_(completion):
543
 
            return oncomplete(completion, buf.value)
544
 
        completion = self.__get_completion(oncomplete_, None)
545
 
        ret = self.librados.rados_aio_read(
546
 
            self.io,
547
 
            c_char_p(object_name),
548
 
            completion.rados_comp,
549
 
            buf,
550
 
            c_size_t(length),
551
 
            c_uint64(offset))
552
 
        if ret < 0:
553
 
            raise make_ex(ret, "error reading %s" % object_name)
554
 
        return completion
555
 
 
556
 
    def require_ioctx_open(self):
557
 
        if self.state != "open":
558
 
            raise IoctxStateError("The pool is %s" % self.state)
559
 
 
560
 
    def change_auid(self, auid):
561
 
        self.require_ioctx_open()
562
 
        ret = self.librados.rados_ioctx_pool_set_auid(self.io, \
563
 
                ctypes.c_uint64(auid))
564
 
        if ret < 0:
565
 
            raise make_ex(ret, "error changing auid of '%s' to %d" %\
566
 
                (self.name, auid))
567
 
 
568
 
    def set_locator_key(self, loc_key):
569
 
        self.require_ioctx_open()
570
 
        if not isinstance(loc_key, str):
571
 
            raise TypeError('loc_key must be a string')
572
 
        self.librados.rados_ioctx_locator_set_key(self.io, \
573
 
                c_char_p(loc_key))
574
 
        self.locator_key = loc_key
575
 
 
576
 
    def get_locator_key(self):
577
 
        return self.locator_key
578
 
 
579
 
    def close(self):
580
 
        if self.state == "open":
581
 
            self.require_ioctx_open()
582
 
            self.librados.rados_ioctx_destroy(self.io)
583
 
            self.state = "closed"
584
 
 
585
 
    def write(self, key, data, offset=0):
586
 
        self.require_ioctx_open()
587
 
        if not isinstance(data, str):
588
 
            raise TypeError('data must be a string')
589
 
        length = len(data)
590
 
        ret = self.librados.rados_write(self.io, c_char_p(key),
591
 
                 c_char_p(data), c_size_t(length), c_uint64(offset))
592
 
        if ret == length:
593
 
            return ret
594
 
        elif ret < 0:
595
 
            raise make_ex(ret, "Ioctx.write(%s): failed to write %s" % \
596
 
                (self.name, key))
597
 
        elif ret < length:
598
 
            raise IncompleteWriteError("Wrote only %d out of %d bytes" % \
599
 
                (ret, length))
600
 
        else:
601
 
            raise LogicError("Ioctx.write(%s): rados_write \
602
 
returned %d, but %d was the maximum number of bytes it could have \
603
 
written." % (self.name, ret, length))
604
 
 
605
 
    def write_full(self, key, data):
606
 
        self.require_ioctx_open()
607
 
        if not isinstance(key, str):
608
 
            raise TypeError('key must be a string')
609
 
        if not isinstance(data, str):
610
 
            raise TypeError('data must be a string')
611
 
        length = len(data)
612
 
        ret = self.librados.rados_write_full(self.io, c_char_p(key),
613
 
                 c_char_p(data), c_size_t(length))
614
 
        if ret == 0:
615
 
            return ret
616
 
        else:
617
 
            raise make_ex(ret, "Ioctx.write(%s): failed to write_full %s" % \
618
 
                (self.name, key))
619
 
 
620
 
    def read(self, key, length=8192, offset=0):
621
 
        self.require_ioctx_open()
622
 
        if not isinstance(key, str):
623
 
            raise TypeError('key must be a string')
624
 
        ret_buf = create_string_buffer(length)
625
 
        ret = self.librados.rados_read(self.io, c_char_p(key), ret_buf,
626
 
                c_size_t(length), c_uint64(offset))
627
 
        if ret < 0:
628
 
            raise make_ex(ret, "Ioctx.read(%s): failed to read %s" % (self.name, key))
629
 
        return ctypes.string_at(ret_buf, ret)
630
 
 
631
 
    def get_stats(self):
632
 
        self.require_ioctx_open()
633
 
        stats = rados_pool_stat_t()
634
 
        ret = self.librados.rados_ioctx_pool_stat(self.io, byref(stats))
635
 
        if ret < 0:
636
 
            raise make_ex(ret, "Ioctx.get_stats(%s): get_stats failed" % self.name)
637
 
        return {'num_bytes': stats.num_bytes,
638
 
                'num_kb': stats.num_kb,
639
 
                'num_objects': stats.num_objects,
640
 
                'num_object_clones': stats.num_object_clones,
641
 
                'num_object_copies': stats.num_object_copies,
642
 
                "num_objects_missing_on_primary": stats.num_objects_missing_on_primary,
643
 
                "num_objects_unfound": stats.num_objects_unfound,
644
 
                "num_objects_degraded": stats.num_objects_degraded,
645
 
                "num_rd": stats.num_rd,
646
 
                "num_rd_kb": stats.num_rd_kb,
647
 
                "num_wr": stats.num_wr,
648
 
                "num_wr_kb": stats.num_wr_kb }
649
 
 
650
 
    def remove_object(self, key):
651
 
        self.require_ioctx_open()
652
 
        if not isinstance(key, str):
653
 
            raise TypeError('key must be a string')
654
 
        ret = self.librados.rados_remove(self.io, c_char_p(key))
655
 
        if ret < 0:
656
 
            raise make_ex(ret, "Failed to remove '%s'" % key)
657
 
        return True
658
 
 
659
 
    def trunc(self, key, size):
660
 
        self.require_ioctx_open()
661
 
        if not isinstance(key, str):
662
 
            raise TypeError('key must be a string')
663
 
        ret = self.librados.rados_trunc(self.io, c_char_p(key), c_size_t(size))
664
 
        if ret < 0:
665
 
            raise make_ex(ret, "Ioctx.trunc(%s): failed to truncate %s" % (self.name, key))
666
 
        return ret
667
 
 
668
 
    def stat(self, key):
669
 
        """Stat object, returns, size/timestamp"""
670
 
        self.require_ioctx_open()
671
 
        if not isinstance(key, str):
672
 
            raise TypeError('key must be a string')
673
 
        psize = c_uint64()
674
 
        pmtime = c_uint64()
675
 
 
676
 
        ret = self.librados.rados_stat(self.io, c_char_p(key), pointer(psize),
677
 
                                        pointer(pmtime))
678
 
        if ret < 0:
679
 
            raise make_ex(ret, "Failed to stat %r" % key)
680
 
        return psize.value, time.localtime(pmtime.value)
681
 
 
682
 
    def get_xattr(self, key, xattr_name):
683
 
        self.require_ioctx_open()
684
 
        if not isinstance(xattr_name, str):
685
 
            raise TypeError('xattr_name must be a string')
686
 
        ret_length = 4096
687
 
        ret_buf = create_string_buffer(ret_length)
688
 
        ret = self.librados.rados_getxattr(self.io, c_char_p(key),
689
 
                    c_char_p(xattr_name), ret_buf, c_size_t(ret_length))
690
 
        if ret < 0:
691
 
            raise make_ex(ret, "Failed to get xattr %r" % xattr_name)
692
 
        return ctypes.string_at(ret_buf, ret)
693
 
 
694
 
    def get_xattrs(self, oid):
695
 
        self.require_ioctx_open()
696
 
        if not isinstance(oid, str):
697
 
            raise TypeError('oid must be a string')
698
 
        it = c_void_p(0)
699
 
        ret = self.librados.rados_getxattrs(self.io, oid, byref(it))
700
 
        if ret != 0:
701
 
            raise make_ex(ret, "Failed to get rados xattrs for object %r" % oid)
702
 
        return XattrIterator(self, it, oid)
703
 
 
704
 
    def set_xattr(self, key, xattr_name, xattr_value):
705
 
        self.require_ioctx_open()
706
 
        if not isinstance(key, str):
707
 
            raise TypeError('key must be a string')
708
 
        if not isinstance(xattr_name, str):
709
 
            raise TypeError('xattr_name must be a string')
710
 
        if not isinstance(xattr_value, str):
711
 
            raise TypeError('xattr_value must be a string')
712
 
        ret = self.librados.rados_setxattr(self.io, c_char_p(key),
713
 
                    c_char_p(xattr_name), c_char_p(xattr_value),
714
 
                    c_size_t(len(xattr_value)))
715
 
        if ret < 0:
716
 
            raise make_ex(ret, "Failed to set xattr %r" % xattr_name)
717
 
        return True
718
 
 
719
 
    def rm_xattr(self, key, xattr_name):
720
 
        self.require_ioctx_open()
721
 
        if not isinstance(key, str):
722
 
            raise TypeError('key must be a string')
723
 
        if not isinstance(xattr_name, str):
724
 
            raise TypeError('xattr_name must be a string')
725
 
        ret = self.librados.rados_rmxattr(self.io, c_char_p(key), c_char_p(xattr_name))
726
 
        if ret < 0:
727
 
            raise make_ex(ret, "Failed to delete key %r xattr %r" %
728
 
                (key, xattr_name))
729
 
        return True
730
 
 
731
 
    def list_objects(self):
732
 
        self.require_ioctx_open()
733
 
        return ObjectIterator(self)
734
 
 
735
 
    def list_snaps(self):
736
 
        self.require_ioctx_open()
737
 
        return SnapIterator(self)
738
 
 
739
 
    def create_snap(self, snap_name):
740
 
        self.require_ioctx_open()
741
 
        if not isinstance(snap_name, str):
742
 
            raise TypeError('snap_name must be a string')
743
 
        ret = self.librados.rados_ioctx_snap_create(self.io,
744
 
                                    c_char_p(snap_name))
745
 
        if (ret != 0):
746
 
            raise make_ex(ret, "Failed to create snap %s" % snap_name)
747
 
 
748
 
    def remove_snap(self, snap_name):
749
 
        self.require_ioctx_open()
750
 
        if not isinstance(snap_name, str):
751
 
            raise TypeError('snap_name must be a string')
752
 
        ret = self.librados.rados_ioctx_snap_remove(self.io,
753
 
                                    c_char_p(snap_name))
754
 
        if (ret != 0):
755
 
            raise make_ex(ret, "Failed to remove snap %s" % snap_name)
756
 
 
757
 
    def lookup_snap(self, snap_name):
758
 
        self.require_ioctx_open()
759
 
        if not isinstance(snap_name, str):
760
 
            raise TypeError('snap_name must be a string')
761
 
        snap_id = c_uint64()
762
 
        ret = self.librados.rados_ioctx_snap_lookup(self.io, \
763
 
                            c_char_p(snap_name), byref(snap_id))
764
 
        if (ret != 0):
765
 
            raise make_ex(ret, "Failed to lookup snap %s" % snap_name)
766
 
        return Snap(self, snap_name, snap_id)
767
 
 
768
 
    def get_last_version(self):
769
 
        self.require_ioctx_open()
770
 
        return self.librados.rados_get_last_version(self.io)
771
 
 
772
 
def set_object_locator(func):
773
 
    def retfunc(self, *args, **kwargs):
774
 
        if self.locator_key is not None:
775
 
            old_locator = self.ioctx.get_locator_key()
776
 
            self.ioctx.set_locator_key(self.locator_key)
777
 
            retval = func(self, *args, **kwargs)
778
 
            self.ioctx.set_locator_key(old_locator)
779
 
            return retval
780
 
        else:
781
 
            return func(self, *args, **kwargs)
782
 
    return retfunc
783
 
 
784
 
class Object(object):
785
 
    """Rados object wrapper, makes the object look like a file"""
786
 
    def __init__(self, ioctx, key, locator_key=None):
787
 
        self.key = key
788
 
        self.ioctx = ioctx
789
 
        self.offset = 0
790
 
        self.state = "exists"
791
 
        self.locator_key = locator_key
792
 
 
793
 
    def __str__(self):
794
 
        return "rados.Object(ioctx=%s,key=%s)" % (str(self.ioctx), self.key)
795
 
 
796
 
    def require_object_exists(self):
797
 
        if self.state != "exists":
798
 
            raise ObjectStateError("The object is %s" % self.state)
799
 
 
800
 
    @set_object_locator
801
 
    def read(self, length = 1024*1024):
802
 
        self.require_object_exists()
803
 
        ret = self.ioctx.read(self.key, length, self.offset)
804
 
        self.offset += len(ret)
805
 
        return ret
806
 
 
807
 
    @set_object_locator
808
 
    def write(self, string_to_write):
809
 
        self.require_object_exists()
810
 
        ret = self.ioctx.write(self.key, string_to_write, self.offset)
811
 
        self.offset += ret
812
 
        return ret
813
 
 
814
 
    @set_object_locator
815
 
    def remove(self):
816
 
        self.require_object_exists()
817
 
        self.ioctx.remove_object(self.key)
818
 
        self.state = "removed"
819
 
 
820
 
    @set_object_locator
821
 
    def stat(self):
822
 
        self.require_object_exists()
823
 
        return self.ioctx.stat(self.key)
824
 
 
825
 
    def seek(self, position):
826
 
        self.require_object_exists()
827
 
        self.offset = position
828
 
 
829
 
    @set_object_locator
830
 
    def get_xattr(self, xattr_name):
831
 
        self.require_object_exists()
832
 
        return self.ioctx.get_xattr(self.key, xattr_name)
833
 
 
834
 
    @set_object_locator
835
 
    def get_xattrs(self, xattr_name):
836
 
        self.require_object_exists()
837
 
        return self.ioctx.get_xattrs(self.key, xattr_name)
838
 
 
839
 
    @set_object_locator
840
 
    def set_xattr(self, xattr_name, xattr_value):
841
 
        self.require_object_exists()
842
 
        return self.ioctx.set_xattr(self.key, xattr_name, xattr_value)
843
 
 
844
 
    @set_object_locator
845
 
    def rm_xattr(self, xattr_name):
846
 
        self.require_object_exists()
847
 
        return self.ioctx.rm_xattr(self.key, xattr_name)