diff options
author | Humble Chirammal <hchiramm@redhat.com> | 2015-02-17 17:26:43 +0530 |
---|---|---|
committer | hchiramm <hchiramm@redhat.com> | 2015-04-25 13:24:33 +0530 |
commit | 75bb7da7094e0344fc7be93408ff6d6ead855253 (patch) | |
tree | 7fe491ac83a1cb94dc992d4eea344c7edc5137a3 /gluster | |
parent | 436a05174b96a90216925424bbb8bb1bcbb1eb2e (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__.py | 49 | ||||
-rwxr-xr-x | gluster/api.py | 402 | ||||
-rwxr-xr-x | gluster/gfapi.py | 578 |
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 |