mirror of
https://github.com/ceph/ceph
synced 2025-01-04 02:02:36 +00:00
pybind/cephfs: Add initial py wrappers for cephfs.
Initial Support for python bindings to libcephfs for testing MDS backtraces with a the python script test-backtraces.py. Signed-off-by: Sam Lang <sam.lang@inktank.com> Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
This commit is contained in:
parent
0c7447576a
commit
acae3d02b9
@ -562,6 +562,7 @@ fi
|
|||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%{python_sitelib}/rados.py*
|
%{python_sitelib}/rados.py*
|
||||||
%{python_sitelib}/rbd.py*
|
%{python_sitelib}/rbd.py*
|
||||||
|
%{python_sitelib}/cephfs.py*
|
||||||
|
|
||||||
#################################################################################
|
#################################################################################
|
||||||
%files -n rest-bench
|
%files -n rest-bench
|
||||||
|
@ -1453,7 +1453,8 @@ dist-hook:
|
|||||||
$(srcdir)/check_version $(srcdir)/.git_version
|
$(srcdir)/check_version $(srcdir)/.git_version
|
||||||
|
|
||||||
python_PYTHON = pybind/rados.py \
|
python_PYTHON = pybind/rados.py \
|
||||||
pybind/rbd.py
|
pybind/rbd.py \
|
||||||
|
pybind/cephfs.py
|
||||||
|
|
||||||
# headers... and everything else we want to include in a 'make dist'
|
# headers... and everything else we want to include in a 'make dist'
|
||||||
# that autotools doesn't magically identify.
|
# that autotools doesn't magically identify.
|
||||||
|
324
src/pybind/cephfs.py
Normal file
324
src/pybind/cephfs.py
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
"""libcephfs Python ctypes wrapper
|
||||||
|
"""
|
||||||
|
from ctypes import CDLL, c_char_p, c_size_t, c_void_p, c_int, c_long, c_uint, c_ulong, \
|
||||||
|
create_string_buffer, byref, Structure, c_uint64, c_ubyte, pointer, \
|
||||||
|
CFUNCTYPE
|
||||||
|
import ctypes
|
||||||
|
import errno
|
||||||
|
|
||||||
|
class Error(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PermissionError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ObjectNotFound(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NoData(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ObjectExists(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class IOError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class NoSpace(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class IncompleteWriteError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class LibCephFSStateError(Error):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def make_ex(ret, msg):
|
||||||
|
ret = abs(ret)
|
||||||
|
if ret == errno.EPERM:
|
||||||
|
return PermissionError(msg)
|
||||||
|
elif ret == errno.ENOENT:
|
||||||
|
return ObjectNotFound(msg)
|
||||||
|
elif ret == errno.EIO:
|
||||||
|
return IOError(msg)
|
||||||
|
elif ret == errno.ENOSPC:
|
||||||
|
return NoSpace(msg)
|
||||||
|
elif ret == errno.EEXIST:
|
||||||
|
return ObjectExists(msg)
|
||||||
|
elif ret == errno.ENODATA:
|
||||||
|
return NoData(msg)
|
||||||
|
else:
|
||||||
|
return Error(msg + (": error code %d" % ret))
|
||||||
|
|
||||||
|
class cephfs_statvfs(Structure):
|
||||||
|
_fields_ = [("f_bsize", c_uint),
|
||||||
|
("f_frsize", c_uint),
|
||||||
|
("f_blocks", c_uint),
|
||||||
|
("f_bfree", c_uint),
|
||||||
|
("f_bavail", c_uint),
|
||||||
|
("f_files", c_uint),
|
||||||
|
("f_ffree", c_uint),
|
||||||
|
("f_favail", c_uint),
|
||||||
|
("f_fsid", c_uint),
|
||||||
|
("f_flag", c_uint),
|
||||||
|
("f_namemax", c_uint)]
|
||||||
|
|
||||||
|
# struct timespec {
|
||||||
|
# long int tv_sec;
|
||||||
|
# long int tv_nsec;
|
||||||
|
# }
|
||||||
|
class cephfs_timespec(Structure):
|
||||||
|
_fields_ = [('tv_sec', c_long),
|
||||||
|
('tv_nsec', c_long)]
|
||||||
|
|
||||||
|
# struct stat {
|
||||||
|
# unsigned long st_dev;
|
||||||
|
# unsigned long st_ino;
|
||||||
|
# unsigned long st_nlink;
|
||||||
|
# unsigned int st_mode;
|
||||||
|
# unsigned int st_uid;
|
||||||
|
# unsigned int st_gid;
|
||||||
|
# int __pad0;
|
||||||
|
# unsigned long st_rdev;
|
||||||
|
# long int st_size;
|
||||||
|
# long int st_blksize;
|
||||||
|
# long int st_blocks;
|
||||||
|
# struct timespec st_atim;
|
||||||
|
# struct timespec st_mtim;
|
||||||
|
# struct timespec st_ctim;
|
||||||
|
# long int __unused[3];
|
||||||
|
# };
|
||||||
|
class cephfs_stat(Structure):
|
||||||
|
_fields_ = [('st_dev', c_ulong), # ID of device containing file
|
||||||
|
('st_ino', c_ulong), # inode number
|
||||||
|
('st_nlink', c_ulong), # number of hard links
|
||||||
|
('st_mode', c_uint), # protection
|
||||||
|
('st_uid', c_uint), # user ID of owner
|
||||||
|
('st_gid', c_uint), # group ID of owner
|
||||||
|
('__pad0', c_int),
|
||||||
|
('st_rdev', c_ulong), # device ID (if special file)
|
||||||
|
('st_size', c_long), # total size, in bytes
|
||||||
|
('st_blksize', c_long), # blocksize for file system I/O
|
||||||
|
('st_blocks', c_long), # number of 512B blocks allocated
|
||||||
|
('st_atime', cephfs_timespec), # time of last access
|
||||||
|
('st_mtime', cephfs_timespec), # time of last modification
|
||||||
|
('st_ctime', cephfs_timespec), # time of last status change
|
||||||
|
('__unused1', c_long),
|
||||||
|
('__unused2', c_long),
|
||||||
|
('__unused3', c_long) ]
|
||||||
|
|
||||||
|
class LibCephFS(object):
|
||||||
|
"""libcephfs python wrapper"""
|
||||||
|
def require_state(self, *args):
|
||||||
|
for a in args:
|
||||||
|
if self.state == a:
|
||||||
|
return
|
||||||
|
raise LibCephFSStateError("You cannot perform that operation on a "
|
||||||
|
"CephFS object in state %s." % (self.state))
|
||||||
|
|
||||||
|
def __init__(self, conf=None, conffile=None):
|
||||||
|
self.libcephfs = CDLL('libcephfs.so.1')
|
||||||
|
self.cluster = c_void_p()
|
||||||
|
|
||||||
|
if conffile is not None and not isinstance(conffile, str):
|
||||||
|
raise TypeError('conffile must be a string or None')
|
||||||
|
ret = self.libcephfs.ceph_create(byref(self.cluster), c_char_p(0))
|
||||||
|
if ret != 0:
|
||||||
|
raise Error("libcephfs_initialize failed with error code: %d" %ret)
|
||||||
|
self.state = "configuring"
|
||||||
|
if conffile is not None:
|
||||||
|
# read the default conf file when '' is given
|
||||||
|
if conffile == '':
|
||||||
|
conffile = None
|
||||||
|
self.conf_read_file(conffile)
|
||||||
|
if conf is not None:
|
||||||
|
for key, value in conf.iteritems():
|
||||||
|
self.conf_set(key, value)
|
||||||
|
|
||||||
|
def conf_read_file(self, conffile=None):
|
||||||
|
if conffile is not None and not isinstance(conffile, str):
|
||||||
|
raise TypeError('conffile param must be a string')
|
||||||
|
ret = self.libcephfs.ceph_conf_read_file(self.cluster, c_char_p(conffile))
|
||||||
|
if ret != 0:
|
||||||
|
raise make_ex(ret, "error calling conf_read_file")
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
if self.state != "shutdown":
|
||||||
|
self.libcephfs.ceph_shutdown(self.cluster)
|
||||||
|
self.state = "shutdown"
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.mount()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type_, value, traceback):
|
||||||
|
self.shutdown()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.shutdown()
|
||||||
|
|
||||||
|
def version(self):
|
||||||
|
major = c_int(0)
|
||||||
|
minor = c_int(0)
|
||||||
|
extra = c_int(0)
|
||||||
|
self.libcephfs.ceph_version(byref(major), byref(minor), byref(extra))
|
||||||
|
return (major.value, minor.value, extra.value)
|
||||||
|
|
||||||
|
def conf_get(self, option):
|
||||||
|
self.require_state("configuring", "connected")
|
||||||
|
if not isinstance(option, str):
|
||||||
|
raise TypeError('option must be a string')
|
||||||
|
length = 20
|
||||||
|
while True:
|
||||||
|
ret_buf = create_string_buffer(length)
|
||||||
|
ret = self.libcephfs.ceph_conf_get(self.cluster, option,
|
||||||
|
ret_buf, c_size_t(length))
|
||||||
|
if ret == 0:
|
||||||
|
return ret_buf.value
|
||||||
|
elif ret == -errno.ENAMETOOLONG:
|
||||||
|
length = length * 2
|
||||||
|
elif ret == -errno.ENOENT:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
raise make_ex(ret, "error calling conf_get")
|
||||||
|
|
||||||
|
def conf_set(self, option, val):
|
||||||
|
self.require_state("configuring", "connected")
|
||||||
|
if not isinstance(option, str):
|
||||||
|
raise TypeError('option must be a string')
|
||||||
|
if not isinstance(val, str):
|
||||||
|
raise TypeError('val must be a string')
|
||||||
|
ret = self.libcephfs.ceph_conf_set(self.cluster, c_char_p(option),
|
||||||
|
c_char_p(val))
|
||||||
|
if ret != 0:
|
||||||
|
raise make_ex(ret, "error calling conf_set")
|
||||||
|
|
||||||
|
def mount(self):
|
||||||
|
self.require_state("configuring")
|
||||||
|
ret = self.libcephfs.ceph_mount(self.cluster, "/")
|
||||||
|
if ret != 0:
|
||||||
|
raise make_ex(ret, "error calling ceph_mount")
|
||||||
|
self.state = "mounted"
|
||||||
|
|
||||||
|
def statfs(self, path):
|
||||||
|
self.require_state("mounted")
|
||||||
|
statbuf = cephfs_statvfs()
|
||||||
|
ret = self.libcephfs.ceph_statfs(self.cluster, c_char_p(path), byref(statbuf))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "statfs failed: %s" % path)
|
||||||
|
return {'f_bsize': statbuf.f_bsize,
|
||||||
|
'f_frsize': statbuf.f_frsize,
|
||||||
|
'f_blocks': statbuf.f_blocks,
|
||||||
|
'f_bfree': statbuf.f_bfree,
|
||||||
|
'f_bavail': statbuf.f_bavail,
|
||||||
|
'f_files': statbuf.f_files,
|
||||||
|
'f_ffree': statbuf.f_ffree,
|
||||||
|
'f_favail': statbuf.f_favail,
|
||||||
|
'f_fsid': statbuf.f_fsid,
|
||||||
|
'f_flag': statbuf.f_flag,
|
||||||
|
'f_namemax': statbuf.f_namemax }
|
||||||
|
|
||||||
|
def sync_fs(self):
|
||||||
|
self.require_state("mounted")
|
||||||
|
ret = self.libcephfs.ceph_sync_fs(self.cluster)
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "sync_fs failed")
|
||||||
|
|
||||||
|
def getcwd(self):
|
||||||
|
self.require_state("mounted")
|
||||||
|
return self.libcephfs.ceph_getcwd(self.cluster)
|
||||||
|
|
||||||
|
def chdir(self, path):
|
||||||
|
self.require_state("mounted")
|
||||||
|
ret = self.libcephfs.ceph_chdir(self.cluster, c_char_p(path))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "chdir failed")
|
||||||
|
|
||||||
|
def mkdir(self, path, mode):
|
||||||
|
self.require_state("mounted")
|
||||||
|
if not isinstance(path, str):
|
||||||
|
raise TypeError('path must be a string')
|
||||||
|
ret = self.libcephfs.ceph_mkdir(self.cluster, c_char_p(path), c_int(mode))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in mkdir '%s'" % path)
|
||||||
|
|
||||||
|
def mkdirs(self, path, mode):
|
||||||
|
self.require_state("mounted")
|
||||||
|
if not isinstance(path, str):
|
||||||
|
raise TypeError('path must be a string')
|
||||||
|
if not isinstance(mode, int):
|
||||||
|
raise TypeError('mode must be an int')
|
||||||
|
ret = self.libcephfs.ceph_mkdir(self.cluster, c_char_p(path), c_int(mode))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in mkdirs '%s'" % path)
|
||||||
|
|
||||||
|
def open(self, path, flags, mode):
|
||||||
|
self.require_state("mounted")
|
||||||
|
if not isinstance(path, str):
|
||||||
|
raise TypeError('path must be a string')
|
||||||
|
if not isinstance(mode, int):
|
||||||
|
raise TypeError('mode must be an int')
|
||||||
|
if not isinstance(flags, int):
|
||||||
|
raise TypeError('flags must be an int')
|
||||||
|
ret = self.libcephfs.ceph_open(self.cluster, c_char_p(path), c_int(flags), c_int(mode))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in open '%s'" % path)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def close(self, fd):
|
||||||
|
self.require_state("mounted")
|
||||||
|
ret = self.libcephfs.ceph_close(self.cluster, c_int(fd))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in close")
|
||||||
|
|
||||||
|
def setxattr(self, path, name, value, flags):
|
||||||
|
if not isinstance(path, str):
|
||||||
|
raise TypeError('path must be a string')
|
||||||
|
if not isinstance(name, str):
|
||||||
|
raise TypeError('name must be a string')
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise TypeError('value must be a string')
|
||||||
|
self.require_state("mounted")
|
||||||
|
ret = self.libcephfs.ceph_setxattr(
|
||||||
|
self.cluster,
|
||||||
|
c_char_p(path),
|
||||||
|
c_char_p(name),
|
||||||
|
c_void_p(value),
|
||||||
|
c_size_t(len(value)),
|
||||||
|
c_int(flags))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in setxattr")
|
||||||
|
|
||||||
|
def stat(self, path):
|
||||||
|
self.require_state("mounted")
|
||||||
|
if not isinstance(path, str):
|
||||||
|
raise TypeError('path must be a string')
|
||||||
|
statbuf = cephfs_stat()
|
||||||
|
ret = self.libcephfs.ceph_stat(
|
||||||
|
self.cluster,
|
||||||
|
c_char_p(path),
|
||||||
|
byref(statbuf))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in stat: %s" % path)
|
||||||
|
return {'st_dev': statbuf.st_dev,
|
||||||
|
'st_ino': statbuf.st_ino,
|
||||||
|
'st_mode': statbuf.st_mode,
|
||||||
|
'st_nlink': statbuf.st_nlink,
|
||||||
|
'st_uid': statbuf.st_uid,
|
||||||
|
'st_gid': statbuf.st_gid,
|
||||||
|
'st_rdev': statbuf.st_rdev,
|
||||||
|
'st_size': statbuf.st_size,
|
||||||
|
'st_blksize': statbuf.st_blksize,
|
||||||
|
'st_blocks': statbuf.st_blocks,
|
||||||
|
'st_atime': statbuf.st_atime,
|
||||||
|
'st_mtime': statbuf.st_mtime,
|
||||||
|
'st_ctime': statbuf.st_ctime }
|
||||||
|
|
||||||
|
def unlink(self, path):
|
||||||
|
self.require_state("mounted")
|
||||||
|
ret = self.libcephfs.ceph_unlink(
|
||||||
|
self.cluster,
|
||||||
|
c_char_p(path))
|
||||||
|
if ret < 0:
|
||||||
|
raise make_ex(ret, "error in unlink: %s" % path)
|
Loading…
Reference in New Issue
Block a user