diff options
| author | Vijay Bellur <vbellur@redhat.com> | 2014-06-20 07:48:19 -0700 | 
|---|---|---|
| committer | Vijay Bellur <vbellur@redhat.com> | 2014-06-20 07:49:12 -0700 | 
| commit | 85ee12f7c061d7035a6441f5d11d9ebed5e574cf (patch) | |
| tree | 5c2aaa372f9fa3a2ef3e29f860c7bfbba9e9ac57 /api | |
| parent | eba1fb05791f1424f68998cf2175cad1ce6098de (diff) | |
Revert "gfapi : remove gfapi.py to avoid confusion"
This reverts commit d0547a28f59d80ed572652af3e327b761c4e540f
Change-Id: I79ff562fff33d4ad7ca34c6442eb0e980890965a
Reviewed-on: http://review.gluster.org/8139
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Tested-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'api')
| -rw-r--r-- | api/examples/Makefile.am | 4 | ||||
| -rw-r--r-- | api/examples/__init__.py.in | 1 | ||||
| -rwxr-xr-x | api/examples/gfapi.py | 422 | ||||
| -rw-r--r-- | api/examples/setup.py.in | 24 | 
4 files changed, 451 insertions, 0 deletions
diff --git a/api/examples/Makefile.am b/api/examples/Makefile.am index 7112c81d6a7..f1a8d12b29e 100644 --- a/api/examples/Makefile.am +++ b/api/examples/Makefile.am @@ -4,3 +4,7 @@ glfsxmp_SOURCES = glfsxmp.c  glfsxmp_CFLAGS = $(GLFS_CFLAGS) -Wall  glfsxmp_LDADD = $(GLFS_LIBS) -lrt +# Install __init__.py (a generated file), and gfapi.py into +# the Python site-packages area +pygfapidir = $(pythondir)/gluster +pygfapi_PYTHON = __init__.py gfapi.py diff --git a/api/examples/__init__.py.in b/api/examples/__init__.py.in new file mode 100644 index 00000000000..280beb2dcec --- /dev/null +++ b/api/examples/__init__.py.in @@ -0,0 +1 @@ +__version__ = "@PACKAGE_VERSION@" diff --git a/api/examples/gfapi.py b/api/examples/gfapi.py new file mode 100755 index 00000000000..3ac67f4d5ba --- /dev/null +++ b/api/examples/gfapi.py @@ -0,0 +1,422 @@ +#!/usr/bin/python + +from ctypes import * +from ctypes.util import find_library +import os +import sys +import time +import types + +# Looks like ctypes is having trouble with dependencies, so just force them to +# load with RTLD_GLOBAL until I figure that out. +glfs = CDLL(find_library("glusterfs"),RTLD_GLOBAL) +xdr = CDLL(find_library("gfxdr"),RTLD_GLOBAL) +api = CDLL(find_library("gfapi"),RTLD_GLOBAL) + +# Wow, the Linux kernel folks really play nasty games with this structure.  If +# you look at the man page for stat(2) and then at this definition you'll note +# two discrepancies.  First, we seem to have st_nlink and st_mode reversed.  In +# fact that's exactly how they're defined *for 64-bit systems*; for 32-bit +# they're in the man-page order.  Even uglier, the man page makes no mention of +# the *nsec fields, but they are very much present and if they're not included +# then we get memory corruption because libgfapi has a structure definition +# that's longer than ours and they overwrite some random bit of memory after +# the space we allocated.  Yes, that's all very disgusting, and I'm still not +# sure this will really work on 32-bit because all of the field types are so +# obfuscated behind macros and feature checks. +class Stat (Structure): +        _fields_ = [ +                ("st_dev",              c_ulong), +                ("st_ino",              c_ulong), +                ("st_nlink",            c_ulong), +                ("st_mode",             c_uint), +                ("st_uid",              c_uint), +                ("st_gid",              c_uint), +                ("st_rdev",             c_ulong), +                ("st_size",             c_ulong), +                ("st_blksize",          c_ulong), +                ("st_blocks",           c_ulong), +                ("st_atime",            c_ulong), +                ("st_atimensec",        c_ulong), +                ("st_mtime",            c_ulong), +                ("st_mtimensec",        c_ulong), +                ("st_ctime",            c_ulong), +                ("st_ctimensec",        c_ulong), +        ] +api.glfs_creat.restype = c_void_p +api.glfs_open.restype = c_void_p +api.glfs_lstat.restype = c_int +api.glfs_lstat.argtypes = [c_void_p, c_char_p, POINTER(Stat)] + +class Dirent (Structure): +        _fields_ = [ +                ("d_ino",       c_ulong), +                ("d_off",       c_ulong), +                ("d_reclen",    c_ushort), +                ("d_type",      c_char), +                ("d_name",      c_char * 256), +        ] +api.glfs_opendir.restype = c_void_p +api.glfs_readdir_r.restype = c_int +api.glfs_readdir_r.argtypes = [c_void_p, POINTER(Dirent), +                               POINTER(POINTER(Dirent))] + +# There's a bit of ctypes glitchiness around __del__ functions and module-level +# variables.  If we unload the module while we still have references to File or +# Volume objects, the module-level variables might have disappeared by the time +# __del__ gets called.  Therefore the objects hold references which they +# release when __del__ is done.  We only actually use the object-local values +# in __del__; for clarity, we just use the simpler module-level form elsewhere. + +class File(object): + +        def __init__ (self, fd): +                # Add a reference so the module-level variable "api" doesn't +                # get yanked out from under us (see comment above File def'n). +                self._api = api +                self.fd = fd + +        def __del__ (self): +                self._api.glfs_close(self.fd) +                self._api = None + +        # File operations, in alphabetical order. + +        def fsync (self): +                return api.glfs_fsync(self.fd) + +        def read (self, buflen, flags=0): +                rbuf = create_string_buffer(buflen) +                rc = api.glfs_read(self.fd,rbuf,buflen,flags) +                if rc > 0: +                        return rbuf.value[:rc] +                else: +                        return rc + +        def read_buffer (self, buf, flags=0): +                return api.glfs_read(self.fd,buf,len(buf),flags) + +        def write (self, data, flags=0): +                return api.glfs_write(self.fd,data,len(data),flags) + +        def fallocate (self, mode, offset, len): +            return api.glfs_fallocate(self.fd, mode, offset, len) + +        def discard (self, offset, len): +            return api.glfs_discard(self.fd, offset, len) + + +class Dir(object): + +        def __init__ (self, fd): +                # Add a reference so the module-level variable "api" doesn't +                # get yanked out from under us (see comment above File def'n). +                self._api = api +                self.fd = fd +                self.cursor = POINTER(Dirent)() + +        def __del__ (self): +                self._api.glfs_closedir(self.fd) +                self._api = None + +        def next (self): +                entry = Dirent() +                entry.d_reclen = 256 +                rc = api.glfs_readdir_r(self.fd,byref(entry),byref(self.cursor)) +                if (rc < 0) or (not self.cursor) or (not self.cursor.contents): +                        return rc +                return entry + +class Volume(object): + +        # Housekeeping functions. + +        def __init__ (self, host, volid, proto="tcp", port=24007): +                # Add a reference so the module-level variable "api" doesn't +                # get yanked out from under us (see comment above File def'n). +                self._api = api +                self.fs = api.glfs_new(volid) +                api.glfs_set_volfile_server(self.fs,proto,host,port) + +        def __del__ (self): +                self._api.glfs_fini(self.fs) +                self._api = None + +        def set_logging (self, path, level): +                api.glfs_set_logging(self.fs,path,level) + +        def mount (self): +                api.glfs_init(self.fs) + +        # File operations, in alphabetical order. + +        def creat (self, path, flags, mode): +                fd = api.glfs_creat(self.fs,path,flags,mode) +                if not fd: +                        return fd +                return File(fd) + +        def getxattr (self, path, key, maxlen): +                buf = create_string_buffer(maxlen) +                rc = api.glfs_getxattr(self.fs,path,key,buf,maxlen) +                if rc < 0: +                        return rc +                return buf.value[:rc] + +        def listxattr (self, path): +                buf = create_string_buffer(512) +                rc = api.glfs_listxattr(self.fs,path,buf,512) +                if rc < 0: +                        return rc +                xattrs = [] +                # Parsing character by character is ugly, but it seems like the +                # easiest way to deal with the "strings separated by NUL in one +                # buffer" format. +                i = 0 +                while i < rc: +                        new_xa = buf.raw[i] +                        i += 1 +                        while i < rc: +                                next_char = buf.raw[i] +                                i += 1 +                                if next_char == '\0': +                                        xattrs.append(new_xa) +                                        break +                                new_xa += next_char +                xattrs.sort() +                return xattrs + +        def lstat (self, path): +                x = Stat() +                rc = api.glfs_lstat(self.fs,path,byref(x)) +                if rc >= 0: +                        return x +                else: +                        return rc + +        def mkdir (self, path): +                return api.glfs_mkdir(self.fs,path) + +        def open (self, path, flags): +                fd = api.glfs_open(self.fs,path,flags) +                if not fd: +                        return fd +                return File(fd) + +        def opendir (self, path): +                fd = api.glfs_opendir(self.fs,path) +                if not fd: +                        return fd +                return Dir(fd) + +        def rename (self, opath, npath): +                return api.glfs_rename(self.fs,opath,npath) + +        def rmdir (self, path): +                return api.glfs_rmdir(self.fs,path) + +        def setxattr (self, path, key, value, vlen): +                return api.glfs_setxattr(self.fs,path,key,value,vlen,0) + +        def unlink (self, path): +                return api.glfs_unlink(self.fs,path) + +if __name__ == "__main__": +        def test_create_write (vol, path, data): +                mypath = path + ".io" +                fd = vol.creat(mypath,os.O_WRONLY|os.O_EXCL,0644) +                if not fd: +                        return False, "creat error" +                rc = fd.write(data) +                if rc != len(data): +                        return False, "wrote %d/%d bytes" % (rc, len(data)) +                return True, "wrote %d bytes" % rc + +        # TBD: this test fails if we do create, open, write, read +        def test_open_read (vol, path, data): +                mypath = path + ".io" +                fd = vol.open(mypath,os.O_RDONLY) +                if not fd: +                        return False, "open error" +                dlen = len(data) * 2 +                buf = fd.read(dlen) +                if type(buf) == types.IntType: +                        return False, "read error %d" % buf +                if len(buf) != len(data): +                        return False, "read %d/%d bytes" % (len(buf), len(data)) +                return True, "read '%s'" % buf + +        def test_lstat (vol, path, data): +                mypath = path + ".io" +                sb = vol.lstat(mypath) +                if type(sb) == types.IntType: +                        return False, "lstat error %d" % sb +                if sb.st_size != len(data): +                        return False, "lstat size is %d, expected %d" % ( +                                sb.st_size, len(data)) +                return True, "lstat got correct size %d" % sb.st_size + +        def test_rename (vol, path, data): +                opath = path + ".io" +                npath = path + ".tmp" +                rc = vol.rename(opath,npath) +                if rc < 0: +                        return False, "rename error %d" % rc +                ofd = vol.open(opath,os.O_RDWR) +                if isinstance(ofd,File): +                        return False, "old path working after rename" +                nfd = vol.open(npath,os.O_RDWR) +                if isinstance(nfd,File): +                        return False, "new path not working after rename" +                return True, "rename worked" + +        def test_unlink (vol, path, data): +                mypath = path + ".tmp" +                rc = vol.unlink(mypath) +                if rc < 0: +                        return False, "unlink error %d" % fd +                fd = vol.open(mypath,os.O_RDWR) +                if isinstance(fd,File): +                        return False, "path still usable after unlink" +                return True, "unlink worked" + +        def test_mkdir (vol, path, data): +                mypath = path + ".dir" +                rc = vol.mkdir(mypath) +                if rc < 0: +                        return False, "mkdir error %d" % rc +                return True, "mkdir worked" + +        def test_create_in_dir (vol, path, data): +                mypath = path + ".dir/probe" +                fd = vol.creat(mypath,os.O_RDWR,0644) +                if not isinstance(fd,File): +                        return False, "create (in dir) error" +                return True, "create (in dir) worked" + +        def test_dir_listing (vol, path, data): +                mypath = path + ".dir" +                fd = vol.opendir(mypath) +                if not isinstance(fd,Dir): +                        return False, "opendir error %d" % fd +                files = [] +                while True: +                        ent = fd.next() +                        if not isinstance(ent,Dirent): +                                break +                        name = ent.d_name[:ent.d_reclen] +                        files.append(name) +                if files != [".", "..", "probe"]: +                        return False, "wrong directory contents" +                return True, "directory listing worked" + +        def test_unlink_in_dir (vol, path, data): +                mypath = path + ".dir/probe" +                rc = vol.unlink(mypath) +                if rc < 0: +                        return False, "unlink (in dir) error %d" % rc +                return True, "unlink (in dir) worked" + +        def test_rmdir (vol, path, data): +                mypath = path + ".dir" +                rc = vol.rmdir(mypath) +                if rc < 0: +                        return False, "rmdir error %d" % rc +                sb = vol.lstat(mypath) +                if not isinstance(sb,Stat): +                        return False, "dir still there after rmdir" +                return True, "rmdir worked" + +        def test_setxattr (vol, path, data): +                mypath = path + ".xa" +                fd = vol.creat(mypath,os.O_RDWR|os.O_EXCL,0644) +                if not fd: +                        return False, "creat (xattr test) error" +                key1, key2 = "hello", "goodbye" +                if vol.setxattr(mypath,"trusted.key1",key1,len(key1)) < 0: +                        return False, "setxattr (key1) error" +                if vol.setxattr(mypath,"trusted.key2",key2,len(key2)) < 0: +                        return False, "setxattr (key2) error" +                return True, "setxattr worked" + +        def test_getxattr (vol, path, data): +                mypath = path + ".xa" +                buf = vol.getxattr(mypath,"trusted.key1",32) +                if type(buf) == types.IntType: +                        return False, "getxattr error" +                if buf != "hello": +                        return False, "wrong getxattr value %s" % buf +                return True, "getxattr worked" + +        def test_listxattr (vol, path, data): +                mypath = path + ".xa" +                xattrs = vol.listxattr(mypath) +                if type(xattrs) == types.IntType: +                        return False, "listxattr error" +                if xattrs != ["trusted.key1","trusted.key2"]: +                        return False, "wrong listxattr value %s" % repr(xattrs) +                return True, "listxattr worked" + +        def test_fallocate (vol, path, data): +                mypath = path + ".io" +                fd = vol.creat(mypath,os.O_WRONLY|os.O_EXCL,0644) +                if not fd: +                        return False, "creat error" +		rc = fd.fallocate(0, 0, 1024*1024) +                if rc != 0: +                        return False, "fallocate error" +		rc = fd.discard(4096, 4096) +		if rc != 0: +			return False, "discard error" +                return True, "fallocate/discard worked" + +        test_list = ( +                test_create_write, +                test_open_read, +                test_lstat, +                test_rename, +                test_unlink, +                test_mkdir, +                test_create_in_dir, +                test_dir_listing, +                test_unlink_in_dir, +                test_rmdir, +                test_setxattr, +                test_getxattr, +                test_listxattr, +                test_fallocate, +        ) + +        ok_to_fail = ( +                # TBD: this fails opening the new file, even though the file +                # did get renamed.  Looks like a gfapi bug, not ours. +                (test_rename, "new path not working after rename"), +                # TBD: similar, call returns error even though it worked +                (test_rmdir, "dir still there after rmdir"), +        ) + +        volid, path = sys.argv[1:3] +        data = "fubar" +        vol = Volume("localhost",volid) +        vol.set_logging("/dev/null",7) +        #vol.set_logging("/dev/stderr",7) +        vol.mount() + +        failures = 0 +        expected = 0 +        for t in test_list: +                rc, msg = t(vol,path,data) +                if rc: +                        print "PASS: %s" % msg +                else: +                        print "FAIL: %s" % msg +                        failures += 1 +                        for otf in ok_to_fail: +                                if (t == otf[0]) and (msg == otf[1]): +                                        print "  (skipping known failure)" +                                        expected += 1 +                                        break # from the *inner* for loop +                        else: +                                break # from the *outer* for loop + +        print "%d failures (%d expected)" % (failures, expected) diff --git a/api/examples/setup.py.in b/api/examples/setup.py.in new file mode 100644 index 00000000000..908ccda8409 --- /dev/null +++ b/api/examples/setup.py.in @@ -0,0 +1,24 @@ +from distutils.core import setup + +DESC = """GlusterFS is a clustered file-system capable of scaling to +several petabytes. It aggregates various storage bricks over Infiniband +RDMA or TCP/IP interconnect into one large parallel network file system. +GlusterFS is one of the most sophisticated file systems in terms of +features and extensibility.  It borrows a powerful concept called +Translators from GNU Hurd kernel. Much of the code in GlusterFS is in +user space and easily manageable. + +This package contains the Python interface to the libgfapi library.""" + +setup( +    name='glusterfs-api', +    version='@PACKAGE_VERSION@', +    description='Python client library for the GlusterFS libgfapi', +    long_description=DESC, +    author='Gluster Community', +    author_email='gluster-devel@gluster.org', +    license='LGPLv3', +    url='http://gluster.org/', +    package_dir={'gluster':''}, +    packages=['gluster'] +)  | 
