diff options
author | Alfredo Deza <alfredo.deza@inktank.com> | 2013-08-16 13:23:13 -0400 |
---|---|---|
committer | Alfredo Deza <alfredo.deza@inktank.com> | 2013-08-16 13:23:13 -0400 |
commit | 4036fb35e9b10e821ec04fb3f65e6941d524475b (patch) | |
tree | 8580e0d0bf6e4e3bab8a7d10146b9c1a5f75bd62 | |
parent | 2cb1b4c85ba2193fd18b7ddc1f7bd6cca8e978ee (diff) | |
download | ceph-4036fb35e9b10e821ec04fb3f65e6941d524475b.tar.gz |
create the cephfs package
Signed-off-by: Alfredo Deza <alfredo.deza@inktank.com>
-rw-r--r-- | src/pybind/cephfs/MANIFEST.in | 2 | ||||
-rw-r--r-- | src/pybind/cephfs/README.rst | 0 | ||||
-rw-r--r-- | src/pybind/cephfs/cephfs.py | 340 | ||||
-rw-r--r-- | src/pybind/cephfs/setup.py | 32 | ||||
-rw-r--r-- | src/pybind/cephfs/tox.ini | 6 |
5 files changed, 380 insertions, 0 deletions
diff --git a/src/pybind/cephfs/MANIFEST.in b/src/pybind/cephfs/MANIFEST.in new file mode 100644 index 00000000000..3c01b12a5a5 --- /dev/null +++ b/src/pybind/cephfs/MANIFEST.in @@ -0,0 +1,2 @@ +include setup.py +include README.rst diff --git a/src/pybind/cephfs/README.rst b/src/pybind/cephfs/README.rst new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/pybind/cephfs/README.rst diff --git a/src/pybind/cephfs/cephfs.py b/src/pybind/cephfs/cephfs.py new file mode 100644 index 00000000000..80b7e4b773f --- /dev/null +++ b/src/pybind/cephfs/cephfs.py @@ -0,0 +1,340 @@ +""" +This module is a thin wrapper around libcephfs. +""" +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 +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): + """ + Translate a libcephfs return code into an exception. + + :param ret: the return code + :type ret: int + :param msg: the error message to use + :type msg: str + :returns: a subclass of :class:`Error` + """ + + errors = { + errno.EPERM : PermissionError, + errno.ENOENT : ObjectNotFound, + errno.EIO : IOError, + errno.ENOSPC : NoSpace, + errno.EEXIST : ObjectExists, + errno.ENODATA : NoData + } + ret = abs(ret) + if ret in errors: + return errors[ret](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): + """ + Unmount and destroy the ceph mount handle. + """ + 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): + """ + Get the version number of the ``libcephfs`` C library. + + :returns: a tuple of ``(major, minor, extra)`` components of the + libcephfs version + """ + 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) diff --git a/src/pybind/cephfs/setup.py b/src/pybind/cephfs/setup.py new file mode 100644 index 00000000000..bbd96dbb9ee --- /dev/null +++ b/src/pybind/cephfs/setup.py @@ -0,0 +1,32 @@ +from setuptools import setup, find_packages +import os + + +def long_description(): + readme = os.path.join(os.path.dirname(__file__), 'README.rst') + return open(readme).read() + + +setup( + name = 'cephfs', + description = 'Bindings for cephfs [ceph]', + packages=find_packages(), + author = 'Inktank', + author_email = 'ceph-devel@vger.kernel.org', + version = '0.67', #XXX Fix version + license = "LGPL2", + zip_safe = False, + keywords = "ceph, cephfs, bindings, api, cli", + long_description = long_description(), + classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)', + 'Topic :: Software Development :: Libraries', + 'Topic :: Utilities', + 'Topic :: System :: Filesystems', + 'Operating System :: POSIX', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + ], +) diff --git a/src/pybind/cephfs/tox.ini b/src/pybind/cephfs/tox.ini new file mode 100644 index 00000000000..9c63bb9d884 --- /dev/null +++ b/src/pybind/cephfs/tox.ini @@ -0,0 +1,6 @@ +[tox] +envlist = py26, py27 + +[testenv] +deps= +commands= |