summaryrefslogtreecommitdiffstats
path: root/gluster
diff options
context:
space:
mode:
authorHumble Chirammal <hchiramm@redhat.com>2015-02-17 17:26:43 +0530
committerhchiramm <hchiramm@redhat.com>2015-04-25 13:24:33 +0530
commit75bb7da7094e0344fc7be93408ff6d6ead855253 (patch)
tree7fe491ac83a1cb94dc992d4eea344c7edc5137a3 /gluster
parent436a05174b96a90216925424bbb8bb1bcbb1eb2e (diff)
Rename module name from glusterfs to gluster
The goal is to consolidate all gluster related python packages under single namespace "gluster". From client's perspective, it was: from glusterfs import gfapi Henceforth, it wil be: from gluster import gfapi Change-Id: If2509f570563ae7660892963607c9474313f803c Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
Diffstat (limited to 'gluster')
-rw-r--r--gluster/__init__.py49
-rwxr-xr-xgluster/api.py402
-rwxr-xr-xgluster/gfapi.py578
3 files changed, 1029 insertions, 0 deletions
diff --git a/gluster/__init__.py b/gluster/__init__.py
new file mode 100644
index 0000000..4562ec8
--- /dev/null
+++ b/gluster/__init__.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2012-2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from pkgutil import extend_path
+__path__ = extend_path(__path__, __name__)
+
+
+class PkgInfo(object):
+ def __init__(self, canonical_version, release, name, final):
+ self.canonical_version = canonical_version
+ self.release = release
+ self.name = name
+ self.final = final
+
+ def save_config(self, filename):
+ """
+ Creates a file with the package configuration which can be sourced by
+ a bash script.
+ """
+ with open(filename, 'w') as fd:
+ fd.write("NAME=%s\n" % self.name)
+ fd.write("VERSION=%s\n" % self.canonical_version)
+ fd.write("RELEASE=%s\n" % self.release)
+
+ @property
+ def pretty_version(self):
+ if self.final:
+ return self.canonical_version
+ else:
+ return '%s-dev' % (self.canonical_version,)
+
+
+###
+### Change the Package version here
+###
+_pkginfo = PkgInfo('0.0.1', '0', 'python-libgfapi', False)
+__version__ = _pkginfo.pretty_version
+__canonical_version__ = _pkginfo.canonical_version
diff --git a/gluster/api.py b/gluster/api.py
new file mode 100755
index 0000000..803a778
--- /dev/null
+++ b/gluster/api.py
@@ -0,0 +1,402 @@
+
+# Copyright (c) 2012-2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import ctypes
+from ctypes.util import find_library
+
+
+# Looks like ctypes is having trouble with dependencies, so just force them to
+# load with RTLD_GLOBAL until I figure that out.
+client = ctypes.CDLL(find_library("gfapi"), ctypes.RTLD_GLOBAL, use_errno=True)
+# The above statement "may" fail with OSError on some systems if libgfapi.so
+# is located in /usr/local/lib/. This happens when glusterfs is installed from
+# source. Refer to: http://bugs.python.org/issue18502
+
+# 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 (ctypes.Structure):
+ _fields_ = [
+ ("st_dev", ctypes.c_ulong),
+ ("st_ino", ctypes.c_ulong),
+ ("st_nlink", ctypes.c_ulong),
+ ("st_mode", ctypes.c_uint),
+ ("st_uid", ctypes.c_uint),
+ ("st_gid", ctypes.c_uint),
+ ("st_rdev", ctypes.c_ulong),
+ ("st_size", ctypes.c_ulong),
+ ("st_blksize", ctypes.c_ulong),
+ ("st_blocks", ctypes.c_ulong),
+ ("st_atime", ctypes.c_ulong),
+ ("st_atimensec", ctypes.c_ulong),
+ ("st_mtime", ctypes.c_ulong),
+ ("st_mtimensec", ctypes.c_ulong),
+ ("st_ctime", ctypes.c_ulong),
+ ("st_ctimensec", ctypes.c_ulong),
+ ]
+
+
+class Statvfs (ctypes.Structure):
+ _fields_ = [
+ ("f_bsize", ctypes.c_ulong),
+ ("f_frsize", ctypes.c_ulong),
+ ("f_blocks", ctypes.c_ulong),
+ ("f_bfree", ctypes.c_ulong),
+ ("f_bavail", ctypes.c_ulong),
+ ("f_files", ctypes.c_ulong),
+ ("f_ffree", ctypes.c_ulong),
+ ("f_favail", ctypes.c_ulong),
+ ("f_fsid", ctypes.c_ulong),
+ ("f_flag", ctypes.c_ulong),
+ ("f_namemax", ctypes.c_ulong),
+ ("__f_spare", ctypes.c_int * 6),
+ ]
+
+
+class Dirent (ctypes.Structure):
+ _fields_ = [
+ ("d_ino", ctypes.c_ulong),
+ ("d_off", ctypes.c_ulong),
+ ("d_reclen", ctypes.c_ushort),
+ ("d_type", ctypes.c_char),
+ ("d_name", ctypes.c_char * 256),
+ ]
+
+
+# Here is the reference card of libgfapi library exported
+# apis with its different versions.
+#
+# GFAPI_3.4.0 {
+# glfs_new;
+# glfs_set_volfile;
+# glfs_set_volfile_server;
+# glfs_set_logging;
+# glfs_init;
+# glfs_fini;
+# glfs_open;
+# glfs_creat;
+# glfs_close;
+# glfs_from_glfd;
+# glfs_set_xlator_option;
+# glfs_read;
+# glfs_write;
+# glfs_read_async;
+# glfs_write_async;
+# glfs_readv;
+# glfs_writev;
+# glfs_readv_async;
+# glfs_writev_async;
+# glfs_pread;
+# glfs_pwrite;
+# glfs_pread_async;
+# glfs_pwrite_async;
+# glfs_preadv;
+# glfs_pwritev;
+# glfs_preadv_async;
+# glfs_pwritev_async;
+# glfs_lseek;
+# glfs_truncate;
+# glfs_ftruncate;
+# glfs_ftruncate_async;
+# glfs_lstat;
+# glfs_stat;
+# glfs_fstat;
+# glfs_fsync;
+# glfs_fsync_async;
+# glfs_fdatasync;
+# glfs_fdatasync_async;
+# glfs_access;
+# glfs_symlink;
+# glfs_readlink;
+# glfs_mknod;
+# glfs_mkdir;
+# glfs_unlink;
+# glfs_rmdir;
+# glfs_rename;
+# glfs_link;
+# glfs_opendir;
+# glfs_readdir_r;
+# glfs_readdirplus_r;
+# glfs_telldir;
+# glfs_seekdir;
+# glfs_closedir;
+# glfs_statvfs;
+# glfs_chmod;
+# glfs_fchmod;
+# glfs_chown;
+# glfs_lchown;
+# glfs_fchown;
+# glfs_utimens;
+# glfs_lutimens;
+# glfs_futimens;
+# glfs_getxattr;
+# glfs_lgetxattr;
+# glfs_fgetxattr;
+# glfs_listxattr;
+# glfs_llistxattr;
+# glfs_flistxattr;
+# glfs_setxattr;
+# glfs_lsetxattr;
+# glfs_fsetxattr;
+# glfs_removexattr;
+# glfs_lremovexattr;
+# glfs_fremovexattr;
+# glfs_getcwd;
+# glfs_chdir;
+# glfs_fchdir;
+# glfs_realpath;
+# glfs_posix_lock;
+# glfs_dup;
+#
+# }
+#
+# GFAPI_3.4.2 {
+# glfs_setfsuid;
+# glfs_setfsgid;
+# glfs_setfsgroups;
+# glfs_h_lookupat;
+# glfs_h_creat;
+# glfs_h_mkdir;
+# glfs_h_mknod;
+# glfs_h_symlink;
+# glfs_h_unlink;
+# glfs_h_close;
+# glfs_h_truncate;
+# glfs_h_stat;
+# glfs_h_getattrs;
+# glfs_h_setattrs;
+# glfs_h_readlink;
+# glfs_h_link;
+# glfs_h_rename;
+# glfs_h_extract_handle;
+# glfs_h_create_from_handle;
+# glfs_h_opendir;
+# glfs_h_open;
+# }
+#
+# GFAPI_3.5.0 {
+#
+# glfs_get_volumeid;
+# glfs_readdir;
+# glfs_readdirplus;
+# glfs_fallocate;
+# glfs_discard;
+# glfs_discard_async;
+# glfs_zerofill;
+# glfs_zerofill_async;
+# glfs_caller_specific_init;
+# glfs_h_setxattrs;
+#
+# }
+#
+# GFAPI_3.5.1 {
+#
+# glfs_unset_volfile_server;
+# glfs_h_getxattrs;
+# glfs_h_removexattrs;
+#
+# }
+#
+# GFAPI_3.6.0 {
+#
+# glfs_get_volfile;
+# glfs_h_access;
+#
+# }
+#
+
+# Define function prototypes for the wrapper functions.
+
+glfs_init = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_init', client))
+
+glfs_statvfs = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p)(('glfs_statvfs', client))
+
+glfs_new = ctypes.CFUNCTYPE(
+ ctypes.c_void_p, ctypes.c_char_p)(('glfs_new', client))
+
+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_logging = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_int)(('glfs_set_logging', client))
+
+glfs_fini = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_fini', client))
+
+
+glfs_close = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_close', client))
+
+glfs_lstat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.POINTER(Stat))(('glfs_lstat', client))
+
+glfs_stat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.POINTER(Stat))(('glfs_stat', client))
+
+glfs_fstat = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(
+ Stat))(('glfs_fstat', client))
+
+glfs_chmod = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_ushort)(('glfs_chmod', client))
+
+glfs_fchmod = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_ushort)(('glfs_fchmod', client))
+
+glfs_chown = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_uint,
+ ctypes.c_uint)(('glfs_chown', client))
+
+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 = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_uint,
+ ctypes.c_uint)(('glfs_fchown', client))
+
+glfs_dup = ctypes.CFUNCTYPE(
+ ctypes.c_void_p, ctypes.c_void_p)(('glfs_dup', client))
+
+glfs_fdatasync = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_fdatasync', client))
+
+glfs_fsync = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_fsync', client))
+
+glfs_lseek = ctypes.CFUNCTYPE(ctypes.c_ulong, ctypes.c_void_p, ctypes.c_ulong,
+ ctypes.c_int)(('glfs_lseek', client))
+
+glfs_read = ctypes.CFUNCTYPE(ctypes.c_size_t,
+ ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t,
+ ctypes.c_int)(('glfs_read', client))
+
+glfs_write = ctypes.CFUNCTYPE(ctypes.c_size_t,
+ ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t,
+ ctypes.c_int)(('glfs_write', client))
+
+glfs_getxattr = ctypes.CFUNCTYPE(ctypes.c_size_t,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t)(('glfs_getxattr', client))
+
+glfs_listxattr = ctypes.CFUNCTYPE(ctypes.c_size_t,
+ ctypes.c_void_p,
+ ctypes.c_char_p,
+ ctypes.c_void_p,
+ ctypes.c_size_t)(('glfs_listxattr', client))
+
+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 = 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 = ctypes.CFUNCTYPE(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))
+
+glfs_unlink = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p)(('glfs_unlink', client))
+
+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_closedir = ctypes.CFUNCTYPE(
+ ctypes.c_int, ctypes.c_void_p)(('glfs_closedir', client))
+
+
+glfs_mkdir = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_char_p,
+ ctypes.c_ushort)(('glfs_mkdir', client))
+
+glfs_opendir = ctypes.CFUNCTYPE(ctypes.c_void_p,
+ ctypes.c_void_p,
+ ctypes.c_char_p)(('glfs_opendir', client))
+
+glfs_rmdir = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_void_p,
+ ctypes.c_char_p)(('glfs_rmdir', client))
+
+glfs_setfsuid = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_uint)(('glfs_setfsuid', client))
+
+glfs_setfsgid = ctypes.CFUNCTYPE(ctypes.c_int,
+ ctypes.c_uint)(('glfs_setfsgid', client))
+
+
+# TODO: creat and open fails on test_create_file_already_exists & test_open_file_not_exist functional testing, # noqa
+# when defined via function prototype.. Need to find RCA. For time being, using it from 'api.glfs_' # noqa
+#_glfs_creat = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_uint) # noqa
+ # (('glfs_creat', client)) # noqa
+#_glfs_open = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int) # noqa
+# (('glfs_open', client)) # noqa
+# TODO: # discard and fallocate fails with "AttributeError: /lib64/libgfapi.so.0: undefined symbol: glfs_discard", # noqa
+# for time being, using it from api.* # noqa
+# glfs_discard = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_ulong, ctypes.c_size_t)(('glfs_discard', client)) # noqa
+#_glfs_fallocate = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_ulong, ctypes.c_size_t) # noqa
+# (('glfs_fallocate', client)) # noqa
+
+
+#glfs_creat = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_uint)(('glfs_creat', client)) # noqa
+#glfs_open = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int)(('glfs_open', client)) # noqa
+
+#glfs_discard = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_ulong, ctypes.c_size_t)(('glfs_discard', client)) # noqa
+#glfs_fallocate = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_ulong, ctypes.c_size_t)(('glfs_fallocate', client)) # noqa
diff --git a/gluster/gfapi.py b/gluster/gfapi.py
new file mode 100755
index 0000000..3f7a068
--- /dev/null
+++ b/gluster/gfapi.py
@@ -0,0 +1,578 @@
+# Copyright (c) 2012-2014 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import ctypes
+import os
+import stat
+import errno
+from gluster import api
+
+
+class File(object):
+
+ def __init__(self, fd, path=None):
+ self.fd = fd
+ self.originalpath = path
+
+ def __enter__(self):
+ if self.fd is None:
+ # __enter__ should only be called within the context
+ # of a 'with' statement when opening a file through
+ # Volume.open()
+ raise ValueError("I/O operation on closed file")
+ return self
+
+ def __exit__(self, type, value, tb):
+ self.close()
+
+ def close(self):
+ ret = api.glfs_close(self.fd)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def discard(self, offset, len):
+ ret = api.client.glfs_discard(self.fd, offset, len)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def dup(self):
+ dupfd = api.glfs_dup(self.fd)
+ if not dupfd:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return File(dupfd, self.originalpath)
+
+ def fallocate(self, mode, offset, len):
+ ret = api.client.glfs_fallocate(self.fd, mode, offset, len)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def fchmod(self, mode):
+ """
+ Change this file's mode
+
+ :param mode: new mode
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_fchmod(self.fd, mode)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def fchown(self, uid, gid):
+ """
+ Change this file's owner and group id
+
+ :param uid: new user id for file
+ :param gid: new group id for file
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_fchown(self.fd, uid, gid)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def fdatasync(self):
+ """
+ Force write of file
+
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_fdatasync(self.fd)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def fgetsize(self):
+ """
+ Return the size of a file, reported by fstat()
+ """
+ return self.fstat().st_size
+
+ def fstat(self):
+ """
+ Returns Stat object for this file.
+ """
+ s = api.Stat()
+ rc = api.glfs_fstat(self.fd, ctypes.byref(s))
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return s
+
+ def fsync(self):
+ ret = api.glfs_fsync(self.fd)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def lseek(self, pos, how):
+ """
+ Set the read/write offset position of this file.
+ The new position is defined by 'pos' relative to 'how'
+
+ :param pos: sets new offset position according to 'how'
+ :param how: SEEK_SET, sets offset position 'pos' bytes relative to
+ beginning of file, SEEK_CUR, the position is set relative
+ to the current position and SEEK_END sets the position
+ relative to the end of the file.
+ :returns: the new offset position
+
+ """
+ return api.glfs_lseek(self.fd, pos, how)
+
+ def read(self, buflen=-1):
+ """
+ read file
+
+ :param buflen: length of read buffer. If less than 0, then whole
+ file is read. Default is -1.
+ :returns: buffer of size buflen
+ """
+ if buflen < 0:
+ buflen = self.fgetsize()
+ rbuf = ctypes.create_string_buffer(buflen)
+ ret = api.glfs_read(self.fd, rbuf, buflen, 0)
+ if ret > 0:
+ return rbuf
+ elif ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ else:
+ return ret
+
+ def write(self, data, flags=0):
+ # creating a ctypes.c_ubyte buffer to handle converting bytearray
+ # to the required C data type
+
+ if type(data) is bytearray:
+ buf = (ctypes.c_ubyte * len(data)).from_buffer(data)
+ else:
+ buf = data
+ ret = api.glfs_write(self.fd, buf, len(buf), flags)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+
+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 = ctypes.POINTER(api.Dirent)()
+
+ def __del__(self):
+ self._api.glfs_closedir(self.fd)
+ self._api = None
+
+ def next(self):
+ entry = api.Dirent()
+ entry.d_reclen = 256
+ rc = api.glfs_readdir_r(self.fd, ctypes.byref(entry),
+ ctypes.byref(self.cursor))
+
+ if (rc < 0) or (not self.cursor) or (not self.cursor.contents):
+ return rc
+
+ return entry
+
+
+class Volume(object):
+
+ 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):
+ return api.glfs_init(self.fs)
+
+ def chmod(self, path, mode):
+ """
+ Change mode of path
+
+ :param path: the item to be modified
+ :mode: new mode
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_chmod(self.fs, path, mode)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def chown(self, path, uid, gid):
+ """
+ Change owner and group id of path
+
+ :param path: the item to be modified
+ :param uid: new user id for item
+ :param gid: new group id for item
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_chown(self.fs, path, uid, gid)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def exists(self, path):
+ """
+ Test whether a path exists.
+ Returns False for broken symbolic links.
+ """
+ try:
+ self.stat(path)
+ except OSError:
+ return False
+ return True
+
+ def getatime(self, path):
+ """
+ Returns the last access time as reported by stat
+ """
+ return self.stat(path).st_atime
+
+ def getctime(self, path):
+ """
+ Returns the time when changes were made to the path as reported by stat
+ This time is updated when changes are made to the file or dir's inode
+ or the contents of the file
+ """
+ return self.stat(path).st_ctime
+
+ def getmtime(self, path):
+ """
+ Returns the time when changes were made to the content of the path
+ as reported by stat
+ """
+ return self.stat(path).st_mtime
+
+ def getsize(self, filename):
+ """
+ Return the size of a file, reported by stat()
+ """
+ return self.stat(filename).st_size
+
+ def getxattr(self, path, key, maxlen):
+ buf = ctypes.create_string_buffer(maxlen)
+ rc = api.glfs_getxattr(self.fs, path, key, buf, maxlen)
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise IOError(err, os.strerror(err))
+ return buf.value[:rc]
+
+ def isdir(self, path):
+ """
+ Test whether a path is an existing directory
+ """
+ try:
+ s = self.stat(path)
+ except OSError:
+ return False
+ return stat.S_ISDIR(s.st_mode)
+
+ def isfile(self, path):
+ """
+ Test whether a path is a regular file
+ """
+ try:
+ s = self.stat(path)
+ except OSError:
+ return False
+ return stat.S_ISREG(s.st_mode)
+
+ def islink(self, path):
+ """
+ Test whether a path is a symbolic link
+ """
+ try:
+ s = self.lstat(path)
+ except OSError:
+ return False
+ return stat.S_ISLNK(s.st_mode)
+
+ def listdir(self, path):
+ """
+ Return list of entries in a given directory 'path'.
+ "." and ".." are not included, and the list is not sorted.
+ """
+ dir_list = []
+ d = self.opendir(path)
+ while True:
+ ent = d.next()
+ if not isinstance(ent, api.Dirent):
+ break
+ name = ent.d_name[:ent.d_reclen]
+ if not name in [".", ".."]:
+ dir_list.append(name)
+ return dir_list
+
+ def listxattr(self, path):
+ buf = ctypes.create_string_buffer(512)
+ rc = api.glfs_listxattr(self.fs, path, buf, 512)
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise IOError(err, os.strerror(err))
+ 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):
+ s = api.Stat()
+ rc = api.glfs_lstat(self.fs, path, ctypes.byref(s))
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return s
+
+ def makedirs(self, name, mode=0777):
+ """
+ Create directories defined in 'name' recursively.
+ """
+ head, tail = os.path.split(name)
+ if not tail:
+ head, tail = os.path.split(head)
+ if head and tail and not self.exists(head):
+ try:
+ self.makedirs(head, mode)
+ except OSError as err:
+ if err.errno != errno.EEXIST:
+ raise
+ if tail == os.curdir:
+ return
+ self.mkdir(name, mode)
+
+ def mkdir(self, path, mode=0777):
+ ret = api.glfs_mkdir(self.fs, path, mode)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def open(self, path, flags, mode=0777):
+ if (os.O_CREAT & flags) == os.O_CREAT:
+ #Without direct call to _api the functest fails on creat and open.
+
+ fd = api.client.glfs_creat(self.fs, path, flags, mode)
+ else:
+ fd = api.client.glfs_open(self.fs, path, flags)
+ if not fd:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+
+ return File(fd, path)
+
+ def opendir(self, path):
+ fd = api.glfs_opendir(self.fs, path)
+ if not fd:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return Dir(fd)
+
+ def removexattr(self, path, key):
+ ret = api.glfs_removexattr(self.fs, path, key)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise IOError(err, os.strerror(err))
+ return ret
+
+ def rename(self, opath, npath):
+ ret = api.glfs_rename(self.fs, opath, npath)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def rmdir(self, path):
+ ret = api.glfs_rmdir(self.fs, path)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def rmtree(self, path, ignore_errors=False, onerror=None):
+ """
+ Delete a whole directory tree structure. Raises OSError
+ if path is a symbolic link.
+
+ :param path: Directory tree to remove
+ :param ignore_errors: If True, errors are ignored
+ :param onerror: If set, it is called to handle the error with arguments
+ (func, path, exc) where func is the function that
+ raised the error, path is the argument that caused it
+ to fail; and exc is the exception that was raised.
+ If ignore_errors is False and onerror is None, an
+ exception is raised
+ """
+ if ignore_errors:
+ def onerror(*args):
+ pass
+ elif onerror is None:
+ def onerror(*args):
+ raise
+ if self.islink(path):
+ raise OSError("Cannot call rmtree on a symbolic link")
+ names = []
+ try:
+ names = self.listdir(path)
+ except OSError as e:
+ onerror(self.listdir, path, e)
+ for name in names:
+ fullname = os.path.join(path, name)
+ if self.isdir(fullname):
+ self.rmtree(fullname, ignore_errors, onerror)
+ else:
+ try:
+ self.unlink(fullname)
+ except OSError as e:
+ onerror(self.unlink, fullname, e)
+ try:
+ self.rmdir(path)
+ except OSError as e:
+ onerror(self.rmdir, path, e)
+
+ def setfsuid(self, uid):
+ ret = api.glfs_setfsuid(uid)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def setfsgid(self, gid):
+ ret = api.glfs_setfsgid(gid)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def setxattr(self, path, key, value, vlen):
+ ret = api.glfs_setxattr(self.fs, path, key, value, vlen, 0)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise IOError(err, os.strerror(err))
+ return ret
+
+ def stat(self, path):
+ s = api.Stat()
+ rc = api.glfs_stat(self.fs, path, ctypes.byref(s))
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return s
+
+ def statvfs(self, path):
+ """
+ To get status information about the file system that contains the file
+ named by the path argument.
+ """
+ s = api.Statvfs()
+ rc = api.glfs_statvfs(self.fs, path, ctypes.byref(s))
+ if rc < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return s
+
+ def symlink(self, source, link_name):
+ """
+ Create a symbolic link 'link_name' which points to 'source'
+ """
+ ret = api.glfs_symlink(self.fs, source, link_name)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def unlink(self, path):
+ """
+ Delete the file 'path'
+
+ :param path: file to be deleted
+ :returns: 0 if success, raises OSError if it fails
+ """
+ ret = api.glfs_unlink(self.fs, path)
+ if ret < 0:
+ err = ctypes.get_errno()
+ raise OSError(err, os.strerror(err))
+ return ret
+
+ def walk(self, top, topdown=True, onerror=None, followlinks=False):
+ """
+ Directory tree generator. Yields a 3-tuple dirpath, dirnames, filenames
+
+ dirpath is the path to the directory, dirnames is a list of the names
+ of the subdirectories in dirpath. filenames is a list of the names of
+ the non-directiry files in dirpath
+ """
+ try:
+ names = self.listdir(top)
+ except OSError as err:
+ if onerror is not None:
+ onerror(err)
+ return
+
+ dirs, nondirs = [], []
+ for name in names:
+ if self.isdir(os.path.join(top, name)):
+ dirs.append(name)
+ else:
+ nondirs.append(name)
+
+ if topdown:
+ yield top, dirs, nondirs
+ for name in dirs:
+ new_path = os.path.join(top, name)
+ if followlinks or not self.islink(new_path):
+ for x in self.walk(new_path, topdown, onerror, followlinks):
+ yield x
+ if not topdown:
+ yield top, dirs, nondirs