2 This module is a thin wrapper around libcephfs.
5 from cpython cimport PyObject, ref, exc
6 from libc.stdint cimport *
7 from libc.stdlib cimport malloc, realloc, free
11 include "mock_cephfs.pxi"
16 from c_cephfs cimport *
17 from rados cimport Rados
19 from collections import namedtuple
20 from datetime import datetime
24 AT_NO_ATTR_SYNC = 0x4000
25 AT_SYMLINK_NOFOLLOW = 0x100
26 cdef int AT_SYMLINK_NOFOLLOW_CDEF = AT_SYMLINK_NOFOLLOW
27 CEPH_STATX_BASIC_STATS = 0x7ff
28 cdef int CEPH_STATX_BASIC_STATS_CDEF = CEPH_STATX_BASIC_STATS
30 CEPH_STATX_NLINK = 0x2
33 CEPH_STATX_RDEV = 0x10
34 CEPH_STATX_ATIME = 0x20
35 CEPH_STATX_MTIME = 0x40
36 CEPH_STATX_CTIME = 0x80
37 CEPH_STATX_INO = 0x100
38 CEPH_STATX_SIZE = 0x200
39 CEPH_STATX_BLOCKS = 0x400
40 CEPH_STATX_BTIME = 0x800
41 CEPH_STATX_VERSION = 0x1000
43 FALLOC_FL_KEEP_SIZE = 0x01
44 FALLOC_FL_PUNCH_HOLE = 0x02
45 FALLOC_FL_NO_HIDE_STALE = 0x04
47 CEPH_SETATTR_MODE = 0x1
48 CEPH_SETATTR_UID = 0x2
49 CEPH_SETATTR_GID = 0x4
50 CEPH_SETATTR_MTIME = 0x8
51 CEPH_SETATTR_ATIME = 0x10
52 CEPH_SETATTR_SIZE = 0x20
53 CEPH_SETATTR_CTIME = 0x40
54 CEPH_SETATTR_BTIME = 0x200
58 CEPHFS_EBLOCKLISTED = 108
62 CEPHFS_ETIMEDOUT = 110
76 CEPHFS_ENAMETOOLONG = 36
82 CEPHFS_ECANCELED = 125
84 CEPHFS_EOPNOTSUPP = 95
87 CEPHFS_ENOTRECOVERABLE = 131
89 CEPHFS_EWOULDBLOCK = CEPHFS_EAGAIN
92 CEPHFS_EDEADLOCK = CEPHFS_EDEADLK
98 cdef extern from "Python.h":
99 # These are in cpython/string.pxd, but use "object" types instead of
100 # PyObject*, which invokes assumptions in cpython that we need to
101 # legitimately break to implement zero-copy string buffers in Image.read().
102 # This is valid use of the Python API and documented as a special case.
103 PyObject *PyBytes_FromStringAndSize(char *v, Py_ssize_t len) except NULL
104 char* PyBytes_AsString(PyObject *string) except NULL
105 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
106 void PyEval_InitThreads()
109 class Error(Exception):
110 def get_error_code(self):
114 class LibCephFSStateError(Error):
118 class OSError(Error):
119 def __init__(self, errno, strerror):
120 super(OSError, self).__init__(errno, strerror)
122 self.strerror = "%s: %s" % (strerror, os.strerror(errno))
125 return '{} [Errno {}]'.format(self.strerror, self.errno)
127 def get_error_code(self):
131 class PermissionError(OSError):
135 class ObjectNotFound(OSError):
139 class NoData(OSError):
143 class ObjectExists(OSError):
147 class IOError(OSError):
151 class NoSpace(OSError):
155 class InvalidValue(OSError):
159 class OperationNotSupported(OSError):
163 class WouldBlock(OSError):
167 class OutOfRange(OSError):
171 class ObjectNotEmpty(OSError):
174 class NotDirectory(OSError):
177 class DiskQuotaExceeded(OSError):
181 cdef errno_to_exception = {
182 CEPHFS_EPERM : PermissionError,
183 CEPHFS_ENOENT : ObjectNotFound,
184 CEPHFS_EIO : IOError,
185 CEPHFS_ENOSPC : NoSpace,
186 CEPHFS_EEXIST : ObjectExists,
187 CEPHFS_ENODATA : NoData,
188 CEPHFS_EINVAL : InvalidValue,
189 CEPHFS_EOPNOTSUPP : OperationNotSupported,
190 CEPHFS_ERANGE : OutOfRange,
191 CEPHFS_EWOULDBLOCK: WouldBlock,
192 CEPHFS_ENOTEMPTY : ObjectNotEmpty,
193 CEPHFS_ENOTDIR : NotDirectory,
194 CEPHFS_EDQUOT : DiskQuotaExceeded,
198 cdef make_ex(ret, msg):
200 Translate a libcephfs return code into an exception.
202 :param ret: the return code
204 :param msg: the error message to use
206 :returns: a subclass of :class:`Error`
209 if ret in errno_to_exception:
210 return errno_to_exception[ret](ret, msg)
212 return OSError(ret, msg)
215 class DirEntry(namedtuple('DirEntry',
216 ['d_ino', 'd_off', 'd_reclen', 'd_type', 'd_name'])):
221 return self.d_type == self.DT_DIR
223 def is_symbol_file(self):
224 return self.d_type == self.DT_LNK
227 return self.d_type == self.DT_REG
229 StatResult = namedtuple('StatResult',
230 ["st_dev", "st_ino", "st_mode", "st_nlink", "st_uid",
231 "st_gid", "st_rdev", "st_size", "st_blksize",
232 "st_blocks", "st_atime", "st_mtime", "st_ctime"])
234 cdef class DirResult(object):
236 cdef ceph_dir_result* handle
238 # Bug in older Cython instances prevents this from being a static method.
240 # cdef create(LibCephFS lib, ceph_dir_result* handle):
246 def __dealloc__(self):
251 raise make_ex(CEPHFS_EBADF, "dir is not open")
252 self.lib.require_state("mounted")
254 ceph_rewinddir(self.lib.cluster, self.handle)
257 def __exit__(self, type_, value, traceback):
262 self.lib.require_state("mounted")
265 dirent = ceph_readdir(self.lib.cluster, self.handle)
269 IF UNAME_SYSNAME == "FreeBSD" or UNAME_SYSNAME == "Darwin":
270 return DirEntry(d_ino=dirent.d_ino,
272 d_reclen=dirent.d_reclen,
273 d_type=dirent.d_type,
274 d_name=dirent.d_name)
276 return DirEntry(d_ino=dirent.d_ino,
278 d_reclen=dirent.d_reclen,
279 d_type=dirent.d_type,
280 d_name=dirent.d_name)
284 self.lib.require_state("mounted")
286 ret = ceph_closedir(self.lib.cluster, self.handle)
288 raise make_ex(ret, "closedir failed")
293 raise make_ex(CEPHFS_EBADF, "dir is not open")
294 self.lib.require_state("mounted")
296 ceph_rewinddir(self.lib.cluster, self.handle)
300 raise make_ex(CEPHFS_EBADF, "dir is not open")
301 self.lib.require_state("mounted")
303 ret = ceph_telldir(self.lib.cluster, self.handle)
305 raise make_ex(ret, "telldir failed")
308 def seekdir(self, offset):
310 raise make_ex(CEPHFS_EBADF, "dir is not open")
311 if not isinstance(offset, int):
312 raise TypeError('offset must be an int')
313 self.lib.require_state("mounted")
314 cdef int64_t _offset = offset
316 ceph_seekdir(self.lib.cluster, self.handle, _offset)
319 def cstr(val, name, encoding="utf-8", opt=False):
321 Create a byte string from a Python string
323 :param basestring val: Python string
324 :param str name: Name of the string parameter, for exceptions
325 :param str encoding: Encoding to use
326 :param bool opt: If True, None is allowed
328 :raises: :class:`InvalidArgument`
330 if opt and val is None:
332 if isinstance(val, bytes):
336 v = val.encode(encoding)
338 raise TypeError('%s must be encodeable as a bytearray' % name)
339 assert isinstance(v, bytes)
342 def cstr_list(list_str, name, encoding="utf-8"):
343 return [cstr(s, name) for s in list_str]
346 def decode_cstr(val, encoding="utf-8"):
348 Decode a byte string into a Python string.
350 :param bytes val: byte string
356 return val.decode(encoding)
358 cdef timeval to_timeval(t):
360 return timeval equivalent from time
363 cdef timeval buf = timeval(tt, (t - tt) * 1000000)
366 cdef timespec to_timespec(t):
368 return timespec equivalent from time
371 cdef timespec buf = timespec(tt, (t - tt) * 1000000000)
374 cdef char* opt_str(s) except? NULL:
380 cdef char ** to_bytes_array(list_bytes):
381 cdef char **ret = <char **>malloc(len(list_bytes) * sizeof(char *))
383 raise MemoryError("malloc failed")
384 for i in range(len(list_bytes)):
385 ret[i] = <char *>list_bytes[i]
389 cdef void* realloc_chk(void* ptr, size_t size) except NULL:
390 cdef void *ret = realloc(ptr, size)
392 raise MemoryError("realloc failed")
396 cdef iovec * to_iovec(buffers) except NULL:
397 cdef iovec *iov = <iovec *>malloc(len(buffers) * sizeof(iovec))
400 raise MemoryError("malloc failed")
401 for i in xrange(len(buffers)):
402 s = <char*>buffers[i]
403 iov[i] = [<void*>s, len(buffers[i])]
407 cdef class LibCephFS(object):
408 """libcephfs python wrapper"""
410 cdef public object state
411 cdef ceph_mount_info *cluster
413 def require_state(self, *args):
414 if self.state in args:
416 raise LibCephFSStateError("You cannot perform that operation on a "
417 "CephFS object in state %s." % (self.state))
419 def __cinit__(self, conf=None, conffile=None, auth_id=None, rados_inst=None):
420 """Create a libcephfs wrapper
422 :param conf dict opt: settings overriding the default ones and conffile
423 :param conffile str opt: the path to ceph.conf to override the default settings
424 :auth_id str opt: the id used to authenticate the client entity
425 :rados_inst Rados opt: a rados.Rados instance
428 self.state = "uninitialized"
429 if rados_inst is not None:
430 if auth_id is not None or conffile is not None or conf is not None:
431 raise make_ex(CEPHFS_EINVAL,
432 "May not pass RADOS instance as well as other configuration")
434 self.create_with_rados(rados_inst)
436 self.create(conf, conffile, auth_id)
438 def create_with_rados(self, Rados rados_inst):
441 ret = ceph_create_from_rados(&self.cluster, rados_inst.cluster)
443 raise Error("libcephfs_initialize failed with error code: %d" % ret)
444 self.state = "configuring"
447 "special value that indicates no conffile should be read when creating a mount handle"
448 DEFAULT_CONF_FILES = -2
449 "special value that indicates the default conffiles should be read when creating a mount handle"
451 def create(self, conf=None, conffile=NO_CONF_FILE, auth_id=None):
453 Create a mount handle for interacting with Ceph. All libcephfs
454 functions operate on a mount info handle.
456 :param conf dict opt: settings overriding the default ones and conffile
457 :param conffile Union[int,str], optional: the path to ceph.conf to override the default settings
458 :auth_id str opt: the id used to authenticate the client entity
460 if conf is not None and not isinstance(conf, dict):
461 raise TypeError("conf must be dict or None")
462 cstr(conffile, 'configfile', opt=True)
463 auth_id = cstr(auth_id, 'auth_id', opt=True)
466 char* _auth_id = opt_str(auth_id)
470 ret = ceph_create(&self.cluster, <const char*>_auth_id)
472 raise Error("libcephfs_initialize failed with error code: %d" % ret)
474 self.state = "configuring"
475 if conffile in (self.NO_CONF_FILE, None):
477 elif conffile in (self.DEFAULT_CONF_FILES, ''):
478 self.conf_read_file(None)
480 self.conf_read_file(conffile)
482 for key, value in conf.items():
483 self.conf_set(key, value)
487 Get associated client addresses with this RADOS session.
489 self.require_state("mounted")
497 ret = ceph_getaddrs(self.cluster, &addrs)
499 raise make_ex(ret, "error calling getaddrs")
501 return decode_cstr(addrs)
503 ceph_buffer_free(addrs)
506 def conf_read_file(self, conffile=None):
508 Load the ceph configuration from the specified config file.
510 :param conffile str opt: the path to ceph.conf to override the default settings
512 conffile = cstr(conffile, 'conffile', opt=True)
514 char *_conffile = opt_str(conffile)
516 ret = ceph_conf_read_file(self.cluster, <const char*>_conffile)
518 raise make_ex(ret, "error calling conf_read_file")
520 def conf_parse_argv(self, argv):
522 Parse the command line arguments and load the configuration parameters.
524 :param argv: the argument list
526 self.require_state("configuring")
527 cargv = cstr_list(argv, 'argv')
529 int _argc = len(argv)
530 char **_argv = to_bytes_array(cargv)
534 ret = ceph_conf_parse_argv(self.cluster, _argc,
535 <const char **>_argv)
537 raise make_ex(ret, "error calling conf_parse_argv")
543 Unmount and destroy the ceph mount handle.
545 if self.state in ["initialized", "mounted"]:
547 ceph_shutdown(self.cluster)
548 self.state = "shutdown"
554 def __exit__(self, type_, value, traceback):
558 def __dealloc__(self):
563 Get the version number of the ``libcephfs`` C library.
565 :returns: a tuple of ``(major, minor, extra)`` components of the
573 ceph_version(&major, &minor, &extra)
574 return (major, minor, extra)
576 def conf_get(self, option):
578 Gets the configuration value as a string.
580 :param option: the config option to get
582 self.require_state("configuring", "initialized", "mounted")
584 option = cstr(option, 'option')
586 char *_option = option
592 ret_buf = <char *>realloc_chk(ret_buf, length)
594 ret = ceph_conf_get(self.cluster, _option, ret_buf, length)
596 return decode_cstr(ret_buf)
597 elif ret == -CEPHFS_ENAMETOOLONG:
599 elif ret == -CEPHFS_ENOENT:
602 raise make_ex(ret, "error calling conf_get")
606 def conf_set(self, option, val):
608 Sets a configuration value from a string.
610 :param option: the configuration option to set
611 :param value: the value of the configuration option to set
613 self.require_state("configuring", "initialized", "mounted")
615 option = cstr(option, 'option')
616 val = cstr(val, 'val')
618 char *_option = option
622 ret = ceph_conf_set(self.cluster, _option, _val)
624 raise make_ex(ret, "error calling conf_set")
628 Initialize the filesystem client (but do not mount the filesystem yet)
630 self.require_state("configuring")
632 ret = ceph_init(self.cluster)
634 raise make_ex(ret, "error calling ceph_init")
635 self.state = "initialized"
637 def mount(self, mount_root=None, filesystem_name=None):
639 Perform a mount using the path for the root of the mount.
641 if self.state == "configuring":
643 self.require_state("initialized")
645 # Configure which filesystem to mount if one was specified
646 if filesystem_name is None:
647 filesystem_name = b""
649 filesystem_name = cstr(filesystem_name, 'filesystem_name')
651 char *_filesystem_name = filesystem_name
654 ret = ceph_select_filesystem(self.cluster,
657 raise make_ex(ret, "error calling ceph_select_filesystem")
659 # Prepare mount_root argument, default to "/"
660 root = b"/" if mount_root is None else mount_root
662 char *_mount_root = root
665 ret = ceph_mount(self.cluster, _mount_root)
667 raise make_ex(ret, "error calling ceph_mount")
668 self.state = "mounted"
672 Unmount a mount handle.
674 self.require_state("mounted")
676 ret = ceph_unmount(self.cluster)
678 raise make_ex(ret, "error calling ceph_unmount")
679 self.state = "initialized"
681 def abort_conn(self):
683 Abort mds connections.
685 self.require_state("mounted")
687 ret = ceph_abort_conn(self.cluster)
689 raise make_ex(ret, "error calling ceph_abort_conn")
690 self.state = "initialized"
692 def get_instance_id(self):
694 Get a global id for current instance
696 self.require_state("initialized", "mounted")
698 ret = ceph_get_instance_id(self.cluster)
701 def statfs(self, path):
703 Perform a statfs on the ceph file system. This call fills in file system wide statistics
704 into the passed in buffer.
706 :param path: any path within the mounted filesystem
708 self.require_state("mounted")
709 path = cstr(path, 'path')
715 ret = ceph_statfs(self.cluster, _path, &statbuf)
717 raise make_ex(ret, "statfs failed: %s" % path)
718 return {'f_bsize': statbuf.f_bsize,
719 'f_frsize': statbuf.f_frsize,
720 'f_blocks': statbuf.f_blocks,
721 'f_bfree': statbuf.f_bfree,
722 'f_bavail': statbuf.f_bavail,
723 'f_files': statbuf.f_files,
724 'f_ffree': statbuf.f_ffree,
725 'f_favail': statbuf.f_favail,
726 'f_fsid': statbuf.f_fsid,
727 'f_flag': statbuf.f_flag,
728 'f_namemax': statbuf.f_namemax}
732 Synchronize all filesystem data to persistent media
734 self.require_state("mounted")
736 ret = ceph_sync_fs(self.cluster)
738 raise make_ex(ret, "sync_fs failed")
740 def fsync(self, int fd, int syncdataonly):
742 Synchronize an open file to persistent media.
744 :param fd: the file descriptor of the file to sync.
745 :param syncdataonly: a boolean whether to synchronize metadata and data (0)
748 self.require_state("mounted")
750 ret = ceph_fsync(self.cluster, fd, syncdataonly)
752 raise make_ex(ret, "fsync failed")
754 def lazyio(self, fd, enable):
756 Enable/disable lazyio for the file.
758 :param fd: the file descriptor of the file for which to enable lazio.
759 :param enable: a boolean to enable lazyio or disable lazyio.
762 self.require_state("mounted")
763 if not isinstance(fd, int):
764 raise TypeError('fd must be an int')
765 if not isinstance(enable, int):
766 raise TypeError('enable must be an int')
773 ret = ceph_lazyio(self.cluster, _fd, _enable)
775 raise make_ex(ret, "lazyio failed")
777 def lazyio_propagate(self, fd, offset, count):
779 Flushes the write buffer for the file thereby propogating the buffered write to the file.
781 :param fd: the file descriptor of the file to sync.
782 :param offset: the byte range starting.
783 :param count: the number of bytes starting from offset.
786 self.require_state("mounted")
787 if not isinstance(fd, int):
788 raise TypeError('fd must be an int')
789 if not isinstance(offset, int):
790 raise TypeError('offset must be an int')
791 if not isinstance(count, int):
792 raise TypeError('count must be an int')
796 int64_t _offset = offset
797 size_t _count = count
800 ret = ceph_lazyio_propagate(self.cluster, _fd, _offset, _count)
802 raise make_ex(ret, "lazyio_propagate failed")
804 def lazyio_synchronize(self, fd, offset, count):
806 Flushes the write buffer for the file and invalidate the read cache. This allows a
807 subsequent read operation to read and cache data directly from the file and hence
808 everyone's propagated writes would be visible.
810 :param fd: the file descriptor of the file to sync.
811 :param offset: the byte range starting.
812 :param count: the number of bytes starting from offset.
815 self.require_state("mounted")
816 if not isinstance(fd, int):
817 raise TypeError('fd must be an int')
818 if not isinstance(offset, int):
819 raise TypeError('offset must be an int')
820 if not isinstance(count, int):
821 raise TypeError('count must be an int')
825 int64_t _offset = offset
826 size_t _count = count
829 ret = ceph_lazyio_synchronize(self.cluster, _fd, _offset, _count)
831 raise make_ex(ret, "lazyio_synchronize failed")
833 def fallocate(self, fd, offset, length, mode=0):
835 Preallocate or release disk space for the file for the byte range.
837 :param fd: the file descriptor of the file to fallocate.
838 :param mode: the flags determines the operation to be performed on the given
839 range. default operation (0) allocate and initialize to zero
840 the file in the byte range, and the file size will be changed
841 if offset + length is greater than the file size. if the
842 FALLOC_FL_KEEP_SIZE flag is specified in the mode, the file size
843 will not be changed. if the FALLOC_FL_PUNCH_HOLE flag is specified
844 in the mode, the operation is deallocate space and zero the byte range.
845 :param offset: the byte range starting.
846 :param length: the length of the range.
849 self.require_state("mounted")
850 if not isinstance(fd, int):
851 raise TypeError('fd must be an int')
852 if not isinstance(mode, int):
853 raise TypeError('mode must be an int')
854 if not isinstance(offset, int):
855 raise TypeError('offset must be an int')
856 if not isinstance(length, int):
857 raise TypeError('length must be an int')
862 int64_t _offset = offset
863 int64_t _length = length
866 ret = ceph_fallocate(self.cluster, _fd, _mode, _offset, _length)
868 raise make_ex(ret, "fallocate failed")
872 Get the current working directory.
874 :rtype the path to the current working directory
876 self.require_state("mounted")
878 ret = ceph_getcwd(self.cluster)
881 def chdir(self, path):
883 Change the current working directory.
885 :param path the path to the working directory to change into.
887 self.require_state("mounted")
889 path = cstr(path, 'path')
890 cdef char* _path = path
892 ret = ceph_chdir(self.cluster, _path)
894 raise make_ex(ret, "chdir failed")
896 def opendir(self, path):
898 Open the given directory.
900 :param path: the path name of the directory to open. Must be either an absolute path
901 or a path relative to the current working directory.
902 :rtype handle: the open directory stream handle
904 self.require_state("mounted")
906 path = cstr(path, 'path')
909 ceph_dir_result* handle
911 ret = ceph_opendir(self.cluster, _path, &handle);
913 raise make_ex(ret, "opendir failed")
919 def readdir(self, DirResult handle):
921 Get the next entry in an open directory.
923 :param handle: the open directory stream handle
924 :rtype dentry: the next directory entry or None if at the end of the
925 directory (or the directory is empty. This pointer
926 should not be freed by the caller, and is only safe to
927 access between return and the next call to readdir or
930 self.require_state("mounted")
932 return handle.readdir()
934 def closedir(self, DirResult handle):
936 Close the open directory.
938 :param handle: the open directory stream handle
940 self.require_state("mounted")
942 return handle.close()
944 def rewinddir(self, DirResult handle):
946 Rewind the directory stream to the beginning of the directory.
948 :param handle: the open directory stream handle
950 return handle.rewinddir()
952 def telldir(self, DirResult handle):
954 Get the current position of a directory stream.
956 :param handle: the open directory stream handle
957 :return value: The position of the directory stream. Note that the offsets
958 returned by ceph_telldir do not have a particular order (cannot
959 be compared with inequality).
961 return handle.telldir()
963 def seekdir(self, DirResult handle, offset):
965 Move the directory stream to a position specified by the given offset.
967 :param handle: the open directory stream handle
968 :param offset: the position to move the directory stream to. This offset should be
969 a value returned by telldir. Note that this value does not refer to
970 the nth entry in a directory, and can not be manipulated with plus
973 return handle.seekdir(offset)
975 def mkdir(self, path, mode):
979 :param path: the path of the directory to create. This must be either an
980 absolute path or a relative path off of the current working directory.
981 :param mode: the permissions the directory should have once created.
984 self.require_state("mounted")
985 path = cstr(path, 'path')
986 if not isinstance(mode, int):
987 raise TypeError('mode must be an int')
992 ret = ceph_mkdir(self.cluster, _path, _mode)
994 raise make_ex(ret, "error in mkdir {}".format(path.decode('utf-8')))
996 def mksnap(self, path, name, mode, metadata={}):
1000 :param path: path of the directory to snapshot.
1001 :param name: snapshot name
1002 :param mode: permission of the snapshot
1003 :param metadata: metadata key/value to store with the snapshot
1005 :raises: :class: `TypeError`
1006 :raises: :class: `Error`
1007 :returns: int: 0 on success
1010 self.require_state("mounted")
1011 path = cstr(path, 'path')
1012 name = cstr(name, 'name')
1013 if not isinstance(mode, int):
1014 raise TypeError('mode must be an int')
1015 if not isinstance(metadata, dict):
1016 raise TypeError('metadata must be an dictionary')
1018 for key, value in metadata.items():
1019 if not isinstance(key, str) or not isinstance(value, str):
1020 raise TypeError('metadata key and values should be strings')
1021 md[key.encode('utf-8')] = value.encode('utf-8')
1027 snap_metadata *_snap_meta = <snap_metadata *>malloc(nr * sizeof(snap_metadata))
1028 if nr and _snap_meta == NULL:
1029 raise MemoryError("malloc failed")
1031 for key, value in md.items():
1032 _snap_meta[i] = snap_metadata(<char*>key, <char*>value)
1035 ret = ceph_mksnap(self.cluster, _path, _name, _mode, _snap_meta, nr)
1038 raise make_ex(ret, "mksnap error")
1041 def rmsnap(self, path, name):
1045 :param path: path of the directory for removing snapshot
1046 :param name: snapshot name
1048 :raises: :class: `Error`
1049 :returns: int: 0 on success
1051 self.require_state("mounted")
1052 path = cstr(path, 'path')
1053 name = cstr(name, 'name')
1057 ret = ceph_rmsnap(self.cluster, _path, _name)
1059 raise make_ex(ret, "rmsnap error")
1062 def snap_info(self, path):
1066 :param path: snapshot path
1068 :raises: :class: `Error`
1069 :returns: dict: snapshot metadata
1071 self.require_state("mounted")
1072 path = cstr(path, 'path')
1076 ret = ceph_get_snap_info(self.cluster, _path, &info)
1078 raise make_ex(ret, "snap_info error")
1080 if info.nr_snap_metadata:
1081 md = {snap_meta.key.decode('utf-8'): snap_meta.value.decode('utf-8') for snap_meta in
1082 info.snap_metadata[:info.nr_snap_metadata]}
1083 ceph_free_snap_info_buffer(&info)
1084 return {'id': info.id, 'metadata': md}
1086 def chmod(self, path, mode) :
1088 Change directory mode.
1090 :param path: the path of the directory to create. This must be either an
1091 absolute path or a relative path off of the current working directory.
1092 :param mode: the permissions the directory should have once created.
1094 self.require_state("mounted")
1095 path = cstr(path, 'path')
1096 if not isinstance(mode, int):
1097 raise TypeError('mode must be an int')
1102 ret = ceph_chmod(self.cluster, _path, _mode)
1104 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1106 def lchmod(self, path, mode) -> None:
1108 Change file mode. If the path is a symbolic link, it won't be dereferenced.
1110 :param path: the path of the file. This must be either an absolute path or
1111 a relative path off of the current working directory.
1112 :param mode: the permissions to be set .
1114 self.require_state("mounted")
1115 path = cstr(path, 'path')
1116 if not isinstance(mode, int):
1117 raise TypeError('mode must be an int')
1122 ret = ceph_lchmod(self.cluster, _path, _mode)
1124 raise make_ex(ret, "error in chmod {}".format(path.decode('utf-8')))
1126 def fchmod(self, fd, mode) :
1128 Change file mode based on fd.
1129 :param fd: the file descriptor of the file to change file mode
1130 :param mode: the permissions to be set.
1132 self.require_state("mounted")
1133 if not isinstance(fd, int):
1134 raise TypeError('fd must be an int')
1135 if not isinstance(mode, int):
1136 raise TypeError('mode must be an int')
1141 ret = ceph_fchmod(self.cluster, _fd, _mode)
1143 raise make_ex(ret, "error in fchmod")
1145 def chown(self, path, uid, gid, follow_symlink=True):
1147 Change directory ownership
1149 :param path: the path of the directory to change.
1150 :param uid: the uid to set
1151 :param gid: the gid to set
1152 :param follow_symlink: perform the operation on the target file if @path
1153 is a symbolic link (default)
1155 self.require_state("mounted")
1156 path = cstr(path, 'path')
1157 if not isinstance(uid, int):
1158 raise TypeError('uid must be an int')
1159 elif not isinstance(gid, int):
1160 raise TypeError('gid must be an int')
1168 ret = ceph_chown(self.cluster, _path, _uid, _gid)
1171 ret = ceph_lchown(self.cluster, _path, _uid, _gid)
1173 raise make_ex(ret, "error in chown {}".format(path.decode('utf-8')))
1175 def lchown(self, path, uid, gid):
1177 Change ownership of a symbolic link
1178 :param path: the path of the symbolic link to change.
1179 :param uid: the uid to set
1180 :param gid: the gid to set
1182 self.chown(path, uid, gid, follow_symlink=False)
1184 def fchown(self, fd, uid, gid):
1186 Change file ownership
1187 :param fd: the file descriptor of the file to change ownership
1188 :param uid: the uid to set
1189 :param gid: the gid to set
1191 self.require_state("mounted")
1192 if not isinstance(fd, int):
1193 raise TypeError('fd must be an int')
1194 if not isinstance(uid, int):
1195 raise TypeError('uid must be an int')
1196 elif not isinstance(gid, int):
1197 raise TypeError('gid must be an int')
1204 ret = ceph_fchown(self.cluster, _fd, _uid, _gid)
1206 raise make_ex(ret, "error in fchown")
1208 def mkdirs(self, path, mode):
1210 Create multiple directories at once.
1212 :param path: the full path of directories and sub-directories that should
1214 :param mode: the permissions the directory should have once created
1216 self.require_state("mounted")
1217 path = cstr(path, 'path')
1218 if not isinstance(mode, int):
1219 raise TypeError('mode must be an int')
1225 ret = ceph_mkdirs(self.cluster, _path, _mode)
1227 raise make_ex(ret, "error in mkdirs {}".format(path.decode('utf-8')))
1229 def rmdir(self, path):
1233 :param path: the path of the directory to remove.
1235 self.require_state("mounted")
1236 path = cstr(path, 'path')
1237 cdef char* _path = path
1238 ret = ceph_rmdir(self.cluster, _path)
1240 raise make_ex(ret, "error in rmdir {}".format(path.decode('utf-8')))
1242 def open(self, path, flags, mode=0):
1244 Create and/or open a file.
1246 :param path: the path of the file to open. If the flags parameter includes O_CREAT,
1247 the file will first be created before opening.
1248 :param flags: set of option masks that control how the file is created/opened.
1249 :param mode: the permissions to place on the file if the file does not exist and O_CREAT
1250 is specified in the flags.
1252 self.require_state("mounted")
1253 path = cstr(path, 'path')
1255 if not isinstance(mode, int):
1256 raise TypeError('mode must be an int')
1257 if isinstance(flags, str):
1260 cephfs_flags = os.O_RDONLY
1268 cephfs_flags |= os.O_TRUNC | os.O_CREAT
1269 elif access_flags > 0 and c == '+':
1272 raise make_ex(CEPHFS_EOPNOTSUPP,
1273 "open flags doesn't support %s" % c)
1275 if access_flags == 1:
1276 cephfs_flags |= os.O_RDONLY;
1277 elif access_flags == 2:
1278 cephfs_flags |= os.O_WRONLY;
1280 cephfs_flags |= os.O_RDWR;
1282 elif isinstance(flags, int):
1283 cephfs_flags = flags
1285 raise TypeError("flags must be a string or an integer")
1289 int _flags = cephfs_flags
1293 ret = ceph_open(self.cluster, _path, _flags, _mode)
1295 raise make_ex(ret, "error in open {}".format(path.decode('utf-8')))
1298 def close(self, fd):
1300 Close the open file.
1302 :param fd: the file descriptor referring to the open file.
1305 self.require_state("mounted")
1306 if not isinstance(fd, int):
1307 raise TypeError('fd must be an int')
1310 ret = ceph_close(self.cluster, _fd)
1312 raise make_ex(ret, "error in close")
1314 def read(self, fd, offset, l):
1316 Read data from the file.
1318 :param fd: the file descriptor of the open file to read from.
1319 :param offset: the offset in the file to read from. If this value is negative, the
1320 function reads from the current offset of the file descriptor.
1321 :param l: the flag to indicate what type of seeking to perform
1323 self.require_state("mounted")
1324 if not isinstance(offset, int):
1325 raise TypeError('offset must be an int')
1326 if not isinstance(l, int):
1327 raise TypeError('l must be an int')
1328 if not isinstance(fd, int):
1329 raise TypeError('fd must be an int')
1332 int64_t _offset = offset
1336 PyObject* ret_s = NULL
1338 ret_s = PyBytes_FromStringAndSize(NULL, _length)
1340 ret_buf = PyBytes_AsString(ret_s)
1342 ret = ceph_read(self.cluster, _fd, ret_buf, _length, _offset)
1344 raise make_ex(ret, "error in read")
1347 _PyBytes_Resize(&ret_s, ret)
1349 return <object>ret_s
1351 # We DECREF unconditionally: the cast to object above will have
1352 # INCREFed if necessary. This also takes care of exceptions,
1353 # including if _PyString_Resize fails (that will free the string
1354 # itself and set ret_s to NULL, hence XDECREF).
1355 ref.Py_XDECREF(ret_s)
1357 def preadv(self, fd, buffers, offset):
1359 Write data to a file.
1361 :param fd: the file descriptor of the open file to read from
1362 :param buffers: the list of byte object to read from the file
1363 :param offset: the offset of the file read from. If this value is negative, the
1364 function reads from the current offset of the file descriptor.
1366 self.require_state("mounted")
1367 if not isinstance(fd, int):
1368 raise TypeError('fd must be an int')
1369 if not isinstance(buffers, list):
1370 raise TypeError('buffers must be a list')
1372 if not isinstance(buf, bytearray):
1373 raise TypeError('buffers must be a list of bytes')
1374 if not isinstance(offset, int):
1375 raise TypeError('offset must be an int')
1379 int _iovcnt = len(buffers)
1380 int64_t _offset = offset
1381 iovec *_iov = to_iovec(buffers)
1384 ret = ceph_preadv(self.cluster, _fd, _iov, _iovcnt, _offset)
1386 raise make_ex(ret, "error in preadv")
1391 def write(self, fd, buf, offset):
1393 Write data to a file.
1395 :param fd: the file descriptor of the open file to write to
1396 :param buf: the bytes to write to the file
1397 :param offset: the offset of the file write into. If this value is negative, the
1398 function writes to the current offset of the file descriptor.
1400 self.require_state("mounted")
1401 if not isinstance(fd, int):
1402 raise TypeError('fd must be an int')
1403 if not isinstance(buf, bytes):
1404 raise TypeError('buf must be a bytes')
1405 if not isinstance(offset, int):
1406 raise TypeError('offset must be an int')
1411 int64_t _offset = offset
1413 size_t length = len(buf)
1416 ret = ceph_write(self.cluster, _fd, _data, length, _offset)
1418 raise make_ex(ret, "error in write")
1421 def pwritev(self, fd, buffers, offset):
1423 Write data to a file.
1425 :param fd: the file descriptor of the open file to write to
1426 :param buffers: the list of byte object to write to the file
1427 :param offset: the offset of the file write into. If this value is negative, the
1428 function writes to the current offset of the file descriptor.
1430 self.require_state("mounted")
1431 if not isinstance(fd, int):
1432 raise TypeError('fd must be an int')
1433 if not isinstance(buffers, list):
1434 raise TypeError('buffers must be a list')
1436 if not isinstance(buf, bytes):
1437 raise TypeError('buffers must be a list of bytes')
1438 if not isinstance(offset, int):
1439 raise TypeError('offset must be an int')
1443 int _iovcnt = len(buffers)
1444 int64_t _offset = offset
1445 iovec *_iov = to_iovec(buffers)
1448 ret = ceph_pwritev(self.cluster, _fd, _iov, _iovcnt, _offset)
1450 raise make_ex(ret, "error in pwritev")
1455 def flock(self, fd, operation, owner):
1457 Apply or remove an advisory lock.
1459 :param fd: the open file descriptor to change advisory lock.
1460 :param operation: the advisory lock operation to be performed on the file
1461 :param owner: the user-supplied owner identifier (an arbitrary integer)
1463 self.require_state("mounted")
1464 if not isinstance(fd, int):
1465 raise TypeError('fd must be an int')
1466 if not isinstance(operation, int):
1467 raise TypeError('operation must be an int')
1472 uint64_t _owner = owner
1475 ret = ceph_flock(self.cluster, _fd, _op, _owner)
1477 raise make_ex(ret, "error in write")
1480 def truncate(self, path, size):
1482 Truncate the file to the given size. If this operation causes the
1483 file to expand, the empty bytes will be filled in with zeros.
1485 :param path: the path to the file to truncate.
1486 :param size: the new size of the file.
1489 if not isinstance(size, int):
1490 raise TypeError('size must be a int')
1493 statx_dict["size"] = size
1494 self.setattrx(path, statx_dict, CEPH_SETATTR_SIZE, AT_SYMLINK_NOFOLLOW)
1496 def ftruncate(self, fd, size):
1498 Truncate the file to the given size. If this operation causes the
1499 file to expand, the empty bytes will be filled in with zeros.
1501 :param path: the path to the file to truncate.
1502 :param size: the new size of the file.
1505 if not isinstance(size, int):
1506 raise TypeError('size must be a int')
1509 statx_dict["size"] = size
1510 self.fsetattrx(fd, statx_dict, CEPH_SETATTR_SIZE)
1512 def mknod(self, path, mode, rdev=0):
1514 Make a block or character special file.
1516 :param path: the path to the special file.
1517 :param mode: the permissions to use and the type of special file. The type can be
1518 one of stat.S_IFREG, stat.S_IFCHR, stat.S_IFBLK, stat.S_IFIFO. Both
1519 should be combined using bitwise OR.
1520 :param rdev: If the file type is stat.S_IFCHR or stat.S_IFBLK then this parameter
1521 specifies the major and minor numbers of the newly created device
1522 special file. Otherwise, it is ignored.
1524 self.require_state("mounted")
1525 path = cstr(path, 'path')
1527 if not isinstance(mode, int):
1528 raise TypeError('mode must be an int')
1529 if not isinstance(rdev, int):
1530 raise TypeError('rdev must be an int')
1538 ret = ceph_mknod(self.cluster, _path, _mode, _rdev)
1540 raise make_ex(ret, "error in mknod {}".format(path.decode('utf-8')))
1542 def getxattr(self, path, name, size=255, follow_symlink=True):
1544 Get an extended attribute.
1546 :param path: the path to the file
1547 :param name: the name of the extended attribute to get
1548 :param size: the size of the pre-allocated buffer
1550 self.require_state("mounted")
1552 path = cstr(path, 'path')
1553 name = cstr(name, 'name')
1559 size_t ret_length = size
1560 char *ret_buf = NULL
1563 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1566 ret = ceph_getxattr(self.cluster, _path, _name, ret_buf,
1570 ret = ceph_lgetxattr(self.cluster, _path, _name, ret_buf,
1574 raise make_ex(ret, "error in getxattr")
1576 return ret_buf[:ret]
1580 def fgetxattr(self, fd, name, size=255):
1582 Get an extended attribute given the fd of a file.
1584 :param fd: the open file descriptor referring to the file
1585 :param name: the name of the extended attribute to get
1586 :param size: the size of the pre-allocated buffer
1588 self.require_state("mounted")
1590 if not isinstance(fd, int):
1591 raise TypeError('fd must be an int')
1592 name = cstr(name, 'name')
1598 size_t ret_length = size
1599 char *ret_buf = NULL
1602 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1604 ret = ceph_fgetxattr(self.cluster, _fd, _name, ret_buf,
1608 raise make_ex(ret, "error in fgetxattr")
1610 return ret_buf[:ret]
1614 def lgetxattr(self, path, name, size=255):
1616 Get an extended attribute without following symbolic links. This
1617 function is identical to ceph_getxattr, but if the path refers to
1618 a symbolic link, we get the extended attributes of the symlink
1619 rather than the attributes of the file it points to.
1621 :param path: the path to the file
1622 :param name: the name of the extended attribute to get
1623 :param size: the size of the pre-allocated buffer
1626 return self.getxattr(path, name, size=size, follow_symlink=False)
1628 def setxattr(self, path, name, value, flags, follow_symlink=True):
1630 Set an extended attribute on a file.
1632 :param path: the path to the file.
1633 :param name: the name of the extended attribute to set.
1634 :param value: the bytes of the extended attribute value
1636 self.require_state("mounted")
1638 name = cstr(name, 'name')
1639 path = cstr(path, 'path')
1640 if not isinstance(flags, int):
1641 raise TypeError('flags must be a int')
1642 if not isinstance(value, bytes):
1643 raise TypeError('value must be a bytes')
1648 char *_value = value
1649 size_t _value_len = len(value)
1654 ret = ceph_setxattr(self.cluster, _path, _name,
1655 _value, _value_len, _flags)
1658 ret = ceph_lsetxattr(self.cluster, _path, _name,
1659 _value, _value_len, _flags)
1662 raise make_ex(ret, "error in setxattr")
1664 def fsetxattr(self, fd, name, value, flags):
1666 Set an extended attribute on a file.
1668 :param fd: the open file descriptor referring to the file.
1669 :param name: the name of the extended attribute to set.
1670 :param value: the bytes of the extended attribute value
1672 self.require_state("mounted")
1674 name = cstr(name, 'name')
1675 if not isinstance(fd, int):
1676 raise TypeError('fd must be an int')
1677 if not isinstance(flags, int):
1678 raise TypeError('flags must be a int')
1679 if not isinstance(value, bytes):
1680 raise TypeError('value must be a bytes')
1685 char *_value = value
1686 size_t _value_len = len(value)
1690 ret = ceph_fsetxattr(self.cluster, _fd, _name,
1691 _value, _value_len, _flags)
1693 raise make_ex(ret, "error in fsetxattr")
1695 def lsetxattr(self, path, name, value, flags):
1697 Set an extended attribute on a file but do not follow symbolic link.
1699 :param path: the path to the file.
1700 :param name: the name of the extended attribute to set.
1701 :param value: the bytes of the extended attribute value
1704 self.setxattr(path, name, value, flags, follow_symlink=False)
1706 def removexattr(self, path, name, follow_symlink=True):
1708 Remove an extended attribute of a file.
1710 :param path: path of the file.
1711 :param name: name of the extended attribute to remove.
1713 self.require_state("mounted")
1715 name = cstr(name, 'name')
1716 path = cstr(path, 'path')
1724 ret = ceph_removexattr(self.cluster, _path, _name)
1727 ret = ceph_lremovexattr(self.cluster, _path, _name)
1730 raise make_ex(ret, "error in removexattr")
1732 def fremovexattr(self, fd, name):
1734 Remove an extended attribute of a file.
1736 :param fd: the open file descriptor referring to the file.
1737 :param name: name of the extended attribute to remove.
1739 self.require_state("mounted")
1741 if not isinstance(fd, int):
1742 raise TypeError('fd must be an int')
1743 name = cstr(name, 'name')
1750 ret = ceph_fremovexattr(self.cluster, _fd, _name)
1752 raise make_ex(ret, "error in fremovexattr")
1754 def lremovexattr(self, path, name):
1756 Remove an extended attribute of a file but do not follow symbolic link.
1758 :param path: path of the file.
1759 :param name: name of the extended attribute to remove.
1761 self.removexattr(path, name, follow_symlink=False)
1763 def listxattr(self, path, size=65536, follow_symlink=True):
1765 List the extended attribute keys set on a file.
1767 :param path: path of the file.
1768 :param size: the size of list buffer to be filled with extended attribute keys.
1770 self.require_state("mounted")
1772 path = cstr(path, 'path')
1776 char *ret_buf = NULL
1777 size_t ret_length = size
1780 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1783 ret = ceph_listxattr(self.cluster, _path, ret_buf, ret_length)
1786 ret = ceph_llistxattr(self.cluster, _path, ret_buf, ret_length)
1789 raise make_ex(ret, "error in listxattr")
1791 return ret, ret_buf[:ret]
1795 def flistxattr(self, fd, size=65536):
1797 List the extended attribute keys set on a file.
1799 :param fd: the open file descriptor referring to the file.
1800 :param size: the size of list buffer to be filled with extended attribute keys.
1802 self.require_state("mounted")
1804 if not isinstance(fd, int):
1805 raise TypeError('fd must be an int')
1809 char *ret_buf = NULL
1810 size_t ret_length = size
1813 ret_buf = <char *>realloc_chk(ret_buf, ret_length)
1815 ret = ceph_flistxattr(self.cluster, _fd, ret_buf, ret_length)
1818 raise make_ex(ret, "error in flistxattr")
1820 return ret, ret_buf[:ret]
1824 def llistxattr(self, path, size=65536):
1826 List the extended attribute keys set on a file but do not follow symbolic link.
1828 :param path: path of the file.
1829 :param size: the size of list buffer to be filled with extended attribute keys.
1832 return self.listxattr(path, size=size, follow_symlink=False)
1834 def stat(self, path, follow_symlink=True):
1836 Get a file's extended statistics and attributes.
1838 :param path: the file or directory to get the statistics of.
1840 self.require_state("mounted")
1841 path = cstr(path, 'path')
1849 ret = ceph_statx(self.cluster, _path, &stx,
1850 CEPH_STATX_BASIC_STATS_CDEF, 0)
1853 ret = ceph_statx(self.cluster, _path, &stx,
1854 CEPH_STATX_BASIC_STATS_CDEF, AT_SYMLINK_NOFOLLOW_CDEF)
1857 raise make_ex(ret, "error in stat: {}".format(path.decode('utf-8')))
1858 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1859 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1860 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1861 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1862 st_blksize=stx.stx_blksize,
1863 st_blocks=stx.stx_blocks,
1864 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1865 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1866 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1868 def lstat(self, path):
1870 Get a file's extended statistics and attributes. When file's a
1871 symbolic link, return the informaion of the link itself rather
1872 than that of the file it points too.
1874 :param path: the file or directory to get the statistics of.
1876 return self.stat(path, follow_symlink=False)
1878 def fstat(self, fd):
1880 Get an open file's extended statistics and attributes.
1882 :param fd: the file descriptor of the file to get statistics of.
1884 self.require_state("mounted")
1885 if not isinstance(fd, int):
1886 raise TypeError('fd must be an int')
1893 ret = ceph_fstatx(self.cluster, _fd, &stx,
1894 CEPH_STATX_BASIC_STATS_CDEF, 0)
1896 raise make_ex(ret, "error in fsat")
1897 return StatResult(st_dev=stx.stx_dev, st_ino=stx.stx_ino,
1898 st_mode=stx.stx_mode, st_nlink=stx.stx_nlink,
1899 st_uid=stx.stx_uid, st_gid=stx.stx_gid,
1900 st_rdev=stx.stx_rdev, st_size=stx.stx_size,
1901 st_blksize=stx.stx_blksize,
1902 st_blocks=stx.stx_blocks,
1903 st_atime=datetime.fromtimestamp(stx.stx_atime.tv_sec),
1904 st_mtime=datetime.fromtimestamp(stx.stx_mtime.tv_sec),
1905 st_ctime=datetime.fromtimestamp(stx.stx_ctime.tv_sec))
1907 def statx(self, path, mask, flag):
1909 Get a file's extended statistics and attributes.
1911 :param path: the file or directory to get the statistics of.
1912 :param mask: want bitfield of CEPH_STATX_* flags showing designed attributes.
1913 :param flag: bitfield that can be used to set AT_* modifier flags (only AT_NO_ATTR_SYNC and AT_SYMLINK_NOFOLLOW)
1916 self.require_state("mounted")
1917 path = cstr(path, 'path')
1918 if not isinstance(mask, int):
1919 raise TypeError('flag must be a int')
1920 if not isinstance(flag, int):
1921 raise TypeError('flag must be a int')
1928 dict_result = dict()
1931 ret = ceph_statx(self.cluster, _path, &stx, _mask, _flag)
1933 raise make_ex(ret, "error in stat: %s" % path)
1935 if (_mask & CEPH_STATX_MODE):
1936 dict_result["mode"] = stx.stx_mode
1937 if (_mask & CEPH_STATX_NLINK):
1938 dict_result["nlink"] = stx.stx_nlink
1939 if (_mask & CEPH_STATX_UID):
1940 dict_result["uid"] = stx.stx_uid
1941 if (_mask & CEPH_STATX_GID):
1942 dict_result["gid"] = stx.stx_gid
1943 if (_mask & CEPH_STATX_RDEV):
1944 dict_result["rdev"] = stx.stx_rdev
1945 if (_mask & CEPH_STATX_ATIME):
1946 dict_result["atime"] = datetime.fromtimestamp(stx.stx_atime.tv_sec)
1947 if (_mask & CEPH_STATX_MTIME):
1948 dict_result["mtime"] = datetime.fromtimestamp(stx.stx_mtime.tv_sec)
1949 if (_mask & CEPH_STATX_CTIME):
1950 dict_result["ctime"] = datetime.fromtimestamp(stx.stx_ctime.tv_sec)
1951 if (_mask & CEPH_STATX_INO):
1952 dict_result["ino"] = stx.stx_ino
1953 if (_mask & CEPH_STATX_SIZE):
1954 dict_result["size"] = stx.stx_size
1955 if (_mask & CEPH_STATX_BLOCKS):
1956 dict_result["blocks"] = stx.stx_blocks
1957 if (_mask & CEPH_STATX_BTIME):
1958 dict_result["btime"] = datetime.fromtimestamp(stx.stx_btime.tv_sec)
1959 if (_mask & CEPH_STATX_VERSION):
1960 dict_result["version"] = stx.stx_version
1964 def setattrx(self, path, dict_stx, mask, flags):
1966 Set a file's attributes.
1968 :param path: the path to the file/directory to set the attributes of.
1969 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
1970 :param stx: a dict of statx structure that must include attribute values to set on the file.
1971 :param flags: mask of AT_* flags (only AT_ATTR_NOFOLLOW is respected for now)
1974 self.require_state("mounted")
1975 path = cstr(path, 'path')
1976 if not isinstance(dict_stx, dict):
1977 raise TypeError('dict_stx must be a dict')
1978 if not isinstance(mask, int):
1979 raise TypeError('mask must be a int')
1980 if not isinstance(flags, int):
1981 raise TypeError('flags must be a int')
1985 if (mask & CEPH_SETATTR_MODE):
1986 stx.stx_mode = dict_stx["mode"]
1987 if (mask & CEPH_SETATTR_UID):
1988 stx.stx_uid = dict_stx["uid"]
1989 if (mask & CEPH_SETATTR_GID):
1990 stx.stx_gid = dict_stx["gid"]
1991 if (mask & CEPH_SETATTR_MTIME):
1992 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
1993 if (mask & CEPH_SETATTR_ATIME):
1994 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
1995 if (mask & CEPH_SETATTR_CTIME):
1996 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
1997 if (mask & CEPH_SETATTR_SIZE):
1998 stx.stx_size = dict_stx["size"]
1999 if (mask & CEPH_SETATTR_BTIME):
2000 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2006 dict_result = dict()
2009 ret = ceph_setattrx(self.cluster, _path, &stx, _mask, _flags)
2011 raise make_ex(ret, "error in setattrx: %s" % path)
2013 def fsetattrx(self, fd, dict_stx, mask):
2015 Set a file's attributes.
2017 :param path: the path to the file/directory to set the attributes of.
2018 :param mask: a mask of all the CEPH_SETATTR_* values that have been set in the statx struct.
2019 :param stx: a dict of statx structure that must include attribute values to set on the file.
2022 self.require_state("mounted")
2023 if not isinstance(fd, int):
2024 raise TypeError('fd must be a int')
2025 if not isinstance(dict_stx, dict):
2026 raise TypeError('dict_stx must be a dict')
2027 if not isinstance(mask, int):
2028 raise TypeError('mask must be a int')
2032 if (mask & CEPH_SETATTR_MODE):
2033 stx.stx_mode = dict_stx["mode"]
2034 if (mask & CEPH_SETATTR_UID):
2035 stx.stx_uid = dict_stx["uid"]
2036 if (mask & CEPH_SETATTR_GID):
2037 stx.stx_gid = dict_stx["gid"]
2038 if (mask & CEPH_SETATTR_MTIME):
2039 stx.stx_mtime = to_timespec(dict_stx["mtime"].timestamp())
2040 if (mask & CEPH_SETATTR_ATIME):
2041 stx.stx_atime = to_timespec(dict_stx["atime"].timestamp())
2042 if (mask & CEPH_SETATTR_CTIME):
2043 stx.stx_ctime = to_timespec(dict_stx["ctime"].timestamp())
2044 if (mask & CEPH_SETATTR_SIZE):
2045 stx.stx_size = dict_stx["size"]
2046 if (mask & CEPH_SETATTR_BTIME):
2047 stx.stx_btime = to_timespec(dict_stx["btime"].timestamp())
2052 dict_result = dict()
2055 ret = ceph_fsetattrx(self.cluster, _fd, &stx, _mask)
2057 raise make_ex(ret, "error in fsetattrx")
2059 def symlink(self, existing, newname):
2061 Creates a symbolic link.
2063 :param existing: the path to the existing file/directory to link to.
2064 :param newname: the path to the new file/directory to link from.
2066 self.require_state("mounted")
2067 existing = cstr(existing, 'existing')
2068 newname = cstr(newname, 'newname')
2070 char* _existing = existing
2071 char* _newname = newname
2074 ret = ceph_symlink(self.cluster, _existing, _newname)
2076 raise make_ex(ret, "error in symlink")
2078 def link(self, existing, newname):
2082 :param existing: the path to the existing file/directory to link to.
2083 :param newname: the path to the new file/directory to link from.
2086 self.require_state("mounted")
2087 existing = cstr(existing, 'existing')
2088 newname = cstr(newname, 'newname')
2090 char* _existing = existing
2091 char* _newname = newname
2094 ret = ceph_link(self.cluster, _existing, _newname)
2096 raise make_ex(ret, "error in link")
2098 def readlink(self, path, size):
2100 Read a symbolic link.
2102 :param path: the path to the symlink to read
2103 :param size: the length of the buffer
2104 :rtype buf: buffer to hold the path of the file that the symlink points to.
2106 self.require_state("mounted")
2107 path = cstr(path, 'path')
2111 int64_t _size = size
2115 buf = <char *>realloc_chk(buf, _size)
2117 ret = ceph_readlink(self.cluster, _path, buf, _size)
2119 raise make_ex(ret, "error in readlink")
2124 def unlink(self, path):
2126 Removes a file, link, or symbolic link. If the file/link has multiple links to it, the
2127 file will not disappear from the namespace until all references to it are removed.
2129 :param path: the path of the file or link to unlink.
2131 self.require_state("mounted")
2132 path = cstr(path, 'path')
2133 cdef char* _path = path
2135 ret = ceph_unlink(self.cluster, _path)
2137 raise make_ex(ret, "error in unlink: {}".format(path.decode('utf-8')))
2139 def rename(self, src, dst):
2141 Rename a file or directory.
2143 :param src: the path to the existing file or directory.
2144 :param dst: the new name of the file or directory.
2147 self.require_state("mounted")
2149 src = cstr(src, 'source')
2150 dst = cstr(dst, 'destination')
2157 ret = ceph_rename(self.cluster, _src, _dst)
2159 raise make_ex(ret, "error in rename {} to {}".format(src.decode(
2160 'utf-8'), dst.decode('utf-8')))
2162 def mds_command(self, mds_spec, args, input_data):
2164 :return 3-tuple of output status int, output status string, output data
2166 mds_spec = cstr(mds_spec, 'mds_spec')
2167 args = cstr(args, 'args')
2168 input_data = cstr(input_data, 'input_data')
2171 char *_mds_spec = opt_str(mds_spec)
2172 char **_cmd = to_bytes_array([args])
2175 char *_inbuf = input_data
2176 size_t _inbuf_len = len(input_data)
2178 char *_outbuf = NULL
2179 size_t _outbuf_len = 0
2181 size_t _outs_len = 0
2185 ret = ceph_mds_command(self.cluster, _mds_spec,
2186 <const char **>_cmd, _cmdlen,
2187 <const char*>_inbuf, _inbuf_len,
2188 &_outbuf, &_outbuf_len,
2190 my_outs = decode_cstr(_outs[:_outs_len])
2191 my_outbuf = _outbuf[:_outbuf_len]
2193 ceph_buffer_free(_outs)
2195 ceph_buffer_free(_outbuf)
2196 return (ret, my_outbuf, my_outs)
2200 def umask(self, mode) :
2201 self.require_state("mounted")
2205 ret = ceph_umask(self.cluster, _mode)
2207 raise make_ex(ret, "error in umask")
2210 def lseek(self, fd, offset, whence):
2212 Set the file's current position.
2214 :param fd: the file descriptor of the open file to read from.
2215 :param offset: the offset in the file to read from. If this value is negative, the
2216 function reads from the current offset of the file descriptor.
2217 :param whence: the flag to indicate what type of seeking to performs:SEEK_SET, SEEK_CUR, SEEK_END
2219 self.require_state("mounted")
2220 if not isinstance(fd, int):
2221 raise TypeError('fd must be an int')
2222 if not isinstance(offset, int):
2223 raise TypeError('offset must be an int')
2224 if not isinstance(whence, int):
2225 raise TypeError('whence must be an int')
2229 int64_t _offset = offset
2230 int64_t _whence = whence
2233 ret = ceph_lseek(self.cluster, _fd, _offset, _whence)
2236 raise make_ex(ret, "error in lseek")
2240 def utime(self, path, times=None):
2242 Set access and modification time for path
2244 :param path: file path for which timestamps have to be changed
2245 :param times: if times is not None, it must be a tuple (atime, mtime)
2248 self.require_state("mounted")
2249 path = cstr(path, 'path')
2251 if not isinstance(times, tuple):
2252 raise TypeError('times must be a tuple')
2253 if not isinstance(times[0], int):
2254 raise TypeError('atime must be an int')
2255 if not isinstance(times[1], int):
2256 raise TypeError('mtime must be an int')
2257 actime = modtime = int(time.time())
2264 utimbuf buf = utimbuf(actime, modtime)
2266 ret = ceph_utime(self.cluster, pth, &buf)
2268 raise make_ex(ret, "error in utime {}".format(path.decode('utf-8')))
2270 def futime(self, fd, times=None):
2272 Set access and modification time for a file pointed by descriptor
2274 :param fd: file descriptor of the open file
2275 :param times: if times is not None, it must be a tuple (atime, mtime)
2278 self.require_state("mounted")
2279 if not isinstance(fd, int):
2280 raise TypeError('fd must be an int')
2282 if not isinstance(times, tuple):
2283 raise TypeError('times must be a tuple')
2284 if not isinstance(times[0], int):
2285 raise TypeError('atime must be an int')
2286 if not isinstance(times[1], int):
2287 raise TypeError('mtime must be an int')
2288 actime = modtime = int(time.time())
2295 utimbuf buf = utimbuf(actime, modtime)
2297 ret = ceph_futime(self.cluster, _fd, &buf)
2299 raise make_ex(ret, "error in futime")
2301 def utimes(self, path, times=None, follow_symlink=True):
2303 Set access and modification time for path
2305 :param path: file path for which timestamps have to be changed
2306 :param times: if times is not None, it must be a tuple (atime, mtime)
2307 :param follow_symlink: perform the operation on the target file if @path
2308 is a symbolic link (default)
2311 self.require_state("mounted")
2312 path = cstr(path, 'path')
2314 if not isinstance(times, tuple):
2315 raise TypeError('times must be a tuple')
2316 if not isinstance(times[0], (int, float)):
2317 raise TypeError('atime must be an int or a float')
2318 if not isinstance(times[1], (int, float)):
2319 raise TypeError('mtime must be an int or a float')
2320 actime = modtime = time.time()
2322 actime = float(times[0])
2323 modtime = float(times[1])
2327 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2330 ret = ceph_utimes(self.cluster, pth, buf)
2333 ret = ceph_lutimes(self.cluster, pth, buf)
2335 raise make_ex(ret, "error in utimes {}".format(path.decode('utf-8')))
2337 def lutimes(self, path, times=None):
2339 Set access and modification time for a file. If the file is a symbolic
2340 link do not follow to the target.
2342 :param path: file path for which timestamps have to be changed
2343 :param times: if times is not None, it must be a tuple (atime, mtime)
2345 self.utimes(path, times=times, follow_symlink=False)
2347 def futimes(self, fd, times=None):
2349 Set access and modification time for a file pointer by descriptor
2351 :param fd: file descriptor of the open file
2352 :param times: if times is not None, it must be a tuple (atime, mtime)
2355 self.require_state("mounted")
2356 if not isinstance(fd, int):
2357 raise TypeError('fd must be an int')
2359 if not isinstance(times, tuple):
2360 raise TypeError('times must be a tuple')
2361 if not isinstance(times[0], (int, float)):
2362 raise TypeError('atime must be an int or a float')
2363 if not isinstance(times[1], (int, float)):
2364 raise TypeError('mtime must be an int or a float')
2365 actime = modtime = time.time()
2367 actime = float(times[0])
2368 modtime = float(times[1])
2372 timeval *buf = [to_timeval(actime), to_timeval(modtime)]
2374 ret = ceph_futimes(self.cluster, _fd, buf)
2376 raise make_ex(ret, "error in futimes")
2378 def futimens(self, fd, times=None):
2380 Set access and modification time for a file pointer by descriptor
2382 :param fd: file descriptor of the open file
2383 :param times: if times is not None, it must be a tuple (atime, mtime)
2386 self.require_state("mounted")
2387 if not isinstance(fd, int):
2388 raise TypeError('fd must be an int')
2390 if not isinstance(times, tuple):
2391 raise TypeError('times must be a tuple')
2392 if not isinstance(times[0], (int, float)):
2393 raise TypeError('atime must be an int or a float')
2394 if not isinstance(times[1], (int, float)):
2395 raise TypeError('mtime must be an int or a float')
2396 actime = modtime = time.time()
2398 actime = float(times[0])
2399 modtime = float(times[1])
2403 timespec *buf = [to_timespec(actime), to_timespec(modtime)]
2405 ret = ceph_futimens(self.cluster, _fd, buf)
2407 raise make_ex(ret, "error in futimens")
2409 def get_file_replication(self, fd):
2411 Get the file replication information from an open file descriptor.
2413 :param fd : the open file descriptor referring to the file to get
2414 the replication information of.
2416 self.require_state("mounted")
2417 if not isinstance(fd, int):
2418 raise TypeError('fd must be an int')
2424 ret = ceph_get_file_replication(self.cluster, _fd)
2426 raise make_ex(ret, "error in get_file_replication")
2430 def get_path_replication(self, path):
2432 Get the file replication information given the path.
2434 :param path: the path of the file/directory to get the replication information of.
2436 self.require_state("mounted")
2437 path = cstr(path, 'path')
2443 ret = ceph_get_path_replication(self.cluster, _path)
2445 raise make_ex(ret, "error in get_path_replication")
2449 def get_pool_id(self, pool_name):
2451 Get the id of the named pool.
2453 :param pool_name: the name of the pool.
2456 self.require_state("mounted")
2457 pool_name = cstr(pool_name, 'pool_name')
2460 char* _pool_name = pool_name
2463 ret = ceph_get_pool_id(self.cluster, _pool_name)
2465 raise make_ex(ret, "error in get_pool_id")
2469 def get_pool_replication(self, pool_id):
2471 Get the pool replication factor.
2473 :param pool_id: the pool id to look up
2476 self.require_state("mounted")
2477 if not isinstance(pool_id, int):
2478 raise TypeError('pool_id must be an int')
2481 int _pool_id = pool_id
2484 ret = ceph_get_pool_replication(self.cluster, _pool_id)
2486 raise make_ex(ret, "error in get_pool_replication")
2490 def debug_get_fd_caps(self, fd):
2492 Get the capabilities currently issued to the client given the fd.
2494 :param fd: the file descriptor to get issued
2497 self.require_state("mounted")
2498 if not isinstance(fd, int):
2499 raise TypeError('fd must be an int')
2505 ret = ceph_debug_get_fd_caps(self.cluster, _fd)
2507 raise make_ex(ret, "error in debug_get_fd_caps")
2511 def debug_get_file_caps(self, path):
2513 Get the capabilities currently issued to the client given the path.
2515 :param path: the path of the file/directory to get the capabilities of.
2518 self.require_state("mounted")
2519 path = cstr(path, 'path')
2525 ret = ceph_debug_get_file_caps(self.cluster, _path)
2527 raise make_ex(ret, "error in debug_get_file_caps")
2531 def get_cap_return_timeout(self):
2533 Get the amount of time that the client has to return caps
2535 In the event that a client does not return its caps, the MDS may blocklist
2536 it after this timeout. Applications should check this value and ensure
2537 that they set the delegation timeout to a value lower than this.
2540 self.require_state("mounted")
2543 ret = ceph_get_cap_return_timeout(self.cluster)
2545 raise make_ex(ret, "error in get_cap_return_timeout")
2549 def set_uuid(self, uuid):
2551 Set ceph client uuid. Must be called before mount.
2553 :param uuid: the uuid to set
2556 uuid = cstr(uuid, 'uuid')
2562 ceph_set_uuid(self.cluster, _uuid)
2564 def set_session_timeout(self, timeout):
2566 Set ceph client session timeout. Must be called before mount.
2568 :param timeout: the timeout to set
2571 if not isinstance(timeout, int):
2572 raise TypeError('timeout must be an int')
2575 int _timeout = timeout
2578 ceph_set_session_timeout(self.cluster, _timeout)
2580 def get_layout(self, fd):
2582 Set ceph client session timeout. Must be called before mount.
2584 :param fd: file descriptor of the file/directory for which to get the layout
2587 if not isinstance(fd, int):
2588 raise TypeError('fd must be an int')
2598 dict_result = dict()
2601 ret = ceph_get_file_layout(self.cluster, _fd, &stripe_unit, &stripe_count, &object_size, &pool_id)
2603 raise make_ex(stripe_unit, "error in get_file_layout")
2604 dict_result["stripe_unit"] = stripe_unit
2605 dict_result["stripe_count"] = stripe_count
2606 dict_result["object_size"] = object_size
2607 dict_result["pool_id"] = pool_id
2611 buf = <char *>realloc_chk(buf, buflen)
2613 ret = ceph_get_file_pool_name(self.cluster, _fd, buf, buflen)
2615 dict_result["pool_name"] = decode_cstr(buf)
2617 elif ret == -CEPHFS_ERANGE:
2620 raise make_ex(ret, "error in get_file_pool_name")
2625 def get_default_pool(self):
2627 Get the default pool name and id of cephfs. This returns dict{pool_name, pool_id}.
2633 dict_result = dict()
2637 buf = <char *>realloc_chk(buf, buflen)
2639 ret = ceph_get_default_data_pool_name(self.cluster, buf, buflen)
2641 dict_result["pool_name"] = decode_cstr(buf)
2643 elif ret == -CEPHFS_ERANGE:
2646 raise make_ex(ret, "error in get_default_data_pool_name")
2649 ret = ceph_get_pool_id(self.cluster, buf)
2651 raise make_ex(ret, "error in get_pool_id")
2652 dict_result["pool_id"] = ret