diff options
-rwxr-xr-x | glusterfs/api.py | 244 | ||||
-rwxr-xr-x | glusterfs/gfapi.py | 116 | ||||
-rw-r--r-- | test/unit/gluster/test_gfapi.py | 37 |
3 files changed, 280 insertions, 117 deletions
diff --git a/glusterfs/api.py b/glusterfs/api.py new file mode 100755 index 0000000..fa3c9f7 --- /dev/null +++ b/glusterfs/api.py @@ -0,0 +1,244 @@ + +# 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), + ] + +# 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_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_ulong)(('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)) + + +# 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/glusterfs/gfapi.py b/glusterfs/gfapi.py index 9217f18..d65099f 100755 --- a/glusterfs/gfapi.py +++ b/glusterfs/gfapi.py @@ -14,95 +14,10 @@ # limitations under the License. import ctypes -from ctypes.util import find_library import os import stat import errno - -# Disclaimer: many of the helper functions (e.g., exists, isdir) where copied -# from the python source code - -# Looks like ctypes is having trouble with dependencies, so just force them to -# load with RTLD_GLOBAL until I figure that out. -api = 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), - ] - -api.glfs_creat.restype = ctypes.c_void_p -api.glfs_open.restype = ctypes.c_void_p -api.glfs_lstat.restype = ctypes.c_int -api.glfs_lstat.argtypes = [ctypes.c_void_p, ctypes.c_char_p, - ctypes.POINTER(Stat)] -api.glfs_opendir.restype = ctypes.c_void_p -api.glfs_readdir_r.restype = ctypes.c_int -api.glfs_readdir_r.argtypes = [ctypes.c_void_p, ctypes.POINTER(Dirent), - ctypes.POINTER(ctypes.POINTER(Dirent))] -api.glfs_stat.restype = ctypes.c_int -api.glfs_stat.argtypes = [ctypes.c_void_p, ctypes.c_char_p, - ctypes.POINTER(Stat)] -api.glfs_fstat.restype = ctypes.c_int -api.glfs_fstat.argtypes = [ctypes.c_void_p, ctypes.POINTER(Stat)] +from glusterfs import api class File(object): @@ -130,7 +45,7 @@ class File(object): return ret def discard(self, offset, len): - ret = api.glfs_discard(self.fd, offset, len) + ret = api.client.glfs_discard(self.fd, offset, len) if ret < 0: err = ctypes.get_errno() raise OSError(err, os.strerror(err)) @@ -144,7 +59,7 @@ class File(object): return File(dupfd, self.originalpath) def fallocate(self, mode, offset, len): - ret = api.glfs_fallocate(self.fd, 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)) @@ -180,7 +95,7 @@ class File(object): """ Returns Stat object for this file. """ - s = Stat() + s = api.Stat() rc = api.glfs_fstat(self.fd, ctypes.byref(s)) if rc < 0: err = ctypes.get_errno() @@ -220,14 +135,15 @@ class File(object): else: return ret - def write(self, data): + 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)) + ret = api.glfs_write(self.fd, buf, len(buf), flags) if ret < 0: err = ctypes.get_errno() raise OSError(err, os.strerror(err)) @@ -241,14 +157,14 @@ class Dir(object): # get yanked out from under us (see comment above File def'n). self._api = api self.fd = fd - self.cursor = ctypes.POINTER(Dirent)() + self.cursor = ctypes.POINTER(api.Dirent)() def __del__(self): self._api.glfs_closedir(self.fd) self._api = None def next(self): - entry = Dirent() + entry = api.Dirent() entry.d_reclen = 256 rc = api.glfs_readdir_r(self.fd, ctypes.byref(entry), ctypes.byref(self.cursor)) @@ -380,7 +296,7 @@ class Volume(object): dir_list = [] while True: ent = d.next() - if not isinstance(ent, Dirent): + if not isinstance(ent, api.Dirent): break name = ent.d_name[:ent.d_reclen] if not name in [".", ".."]: @@ -412,7 +328,7 @@ class Volume(object): return xattrs def lstat(self, path): - s = Stat() + s = api.Stat() rc = api.glfs_lstat(self.fs, path, ctypes.byref(s)) if rc < 0: err = ctypes.get_errno() @@ -445,9 +361,11 @@ class Volume(object): def open(self, path, flags, mode=0777): if (os.O_CREAT & flags) == os.O_CREAT: - fd = api.glfs_creat(self.fs, path, flags, mode) + #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.glfs_open(self.fs, path, flags) + fd = api.client.glfs_open(self.fs, path, flags) if not fd: err = ctypes.get_errno() raise OSError(err, os.strerror(err)) @@ -531,7 +449,7 @@ class Volume(object): return ret def stat(self, path): - s = Stat() + s = api.Stat() rc = api.glfs_stat(self.fs, path, ctypes.byref(s)) if rc < 0: err = ctypes.get_errno() @@ -543,7 +461,7 @@ class Volume(object): To get status information about the file system that contains the file named by the path argument. """ - s = Statvfs() + s = api.Statvfs() rc = api.glfs_statvfs(self.fs, path, ctypes.byref(s)) if rc < 0: err = ctypes.get_errno() diff --git a/test/unit/gluster/test_gfapi.py b/test/unit/gluster/test_gfapi.py index c7627b1..550ba79 100644 --- a/test/unit/gluster/test_gfapi.py +++ b/test/unit/gluster/test_gfapi.py @@ -20,6 +20,7 @@ import stat import errno from glusterfs import gfapi +from glusterfs import api from nose import SkipTest from mock import Mock, patch from contextlib import nested @@ -108,7 +109,7 @@ class TestFile(unittest.TestCase): with patch("glusterfs.gfapi.api.glfs_fstat", mock_glfs_fstat): s = self.fd.fstat() - self.assertTrue(isinstance(s, gfapi.Stat)) + self.assertTrue(isinstance(s, api.Stat)) def test_fstat_fail_exception(self): mock_glfs_fstat = Mock() @@ -296,7 +297,7 @@ class TestVolume(unittest.TestCase): mock_glfs_creat = Mock() mock_glfs_creat.return_value = 2 - with patch("glusterfs.gfapi.api.glfs_creat", mock_glfs_creat): + with patch("glusterfs.api.client.glfs_creat", mock_glfs_creat): with self.vol.open("file.txt", os.O_CREAT, 0644) as fd: self.assertTrue(isinstance(fd, gfapi.File)) self.assertEqual(mock_glfs_creat.call_count, 1) @@ -322,7 +323,7 @@ class TestVolume(unittest.TestCase): def test_isdir_true(self): mock_glfs_stat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFDIR mock_glfs_stat.return_value = s @@ -332,7 +333,7 @@ class TestVolume(unittest.TestCase): def test_isdir_false(self): mock_glfs_stat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFREG mock_glfs_stat.return_value = s @@ -350,7 +351,7 @@ class TestVolume(unittest.TestCase): def test_isfile_true(self): mock_glfs_stat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFREG mock_glfs_stat.return_value = s @@ -360,7 +361,7 @@ class TestVolume(unittest.TestCase): def test_isfile_false(self): mock_glfs_stat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFDIR mock_glfs_stat.return_value = s @@ -378,7 +379,7 @@ class TestVolume(unittest.TestCase): def test_islink_true(self): mock_glfs_lstat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFLNK mock_glfs_lstat.return_value = s @@ -388,7 +389,7 @@ class TestVolume(unittest.TestCase): def test_islink_false(self): mock_glfs_lstat = Mock() - s = gfapi.Stat() + s = api.Stat() s.st_mode = stat.S_IFREG mock_glfs_lstat.return_value = s @@ -425,13 +426,13 @@ class TestVolume(unittest.TestCase): mock_glfs_opendir = Mock() mock_glfs_opendir.return_value = 2 - dirent1 = gfapi.Dirent() + dirent1 = api.Dirent() dirent1.d_name = "mockfile" dirent1.d_reclen = 8 - dirent2 = gfapi.Dirent() + dirent2 = api.Dirent() dirent2.d_name = "mockdir" dirent2.d_reclen = 7 - dirent3 = gfapi.Dirent() + dirent3 = api.Dirent() dirent3.d_name = "." dirent3.d_reclen = 1 mock_Dir_next = Mock() @@ -474,7 +475,7 @@ class TestVolume(unittest.TestCase): with patch("glusterfs.gfapi.api.glfs_lstat", mock_glfs_lstat): s = self.vol.lstat("file.txt") - self.assertTrue(isinstance(s, gfapi.Stat)) + self.assertTrue(isinstance(s, api.Stat)) def test_lstat_fail_exception(self): mock_glfs_lstat = Mock() @@ -489,7 +490,7 @@ class TestVolume(unittest.TestCase): with patch("glusterfs.gfapi.api.glfs_stat", mock_glfs_stat): s = self.vol.stat("file.txt") - self.assertTrue(isinstance(s, gfapi.Stat)) + self.assertTrue(isinstance(s, api.Stat)) def test_stat_fail_exception(self): mock_glfs_stat = Mock() @@ -504,7 +505,7 @@ class TestVolume(unittest.TestCase): with patch("glusterfs.gfapi.api.glfs_statvfs", mock_glfs_statvfs): s = self.vol.statvfs("/") - self.assertTrue(isinstance(s, gfapi.Statvfs)) + self.assertTrue(isinstance(s, api.Statvfs)) def test_statvfs_fail_exception(self): mock_glfs_statvfs = Mock() @@ -572,7 +573,7 @@ class TestVolume(unittest.TestCase): mock_glfs_open = Mock() mock_glfs_open.return_value = 2 - with patch("glusterfs.gfapi.api.glfs_open", mock_glfs_open): + with patch("glusterfs.api.client.glfs_open", mock_glfs_open): with self.vol.open("file.txt", os.O_WRONLY) as fd: self.assertTrue(isinstance(fd, gfapi.File)) self.assertEqual(mock_glfs_open.call_count, 1) @@ -587,14 +588,14 @@ class TestVolume(unittest.TestCase): with self.vol.open("file.txt", os.O_WRONLY) as fd: self.assertEqual(fd, None) - with patch("glusterfs.gfapi.api.glfs_open", mock_glfs_open): + with patch("glusterfs.api.client.glfs_open", mock_glfs_open): self.assertRaises(OSError, assert_open) def test_open_direct_success(self): mock_glfs_open = Mock() mock_glfs_open.return_value = 2 - with patch("glusterfs.gfapi.api.glfs_open", mock_glfs_open): + with patch("glusterfs.api.client.glfs_open", mock_glfs_open): fd = self.vol.open("file.txt", os.O_WRONLY) self.assertTrue(isinstance(fd, gfapi.File)) self.assertEqual(mock_glfs_open.call_count, 1) @@ -604,7 +605,7 @@ class TestVolume(unittest.TestCase): mock_glfs_open = Mock() mock_glfs_open.return_value = None - with patch("glusterfs.gfapi.api.glfs_open", mock_glfs_open): + with patch("glusterfs.api.client.glfs_open", mock_glfs_open): self.assertRaises(OSError, self.vol.open, "file.txt", os.O_RDONLY) def test_opendir_success(self): |