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:
Sam Lang 2013-02-21 07:38:14 -06:00
parent 0c7447576a
commit acae3d02b9
3 changed files with 327 additions and 1 deletions

View File

@ -562,6 +562,7 @@ fi
%defattr(-,root,root,-)
%{python_sitelib}/rados.py*
%{python_sitelib}/rbd.py*
%{python_sitelib}/cephfs.py*
#################################################################################
%files -n rest-bench

View File

@ -1453,7 +1453,8 @@ dist-hook:
$(srcdir)/check_version $(srcdir)/.git_version
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'
# that autotools doesn't magically identify.

324
src/pybind/cephfs.py Normal file
View 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)