]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/rgw/rgw.pyx
import ceph 14.2.5
[ceph.git] / ceph / src / pybind / rgw / rgw.pyx
CommitLineData
7c673cae
FG
1"""
2This module is a thin wrapper around rgw_file.
3"""
4
5
6from cpython cimport PyObject, ref, exc, array
7from libc.stdint cimport *
8from libcpp cimport bool
9from libc.stdlib cimport malloc, realloc, free
10
11cimport rados
12
13from collections import namedtuple
14from datetime import datetime
15import errno
16
17
18cdef extern from "Python.h":
19 # These are in cpython/string.pxd, but use "object" types instead of
20 # PyObject*, which invokes assumptions in cpython that we need to
21 # legitimately break to implement zero-copy string buffers in Image.read().
22 # This is valid use of the Python API and documented as a special case.
23 PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL
24 char* PyBytes_AsString(PyObject *string) except NULL
25 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
26 void PyEval_InitThreads()
27
28
29cdef extern from "time.h":
30 ctypedef long int time_t
31
32
33cdef extern from "sys/stat.h":
34 cdef struct stat:
35 unsigned long st_dev
36 unsigned long st_ino
37 unsigned long st_nlink
38 unsigned int st_mode
39 unsigned int st_uid
40 unsigned int st_gid
41 int __pad0
42 unsigned long st_rdev
43 long int st_size
44 long int st_blksize
45 long int st_blocks
46 time_t st_atime
47 time_t st_mtime
48 time_t st_ctime
49
50
51cdef extern from "rados/librgw.h" nogil:
52 ctypedef void* librgw_t
53
54 int librgw_create(librgw_t *rgw, int argc, char **argv)
55 void librgw_shutdown(librgw_t rgw)
56
57
58cdef extern from "rados/rgw_file.h" nogil:
59 enum:
60 RGW_FS_TYPE_FILE
61 RGW_FS_TYPE_DIRECTORY
62
63 RGW_LOOKUP_FLAG_CREATE
64
65 RGW_SETATTR_MODE
66 RGW_SETATTR_UID
67 RGW_SETATTR_GID
68 RGW_SETATTR_MTIME
69 RGW_SETATTR_ATIME
70 RGW_SETATTR_SIZE
71 RGW_SETATTR_CTIME
72
73 RGW_READDIR_FLAG_NONE
74 RGW_READDIR_FLAG_DOTDOT
75
76 RGW_OPEN_FLAG_CREATE
77 RGW_OPEN_FLAG_V3 # ops have v3 semantics
78 RGW_OPEN_FLAG_STATELESS # alias it
79
80 RGW_CLOSE_FLAG_RELE
81
82
83 ctypedef void *rgw_fh_hk
84 cdef struct rgw_file_handle:
85 pass
86
87 cdef struct rgw_fs:
88 librgw_t rgw
89 void *fs_private
90 void *root_fh
91
92 # mount info hypothetical--emulate Unix, support at least UUID-length fsid
93 cdef struct rgw_statvfs:
94 uint64_t f_bsize # file system block size
95 uint64_t f_frsize # fragment size
96 uint64_t f_blocks # size of fs in f_frsize units
97 uint64_t f_bfree # free blocks
98 uint64_t f_bavail # free blocks for unprivileged users
99 uint64_t f_files # inodes
100 uint64_t f_ffree # free inodes
101 uint64_t f_favail # free inodes for unprivileged users
102 uint64_t f_fsid[2] # /* file system ID
103 uint64_t f_flag # mount flags
104 uint64_t f_namemax # maximum filename length
105
106 void rgwfile_version(int *major, int *minor, int *extra)
107
108 int rgw_lookup(rgw_fs *fs,
109 rgw_file_handle *parent_fh, const char *path,
eafe8130
TL
110 rgw_file_handle **fh, stat* st, uint32_t st_mask,
111 uint32_t flags)
7c673cae
FG
112
113 int rgw_lookup_handle(rgw_fs *fs, rgw_fh_hk *fh_hk,
114 rgw_file_handle **fh, uint32_t flags)
115
116 int rgw_fh_rele(rgw_fs *fs, rgw_file_handle *fh,
117 uint32_t flags)
118
119 int rgw_mount(librgw_t rgw, const char *uid, const char *key,
120 const char *secret, rgw_fs **fs, uint32_t flags)
121
122 int rgw_umount(rgw_fs *fs, uint32_t flags)
123
124 int rgw_statfs(rgw_fs *fs, rgw_file_handle *parent_fh,
125 rgw_statvfs *vfs_st, uint32_t flags)
126
127 int rgw_create(rgw_fs *fs, rgw_file_handle *parent_fh,
128 const char *name, stat *st, uint32_t mask,
129 rgw_file_handle **fh, uint32_t posix_flags,
130 uint32_t flags)
131
132 int rgw_mkdir(rgw_fs *fs,
133 rgw_file_handle *parent_fh,
134 const char *name, stat *st, uint32_t mask,
135 rgw_file_handle **fh, uint32_t flags)
136
137 int rgw_rename(rgw_fs *fs,
138 rgw_file_handle *olddir, const char* old_name,
139 rgw_file_handle *newdir, const char* new_name,
140 uint32_t flags)
141
142 int rgw_unlink(rgw_fs *fs,
143 rgw_file_handle *parent_fh, const char* path,
144 uint32_t flags)
145
146 int rgw_readdir(rgw_fs *fs,
147 rgw_file_handle *parent_fh, uint64_t *offset,
eafe8130 148 bool (*cb)(const char *name, void *arg, uint64_t offset, stat *st, uint32_t st_mask, uint32_t flags) nogil except? -9000,
7c673cae
FG
149 void *cb_arg, bool *eof, uint32_t flags) except? -9000
150
151 int rgw_getattr(rgw_fs *fs,
152 rgw_file_handle *fh, stat *st,
153 uint32_t flags)
154
155 int rgw_setattr(rgw_fs *fs, rgw_file_handle *fh, stat *st,
156 uint32_t mask, uint32_t flags)
157
158 int rgw_truncate(rgw_fs *fs, rgw_file_handle *fh, uint64_t size, uint32_t flags)
159
160 int rgw_open(rgw_fs *fs, rgw_file_handle *parent_fh,
161 uint32_t posix_flags, uint32_t flags)
162
163 int rgw_close(rgw_fs *fs, rgw_file_handle *fh,
164 uint32_t flags)
165
166 int rgw_read(rgw_fs *fs,
167 rgw_file_handle *fh, uint64_t offset,
168 size_t length, size_t *bytes_read, void *buffer,
169 uint32_t flags)
170
171 int rgw_write(rgw_fs *fs,
172 rgw_file_handle *fh, uint64_t offset,
173 size_t length, size_t *bytes_written, void *buffer,
174 uint32_t flags)
175
176 int rgw_fsync(rgw_fs *fs, rgw_file_handle *fh,
177 uint32_t flags)
178
179 int rgw_commit(rgw_fs *fs, rgw_file_handle *fh,
180 uint64_t offset, uint64_t length, uint32_t flags)
181
182
183class Error(Exception):
184 pass
185
186
187class OSError(Error):
188 """ `OSError` class, derived from `Error` """
189 def __init__(self, errno, strerror):
190 self.errno = errno
191 self.strerror = strerror
192
193 def __str__(self):
194 return '[Errno {0}] {1}'.format(self.errno, self.strerror)
195
196
197class PermissionError(OSError):
198 pass
199
200
201class ObjectNotFound(OSError):
202 pass
203
204
205class NoData(Error):
206 pass
207
208
209class ObjectExists(Error):
210 pass
211
212
213class IOError(OSError):
214 pass
215
216
217class NoSpace(Error):
218 pass
219
220
221class InvalidValue(Error):
222 pass
223
224
225class OperationNotSupported(Error):
226 pass
227
228
229class IncompleteWriteError(Error):
230 pass
231
232
233class LibCephFSStateError(Error):
234 pass
235
236class WouldBlock(Error):
237 pass
238
239class OutOfRange(Error):
240 pass
241
242IF UNAME_SYSNAME == "FreeBSD":
243 cdef errno_to_exception = {
244 errno.EPERM : PermissionError,
245 errno.ENOENT : ObjectNotFound,
246 errno.EIO : IOError,
247 errno.ENOSPC : NoSpace,
248 errno.EEXIST : ObjectExists,
249 errno.ENOATTR : NoData,
250 errno.EINVAL : InvalidValue,
251 errno.EOPNOTSUPP : OperationNotSupported,
252 errno.ERANGE : OutOfRange,
253 errno.EWOULDBLOCK: WouldBlock,
254 }
255ELSE:
256 cdef errno_to_exception = {
257 errno.EPERM : PermissionError,
258 errno.ENOENT : ObjectNotFound,
259 errno.EIO : IOError,
260 errno.ENOSPC : NoSpace,
261 errno.EEXIST : ObjectExists,
262 errno.ENODATA : NoData,
263 errno.EINVAL : InvalidValue,
264 errno.EOPNOTSUPP : OperationNotSupported,
265 errno.ERANGE : OutOfRange,
266 errno.EWOULDBLOCK: WouldBlock,
267 }
268
269
270cdef class FileHandle(object):
271 cdef rgw_file_handle *handler
272
273
274StatResult = namedtuple('StatResult',
275 ["st_dev", "st_ino", "st_mode", "st_nlink", "st_uid",
276 "st_gid", "st_rdev", "st_size", "st_blksize",
277 "st_blocks", "st_atime", "st_mtime", "st_ctime"])
278
279
280def cstr(val, name, encoding="utf-8", opt=False):
281 """
282 Create a byte string from a Python string
283
284 :param basestring val: Python string
285 :param str name: Name of the string parameter, for exceptions
286 :param str encoding: Encoding to use
287 :param bool opt: If True, None is allowed
288 :rtype: bytes
289 :raises: :class:`InvalidArgument`
290 """
291 if opt and val is None:
292 return None
293 if isinstance(val, bytes):
294 return val
295 elif isinstance(val, unicode):
296 return val.encode(encoding)
297 else:
298 raise TypeError('%s must be a string' % name)
299
300
301cdef make_ex(ret, msg):
302 """
303 Translate a librados return code into an exception.
304
305 :param ret: the return code
306 :type ret: int
307 :param msg: the error message to use
308 :type msg: str
309 :returns: a subclass of :class:`Error`
310 """
311 ret = abs(ret)
312 if ret in errno_to_exception:
313 return errno_to_exception[ret](ret, msg)
314 else:
315 return Error(msg + (": error code %d" % ret))
316
317
eafe8130 318cdef bool readdir_cb(const char *name, void *arg, uint64_t offset, stat *st, uint32_t st_mask, uint32_t flags) \
7c673cae
FG
319except? -9000 with gil:
320 if exc.PyErr_Occurred():
321 return False
11fdf7f2 322 (<object>arg)(name, offset, flags)
7c673cae
FG
323 return True
324
325
326class LibCephFSStateError(Error):
327 pass
328
329
330cdef class LibRGWFS(object):
331 """librgwfs python wrapper"""
332
333 cdef public object state
334 cdef public object uid
335 cdef public object key
336 cdef public object secret
337 cdef librgw_t cluster
338 cdef rgw_fs *fs
339
340 def require_state(self, *args):
341 if self.state in args:
342 return
343 raise LibCephFSStateError("You cannot perform that operation on a "
344 "RGWFS object in state %s." % (self.state))
345
346 def __cinit__(self, uid, key, secret):
347 PyEval_InitThreads()
348 self.state = "umounted"
349 ret = librgw_create(&self.cluster, 0, NULL)
350 if ret != 0:
351 raise make_ex(ret, "error calling librgw_create")
352 self.uid = cstr(uid, "uid")
353 self.key = cstr(key, "key")
354 self.secret = cstr(secret, "secret")
355
356 def shutdown(self):
357 """
358 Unmount and destroy the ceph mount handle.
359 """
360 if self.state in ["mounted"]:
361 with nogil:
362 ret = rgw_umount(self.fs, 0);
363 if ret != 0:
364 raise make_ex(ret, "error calling rgw_unmount")
365 self.state = "shutdown"
366
367 def __enter__(self):
368 self.mount()
369 return self
370
371 def __exit__(self, type_, value, traceback):
372 self.shutdown()
373
374 def __dealloc__(self):
375 self.shutdown()
376
377 def version(self):
378 """
379 Get the version number of the ``librgwfile`` C library.
380
381 :returns: a tuple of ``(major, minor, extra)`` components of the
382 libcephfs version
383 """
384 cdef:
385 int major = 0
386 int minor = 0
387 int extra = 0
388 with nogil:
389 rgwfile_version(&major, &minor, &extra)
390 return (major, minor, extra)
391
392 def mount(self):
393 self.require_state("umounted")
394 cdef:
395 char *_uid = self.uid
396 char *_key = self.key
397 char *_secret = self.secret
398 with nogil:
399 ret = rgw_mount(self.cluster, <const char*>_uid, <const char*>_key,
400 <const char*>_secret, &self.fs, 0)
401 if ret != 0:
402 raise make_ex(ret, "error calling rgw_mount")
403 self.state = "mounted"
404 dir_handler = FileHandle()
405 dir_handler.handler = <rgw_file_handle*>self.fs.root_fh
406 return dir_handler
407
408 def unmount(self):
409 self.require_state("mounted")
410 with nogil:
411 ret = rgw_umount(self.fs, 0)
412 if ret != 0:
413 raise make_ex(ret, "error calling rgw_umount")
414 self.state = "umounted"
415
416 def statfs(self):
417 self.require_state("mounted")
418 cdef:
419 rgw_statvfs statbuf
420
421 with nogil:
422 ret = rgw_statfs(self.fs, <rgw_file_handle*>self.fs.root_fh, &statbuf, 0)
423 if ret < 0:
424 raise make_ex(ret, "statfs failed")
425 cdef uint64_t[:] fsid = statbuf.f_fsid
426 return {'f_bsize': statbuf.f_bsize,
427 'f_frsize': statbuf.f_frsize,
428 'f_blocks': statbuf.f_blocks,
429 'f_bfree': statbuf.f_bfree,
430 'f_bavail': statbuf.f_bavail,
431 'f_files': statbuf.f_files,
432 'f_ffree': statbuf.f_ffree,
433 'f_favail': statbuf.f_favail,
434 'f_fsid': fsid,
435 'f_flag': statbuf.f_flag,
436 'f_namemax': statbuf.f_namemax}
437
438
439 def create(self, FileHandle dir_handler, filename, flags = 0):
440 self.require_state("mounted")
441
442 if not isinstance(flags, int):
443 raise TypeError("flags must be an integer")
444
445 filename = cstr(filename, 'filename')
446
447 cdef:
448 rgw_file_handle *_dir_handler = <rgw_file_handle*>dir_handler.handler
449 rgw_file_handle *_file_handler
450 int _flags = flags
451 char* _filename = filename
452 stat statbuf
453
454 with nogil:
455 ret = rgw_create(self.fs, _dir_handler, _filename, &statbuf, 0,
456 &_file_handler, 0, _flags)
457 if ret < 0:
458 raise make_ex(ret, "error in create '%s'" % filename)
459 with nogil:
460 ret = rgw_open(self.fs, _file_handler, 0, _flags)
461 if ret < 0:
462 raise make_ex(ret, "error in open '%s'" % filename)
463
464 file_handler = FileHandle()
465 file_handler.handler = _file_handler
466 return file_handler
467
468 def mkdir(self, FileHandle dir_handler, dirname, flags = 0):
469 self.require_state("mounted")
470 dirname = cstr(dirname, 'dirname')
471 new_dir_handler = FileHandle()
472 cdef:
473 rgw_file_handle *_dir_handler = <rgw_file_handle*>dir_handler.handler
474 rgw_file_handle *_new_dir_handler
475 char* _dirname = dirname
476 int _flags = flags
477 stat statbuf
478 with nogil:
479 ret = rgw_mkdir(self.fs, _dir_handler, _dirname, &statbuf,
480 0, &_new_dir_handler, _flags)
481 if ret < 0:
482 raise make_ex(ret, "error in mkdir '%s'" % dirname)
483 new_dir_handler.handler = _new_dir_handler
484 return new_dir_handler
485
486 def rename(self, FileHandle src_handler, src_name, FileHandle dst_handler, dst_name, flags = 0):
487 self.require_state("mounted")
488
489 src_name = cstr(src_name, 'src_name')
490 dst_name = cstr(dst_name, 'dst_name')
491
492 cdef:
493 rgw_file_handle *_src_dir_handler = <rgw_file_handle*>src_handler.handler
494 rgw_file_handle *_dst_dir_handler = <rgw_file_handle*>dst_handler.handler
495 char* _src_name = src_name
496 char* _dst_name = dst_name
497 int _flags = flags
498
499 with nogil:
500 ret = rgw_rename(self.fs, _src_dir_handler, _src_name,
501 _dst_dir_handler, _dst_name, _flags)
502 if ret < 0:
503 raise make_ex(ret, "error in rename '%s' to '%s'" % (src_name,
504 dst_name))
505 return ret
506
507 def unlink(self, FileHandle handler, name, flags = 0):
508 self.require_state("mounted")
509 name = cstr(name, 'name')
510 cdef:
511 rgw_file_handle *_handler = <rgw_file_handle*>handler.handler
512 int _flags = flags
513 char* _name = name
514 with nogil:
515 ret = rgw_unlink(self.fs, _handler, _name, _flags)
516 if ret < 0:
517 raise make_ex(ret, "error in unlink")
518 return ret
519
520 def readdir(self, FileHandle dir_handler, iterate_cb, offset, flags = 0):
521 self.require_state("mounted")
522
523 cdef:
524 rgw_file_handle *_dir_handler = <rgw_file_handle*>dir_handler.handler
525 uint64_t _offset = offset
526 bool _eof
527 uint32_t _flags = flags
528 with nogil:
529 ret = rgw_readdir(self.fs, _dir_handler, &_offset, &readdir_cb,
530 <void *>iterate_cb, &_eof, _flags)
531 if ret < 0:
532 raise make_ex(ret, "error in readdir")
533
534 return (_offset, _eof)
535
536 def fstat(self, FileHandle file_handler):
537 self.require_state("mounted")
538
539 cdef:
540 rgw_file_handle *_file_handler = <rgw_file_handle*>file_handler.handler
541 stat statbuf
542
543 with nogil:
544 ret = rgw_getattr(self.fs, _file_handler, &statbuf, 0)
545 if ret < 0:
546 raise make_ex(ret, "error in getattr")
547 return StatResult(st_dev=statbuf.st_dev, st_ino=statbuf.st_ino,
548 st_mode=statbuf.st_mode, st_nlink=statbuf.st_nlink,
549 st_uid=statbuf.st_uid, st_gid=statbuf.st_gid,
550 st_rdev=statbuf.st_rdev, st_size=statbuf.st_size,
551 st_blksize=statbuf.st_blksize,
552 st_blocks=statbuf.st_blocks,
553 st_atime=datetime.fromtimestamp(statbuf.st_atime),
554 st_mtime=datetime.fromtimestamp(statbuf.st_mtime),
555 st_ctime=datetime.fromtimestamp(statbuf.st_ctime))
556
557 def opendir(self, FileHandle dir_handler, dirname, flags = 0):
558 self.require_state("mounted")
559
560 if not isinstance(flags, int):
561 raise TypeError("flags must be an integer")
562
563 dirname = cstr(dirname, 'dirname')
564
565 cdef:
566 rgw_file_handle *_dir_handler = <rgw_file_handle*>dir_handler.handler
567 rgw_file_handle *_file_handler
568 int _flags = flags
569 char* _dirname = dirname
eafe8130
TL
570 stat st
571 uint32_t st_mask = 0
7c673cae
FG
572
573 with nogil:
574 ret = rgw_lookup(self.fs, _dir_handler, _dirname,
eafe8130 575 &_file_handler, &st, st_mask, _flags)
7c673cae
FG
576 if ret < 0:
577 raise make_ex(ret, "error in open '%s'" % dirname)
578
579 file_handler = FileHandle()
580 file_handler.handler = _file_handler
581 return file_handler
582
583 def open(self, FileHandle dir_handler, filename, flags = 0):
584 self.require_state("mounted")
585
586 if not isinstance(flags, int):
587 raise TypeError("flags must be an integer")
588
589 filename = cstr(filename, 'filename')
590
591 cdef:
592 rgw_file_handle *_dir_handler = <rgw_file_handle*>dir_handler.handler
593 rgw_file_handle *_file_handler
594 int _flags = flags
595 char* _filename = filename
eafe8130
TL
596 stat st
597 uint32_t st_mask = 0
7c673cae
FG
598
599 with nogil:
600 ret = rgw_lookup(self.fs, _dir_handler, _filename,
eafe8130 601 &_file_handler, &st, st_mask, _flags)
7c673cae
FG
602 if ret < 0:
603 raise make_ex(ret, "error in open '%s'" % filename)
604 with nogil:
605 ret = rgw_open(self.fs, _file_handler, 0, _flags)
606 if ret < 0:
607 raise make_ex(ret, "error in open '%s'" % filename)
608
609 file_handler = FileHandle()
610 file_handler.handler = _file_handler
611 return file_handler
612
613 def close(self, FileHandle file_handler, flags = 0):
614 self.require_state("mounted")
615 cdef:
616 rgw_file_handle *_file_handler = <rgw_file_handle*>file_handler.handler
617 int _flags = flags
618 with nogil:
619 ret = rgw_close(self.fs, _file_handler, _flags)
620 if ret < 0:
621 raise make_ex(ret, "error in close")
622
623 def read(self, FileHandle file_handler, offset, l, flags = 0):
624 self.require_state("mounted")
625 if not isinstance(offset, int):
626 raise TypeError('offset must be an int')
627 if not isinstance(l, int):
628 raise TypeError('l must be an int')
629 cdef:
630 rgw_file_handle *_file_handler = <rgw_file_handle*>file_handler.handler
631 int64_t _offset = offset
632 size_t _length = l
633 size_t _got
634 int _flags = flags
635
636 char *ret_buf
637 PyObject* ret_s = NULL
638
639 ret_s = PyBytes_FromStringAndSize(NULL, _length)
640 try:
641 ret_buf = PyBytes_AsString(ret_s)
642 with nogil:
643 ret = rgw_read(self.fs, _file_handler, _offset, _length,
644 &_got, ret_buf, _flags)
645 if ret < 0:
646 raise make_ex(ret, "error in read")
647
648 if _got < _length:
649 _PyBytes_Resize(&ret_s, _got)
650
651 return <object>ret_s
652 finally:
653 # We DECREF unconditionally: the cast to object above will have
654 # INCREFed if necessary. This also takes care of exceptions,
655 # including if _PyString_Resize fails (that will free the string
656 # itself and set ret_s to NULL, hence XDECREF).
657 ref.Py_XDECREF(ret_s)
658
659
660 def write(self, FileHandle file_handler, offset, buf, flags = 0):
661 self.require_state("mounted")
662 if not isinstance(buf, bytes):
663 raise TypeError('buf must be a bytes')
664 if not isinstance(offset, int):
665 raise TypeError('offset must be an int')
666
667 cdef:
668 rgw_file_handle *_file_handler = <rgw_file_handle*>file_handler.handler
669 char *_data = buf
670 int64_t _offset = offset
671
672 size_t length = len(buf)
673 int _flags = flags
674 size_t _written
675
676 with nogil:
677 ret = rgw_write(self.fs, _file_handler, _offset, length, &_written,
678 _data, _flags)
679 if ret < 0:
680 raise make_ex(ret, "error in write")
681 return ret
682
683
684 def fsync(self, FileHandle handler, flags = 0):
685 self.require_state("mounted")
686
687 cdef:
688 rgw_file_handle *_file_handler = <rgw_file_handle*>handler.handler
689 int _flags = flags
690 with nogil:
691 ret = rgw_fsync(self.fs, _file_handler, _flags)
692
693 if ret < 0:
694 raise make_ex(ret, "fsync failed")