summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md37
-rw-r--r--doc/markdown/dev_guide.md1
-rwxr-xr-xgluster/api.py304
-rwxr-xr-xgluster/gfapi.py29
-rw-r--r--test/functional/libgfapi-python-tests.py24
5 files changed, 232 insertions, 163 deletions
diff --git a/README.md b/README.md
index ad918e9..3720fcb 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,44 @@ $ cd libgfapi-python
```
$ sudo python setup.py install
```
+# Usage
+```python
+from gluster import gfapi
+import os
+
+## Create virtual mount
+volume = gfapi.Volume(....)
+volume.mount()
+
+## Create a new directory
+volume.mkdir('newdir', 0755)
+
+## Create a new directory recursively
+volume.makedirs('/somedir/dir',0755)
+
+## Delete a directory
+volume.rmdir('/somedir/dir')
+
+## Create a file from a string using fopen. w+: open file for reading and writing
+with volume.fopen('somefile.txt', 'w+') as fd:
+ fd.write("shadowfax")
+
+## Read a file. r: open file for only reading
+with volume.fopen('somefile.txt', 'r') as fd:
+ print fd.read()
+
+## Write to an existing file. a+: open a file for reading and appending
+with volume.fopen('somefile.txt','a+') as fd:
+ fd.write("\n some new line in our file")
+
+## Delete a file
+volume.unlink('somefile.txt')
+
+## Unmount a volume
+volume.unmount()
+
+```
# Development
* [Developer Guide](doc/markdown/dev_guide.md)
diff --git a/doc/markdown/dev_guide.md b/doc/markdown/dev_guide.md
index 8ca72c6..770de4b 100644
--- a/doc/markdown/dev_guide.md
+++ b/doc/markdown/dev_guide.md
@@ -204,6 +204,7 @@ If new functionality has been added, it is highly recommended that one or more t
The functional tests expects a GlusterFS `test` volume to be created and accessible.
+Note: You may need to update your test configuration file, located at `test/test.conf` with your hostname and volume name.
To run the functional tests, please type:
```
diff --git a/gluster/api.py b/gluster/api.py
index 0ec5413..c7270bb 100755
--- a/gluster/api.py
+++ b/gluster/api.py
@@ -257,216 +257,222 @@ class Dirent (ctypes.Structure):
# }
#
+def gfapi_prototype(method_name, restype, *argtypes):
+ """
+ Create a named foreign function belonging to gfapi
+
+ :param method_name: Name of the foreign function
+ :param restype: resulting type
+ :param argtypes: arguments that represent argument types
+ :returns: foreign function of gfapi library
+ """
+ # use_errno=True ensures that errno is exposed by ctypes.get_errno()
+ return ctypes.CFUNCTYPE(restype, *argtypes, use_errno=True)(
+ (method_name, client)
+ )
+
# Define function prototypes for the wrapper functions.
-glfs_init = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_init', client))
+glfs_init = gfapi_prototype('glfs_init', ctypes.c_int, ctypes.c_void_p)
-glfs_statvfs = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_void_p)(('glfs_statvfs', client))
+glfs_statvfs = gfapi_prototype('glfs_statvfs', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p)
-glfs_new = ctypes.CFUNCTYPE(
- ctypes.c_void_p, ctypes.c_char_p)(('glfs_new', client))
+glfs_new = gfapi_prototype('glfs_new', ctypes.c_void_p, ctypes.c_char_p)
-glfs_set_volfile_server = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_char_p,
- ctypes.c_int)(('glfs_set_volfile_server', client)) # noqa
+glfs_set_volfile_server = gfapi_prototype(
+ 'glfs_set_volfile_server', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int)
-glfs_set_logging = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int)(('glfs_set_logging', client))
+glfs_set_logging = gfapi_prototype('glfs_set_logging', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int)
-glfs_fini = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_fini', client))
+glfs_fini = gfapi_prototype('glfs_fini', ctypes.c_int,
+ ctypes.c_void_p)
-glfs_creat = ctypes.CFUNCTYPE(ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int,
- ctypes.c_uint,
- use_errno=True)(('glfs_creat', client))
+glfs_creat = gfapi_prototype('glfs_creat', ctypes.c_void_p, ctypes.c_void_p,
+ ctypes.c_char_p, ctypes.c_int, ctypes.c_uint)
-glfs_open = ctypes.CFUNCTYPE(ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int,
- use_errno=True)(('glfs_open', client))
+glfs_open = gfapi_prototype('glfs_open', ctypes.c_void_p, ctypes.c_void_p,
+ ctypes.c_char_p, ctypes.c_int)
+
+glfs_close = gfapi_prototype('glfs_close', ctypes.c_int,
+ ctypes.c_void_p)
-glfs_close = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_close', client))
+glfs_lstat = gfapi_prototype('glfs_lstat', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.POINTER(Stat))
-glfs_lstat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
- ctypes.POINTER(Stat))(('glfs_lstat', client))
+glfs_stat = gfapi_prototype('glfs_stat', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.POINTER(Stat))
-glfs_stat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
- ctypes.POINTER(Stat))(('glfs_stat', client))
+glfs_fstat = gfapi_prototype('glfs_fstat', ctypes.c_int,
+ ctypes.c_void_p, ctypes.POINTER(Stat))
-glfs_fstat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(
- Stat))(('glfs_fstat', client))
+glfs_chmod = gfapi_prototype('glfs_chmod', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_ushort)
-glfs_chmod = ctypes.CFUNCTYPE(ctypes.c_int,
+glfs_fchmod = gfapi_prototype('glfs_fchmod', ctypes.c_int,
ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_ushort)(('glfs_chmod', client))
+ ctypes.c_ushort)
-glfs_fchmod = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_ushort)(('glfs_fchmod', client))
+glfs_chown = gfapi_prototype('glfs_chown', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_uint,
+ ctypes.c_uint)
-glfs_chown = ctypes.CFUNCTYPE(ctypes.c_int,
+glfs_lchown = gfapi_prototype('glfs_lchown', ctypes.c_int,
ctypes.c_void_p,
ctypes.c_char_p,
ctypes.c_uint,
- ctypes.c_uint)(('glfs_chown', client))
+ ctypes.c_uint)
-glfs_lchown = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_uint,
- ctypes.c_uint)(('glfs_lchown', client))
+glfs_fchown = gfapi_prototype('glfs_fchown', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_uint,
+ ctypes.c_uint)
-glfs_fchown = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_uint,
- ctypes.c_uint)(('glfs_fchown', client))
+glfs_dup = gfapi_prototype('glfs_dup', ctypes.c_void_p,
+ ctypes.c_void_p)
-glfs_dup = ctypes.CFUNCTYPE(
- ctypes.c_void_p, ctypes.c_void_p)(('glfs_dup', client))
+glfs_fdatasync = gfapi_prototype('glfs_fdatasync', ctypes.c_int,
+ ctypes.c_void_p)
-glfs_fdatasync = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_fdatasync', client))
+glfs_fsync = gfapi_prototype('glfs_fsync', ctypes.c_int,
+ ctypes.c_void_p)
-glfs_fsync = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_fsync', client))
+glfs_lseek = gfapi_prototype('glfs_lseek', ctypes.c_ulong,
+ ctypes.c_void_p, ctypes.c_ulong,
+ ctypes.c_int)
-glfs_lseek = ctypes.CFUNCTYPE(ctypes.c_ulong, ctypes.c_void_p, ctypes.c_ulong,
- ctypes.c_int)(('glfs_lseek', client))
+glfs_read = gfapi_prototype('glfs_read', ctypes.c_ssize_t,
+ ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t,
+ ctypes.c_int)
-glfs_read = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
+glfs_write = gfapi_prototype('glfs_write', ctypes.c_ssize_t,
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t,
- ctypes.c_int)(('glfs_read', client))
+ ctypes.c_int)
-glfs_write = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
- ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_size_t,
- ctypes.c_int)(('glfs_write', client))
+glfs_getxattr = gfapi_prototype('glfs_getxattr', ctypes.c_ssize_t,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t)
-glfs_getxattr = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
+glfs_listxattr = gfapi_prototype('glfs_listxattr', ctypes.c_ssize_t,
ctypes.c_void_p,
ctypes.c_char_p,
- ctypes.c_char_p,
ctypes.c_void_p,
- ctypes.c_size_t)(('glfs_getxattr', client))
+ ctypes.c_size_t)
-glfs_listxattr = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_void_p,
- ctypes.c_size_t)(('glfs_listxattr', client))
+glfs_removexattr = gfapi_prototype('glfs_removexattr', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p)
-glfs_removexattr = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_char_p)(('glfs_removexattr', client)) # noqa
+glfs_setxattr = gfapi_prototype('glfs_setxattr', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t,
+ ctypes.c_int)
-glfs_setxattr = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_char_p,
- ctypes.c_void_p,
- ctypes.c_size_t,
- ctypes.c_int)(('glfs_setxattr', client))
+glfs_rename = gfapi_prototype('glfs_rename', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p)
-glfs_rename = ctypes.CFUNCTYPE(ctypes.c_int,
+glfs_symlink = gfapi_prototype('glfs_symlink', ctypes.c_int,
ctypes.c_void_p,
ctypes.c_char_p,
- ctypes.c_char_p)(('glfs_rename', client))
-
-glfs_symlink = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_char_p)(('glfs_symlink', client))
+ ctypes.c_char_p)
-glfs_unlink = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p)(('glfs_unlink', client))
+glfs_unlink = gfapi_prototype('glfs_unlink', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_char_p)
-glfs_readdir_r = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p,
- ctypes.POINTER(Dirent),
- ctypes.POINTER(ctypes.POINTER(Dirent)))(('glfs_readdir_r', client)) # noqa
+glfs_readdir_r = gfapi_prototype('glfs_readdir_r', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.POINTER(Dirent),
+ ctypes.POINTER(ctypes.POINTER(Dirent)))
-glfs_closedir = ctypes.CFUNCTYPE(
- ctypes.c_int, ctypes.c_void_p)(('glfs_closedir', client))
+glfs_closedir = gfapi_prototype('glfs_closedir', ctypes.c_int,
+ ctypes.c_void_p)
+glfs_mkdir = gfapi_prototype('glfs_mkdir', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.c_ushort)
-glfs_mkdir = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
- ctypes.c_ushort)(('glfs_mkdir', client))
+glfs_opendir = gfapi_prototype('glfs_opendir', ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_char_p)
-glfs_opendir = ctypes.CFUNCTYPE(ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_char_p)(('glfs_opendir', client))
+glfs_rmdir = gfapi_prototype('glfs_rmdir', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p)
-glfs_rmdir = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p)(('glfs_rmdir', client))
+glfs_setfsuid = gfapi_prototype('glfs_setfsuid', ctypes.c_int,
+ ctypes.c_uint)
-glfs_setfsuid = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_uint)(('glfs_setfsuid', client))
+glfs_setfsgid = gfapi_prototype('glfs_setfsgid', ctypes.c_int,
+ ctypes.c_uint)
-glfs_setfsgid = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_uint)(('glfs_setfsgid', client))
+glfs_ftruncate = gfapi_prototype('glfs_ftruncate', ctypes.c_int,
+ ctypes.c_void_p, ctypes.c_int)
-glfs_ftruncate = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p,
- ctypes.c_int)(('glfs_ftruncate', client))
+glfs_fgetxattr = gfapi_prototype('glfs_fgetxattr', ctypes.c_ssize_t,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t)
-glfs_fgetxattr = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_void_p,
- ctypes.c_size_t)(('glfs_fgetxattr', client))
+glfs_fremovexattr = gfapi_prototype('glfs_fremovexattr', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p)
-glfs_fremovexattr = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p)(('glfs_fremovexattr',
- client))
+glfs_fsetxattr = gfapi_prototype('glfs_fsetxattr', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t,
+ ctypes.c_int)
-glfs_fsetxattr = ctypes.CFUNCTYPE(ctypes.c_int,
+glfs_flistxattr = gfapi_prototype('glfs_flistxattr', ctypes.c_ssize_t,
ctypes.c_void_p,
- ctypes.c_char_p,
ctypes.c_void_p,
- ctypes.c_size_t,
- ctypes.c_int)(('glfs_fsetxattr', client))
+ ctypes.c_size_t)
-glfs_flistxattr = ctypes.CFUNCTYPE(ctypes.c_ssize_t,
- ctypes.c_void_p,
- ctypes.c_void_p,
- ctypes.c_size_t)(('glfs_flistxattr',
- client))
+glfs_access = gfapi_prototype('glfs_access', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int)
-glfs_access = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_int)(('glfs_access', client))
+glfs_readlink = gfapi_prototype('glfs_readlink', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_size_t)
-glfs_readlink = ctypes.CFUNCTYPE(ctypes.c_int,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_char_p,
- ctypes.c_size_t)(('glfs_readlink', client))
+glfs_chdir = gfapi_prototype('glfs_chdir', ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p)
-glfs_chdir = ctypes.CFUNCTYPE(ctypes.c_int,
+glfs_getcwd = gfapi_prototype('glfs_getcwd', ctypes.c_char_p,
ctypes.c_void_p,
- ctypes.c_char_p)(('glfs_chdir', client))
-
-glfs_getcwd = ctypes.CFUNCTYPE(ctypes.c_char_p,
- ctypes.c_void_p,
- ctypes.c_char_p,
- ctypes.c_size_t)(('glfs_getcwd', client))
+ ctypes.c_char_p,
+ ctypes.c_size_t)
# TODO: # discard and fallocate fails with "AttributeError: /lib64/libgfapi.so.0: undefined symbol: glfs_discard", # noqa
diff --git a/gluster/gfapi.py b/gluster/gfapi.py
index 7fee1bb..3a344c8 100755
--- a/gluster/gfapi.py
+++ b/gluster/gfapi.py
@@ -322,7 +322,7 @@ class File(object):
# In python 2.x, read() always returns a string. It's really upto
# the consumer to decode this string into whatever encoding it was
# written with.
- return rbuf.value[:ret]
+ return rbuf.raw[:ret]
elif ret < 0:
err = ctypes.get_errno()
raise OSError(err, os.strerror(err))
@@ -432,24 +432,27 @@ class Volume(object):
self.fs = api.glfs_new(self.volname)
if not self.fs:
- raise LibgfapiException("glfs_new(%s) failed." % (self.volname))
+ err = ctypes.get_errno()
+ raise LibgfapiException("glfs_new(%s) failed: %s" %
+ (self.volname, os.strerror(err)))
ret = api.glfs_set_volfile_server(self.fs, self.protocol,
self.host, self.port)
if ret < 0:
- # FIXME: For some reason, get_errno() is not able to capture
- # proper errno. Until then..
- # https://bugzilla.redhat.com/show_bug.cgi?id=1196161
+ err = ctypes.get_errno()
raise LibgfapiException("glfs_set_volfile_server(%s, %s, %s, "
- "%s) failed." % (self.fs, self.protocol,
- self.host, self.port))
+ "%s) failed: %s" % (self.fs, self.protocol,
+ self.host, self.port,
+ os.strerror(err)))
self.set_logging(self.log_file, self.log_level)
if self.fs and not self._mounted:
ret = api.glfs_init(self.fs)
if ret < 0:
- raise LibgfapiException("glfs_init(%s) failed." % (self.fs))
+ err = ctypes.get_errno()
+ raise LibgfapiException("glfs_init(%s) failed: %s" %
+ (self.fs, os.strerror(err)))
else:
self._mounted = True
@@ -463,7 +466,9 @@ class Volume(object):
if self.fs:
ret = self._api.glfs_fini(self.fs)
if ret < 0:
- raise LibgfapiException("glfs_fini(%s) failed." % (self.fs))
+ err = ctypes.get_errno()
+ raise LibgfapiException("glfs_fini(%s) failed: %s" %
+ (self.fs, os.strerror(err)))
else:
# Succeeded. Protect against multiple umount() calls.
self._mounted = False
@@ -497,8 +502,10 @@ class Volume(object):
if self.fs:
ret = api.glfs_set_logging(self.fs, self.log_file, self.log_level)
if ret < 0:
- raise LibgfapiException("glfs_set_logging(%s, %s) failed." %
- (self.log_file, self.log_level))
+ err = ctypes.get_errno()
+ raise LibgfapiException("glfs_set_logging(%s, %s) failed: %s" %
+ (self.log_file, self.log_level,
+ os.strerror(err)))
self.log_file = log_file
self.log_level = log_level
diff --git a/test/functional/libgfapi-python-tests.py b/test/functional/libgfapi-python-tests.py
index cef0f01..bfd5c50 100644
--- a/test/functional/libgfapi-python-tests.py
+++ b/test/functional/libgfapi-python-tests.py
@@ -133,7 +133,13 @@ class FileOpsTest(unittest.TestCase):
# invalid mode
self.assertRaises(ValueError, self.vol.fopen, "file", 'x+')
# file does not exist
- self.assertRaises(OSError, self.vol.fopen, "file", 'r')
+ try:
+ self.vol.fopen("file", "r")
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ self.fail("Expecting ENOENT")
+ else:
+ self.fail("Expecting ENOENT")
def test_fopen(self):
# Default permission should be 0666
@@ -290,7 +296,13 @@ class FileOpsTest(unittest.TestCase):
def test_rename(self):
newpath = self.path + ".rename"
self.vol.rename(self.path, newpath)
- self.assertRaises(OSError, self.vol.lstat, self.path)
+ try:
+ self.vol.lstat(self.path)
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ self.fail("Expecting ENOENT")
+ else:
+ self.fail("Expecting ENOENT")
def test_stat(self):
sb = self.vol.stat(self.path)
@@ -299,7 +311,13 @@ class FileOpsTest(unittest.TestCase):
def test_unlink(self):
self.vol.unlink(self.path)
- self.assertRaises(OSError, self.vol.lstat, self.path)
+ try:
+ self.vol.lstat(self.path)
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ self.fail("Expecting ENOENT")
+ else:
+ self.fail("Expecting ENOENT")
def test_setxattr(self):
value = "hello world"