summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago da Silva <thiago@redhat.com>2014-03-26 16:31:28 -0400
committerThiago da Silva <thiago@redhat.com>2014-03-27 15:21:50 -0400
commitbd683d93cb8d967f761d53f7a6b4243eb8eaedea (patch)
tree5f73d7390690a9de7e3bf2371fb6590d5ecce95b
parentf50cec44586e851c3d9e29dd59574696ae6f0c3a (diff)
adding new apis requested from gluster-swift proj
This patch adds new functions that are used in gluster-swift. These functions are: dup, lseek, getatime, getctime, getmtime Change-Id: I09036f7e393a89e09913f7f6de173e1eca8dca95 Signed-off-by: Thiago da Silva <thiago@redhat.com>
-rw-r--r--glusterfs/gfapi.py48
-rw-r--r--test/functional/libgfapi-python-tests.py24
-rw-r--r--test/unit/gluster/test_gfapi.py20
3 files changed, 89 insertions, 3 deletions
diff --git a/glusterfs/gfapi.py b/glusterfs/gfapi.py
index 6ca2ad7..80418a1 100644
--- a/glusterfs/gfapi.py
+++ b/glusterfs/gfapi.py
@@ -87,8 +87,9 @@ api.glfs_fstat.argtypes = [ctypes.c_void_p, ctypes.POINTER(Stat)]
class File(object):
- def __init__(self, fd):
+ def __init__(self, fd, path=None):
self.fd = fd
+ self.originalpath = path
def __enter__(self):
if self.fd is None:
@@ -115,6 +116,13 @@ class File(object):
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.glfs_fallocate(self.fd, mode, offset, len)
if ret < 0:
@@ -166,6 +174,21 @@ class File(object):
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, flags=0):
rbuf = ctypes.create_string_buffer(buflen)
ret = api.glfs_read(self.fd, rbuf, buflen, flags)
@@ -263,6 +286,27 @@ class Volume(object):
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()
@@ -388,7 +432,7 @@ class Volume(object):
err = ctypes.get_errno()
raise OSError(err, os.strerror(err))
- return File(fd)
+ return File(fd, path)
def opendir(self, path):
fd = api.glfs_opendir(self.fs, path)
diff --git a/test/functional/libgfapi-python-tests.py b/test/functional/libgfapi-python-tests.py
index ac5c38f..baad035 100644
--- a/test/functional/libgfapi-python-tests.py
+++ b/test/functional/libgfapi-python-tests.py
@@ -78,6 +78,7 @@ class FileOpsTest(unittest.TestCase):
self.assertEqual(rc, len(self.data))
ret = fd.fsync()
self.assertEqual(ret, 0)
+ self.assertEqual(fd.originalpath, self.path)
def tearDown(self):
self.path = None
@@ -110,6 +111,29 @@ class FileOpsTest(unittest.TestCase):
g.close()
self.fail("Expected a OSError with errno.EEXIST")
+ def test_write_file_dup_lseek_read(self):
+ try:
+ f = self.vol.open("dune", os.O_CREAT | os.O_EXCL | os.O_RDWR)
+ f.write("I must not fear. Fear is the mind-killer.")
+ fdup = f.dup()
+ self.assertTrue(isinstance(fdup, gfapi.File))
+ f.close()
+ ret = fdup.lseek(0, os.SEEK_SET)
+ self.assertEqual(ret, 0)
+
+ buf = fdup.read(15)
+ self.assertEqual(buf.value, "I must not fear")
+
+ ret = fdup.lseek(29, os.SEEK_SET)
+ self.assertEqual(ret, 29)
+
+ buf = fdup.read(11)
+ self.assertEqual(buf.value, "mind-killer")
+
+ fdup.close()
+ except OSError as e:
+ self.fail(e.message)
+
def test_exists(self):
e = self.vol.exists(self.path)
self.assertTrue(e)
diff --git a/test/unit/gluster/test_gfapi.py b/test/unit/gluster/test_gfapi.py
index ed7ed45..e0308f7 100644
--- a/test/unit/gluster/test_gfapi.py
+++ b/test/unit/gluster/test_gfapi.py
@@ -49,7 +49,7 @@ class TestFile(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.fd = gfapi.File(2)
+ cls.fd = gfapi.File(2, 'fakefile')
@classmethod
def tearDownClass(cls):
@@ -77,6 +77,16 @@ class TestFile(unittest.TestCase):
with patch("glusterfs.gfapi.api.glfs_fchown", mock_glfs_fchown):
self.assertRaises(OSError, self.fd.fchown, 9, 11)
+ def test_dup(self):
+ mock_glfs_dup = Mock()
+ mock_glfs_dup.return_value = 2
+
+ with patch("glusterfs.gfapi.api.glfs_dup", mock_glfs_dup):
+ f = self.fd.dup()
+ self.assertTrue(isinstance(f, gfapi.File))
+ self.assertEqual(f.originalpath, "fakefile")
+ self.assertEqual(f.fd, 2)
+
def test_fdatasync_success(self):
mock_glfs_fdatasync = Mock()
mock_glfs_fdatasync.return_value = 4
@@ -122,6 +132,14 @@ class TestFile(unittest.TestCase):
with patch("glusterfs.gfapi.api.glfs_fsync", mock_glfs_fsync):
self.assertRaises(OSError, self.fd.fsync)
+ def test_lseek_success(self):
+ mock_glfs_lseek = Mock()
+ mock_glfs_lseek.return_value = 20
+
+ with patch("glusterfs.gfapi.api.glfs_lseek", mock_glfs_lseek):
+ o = self.fd.lseek(20, os.SEEK_SET)
+ self.assertEqual(o, 20)
+
def test_read_success(self):
def _mock_glfs_read(fd, rbuf, buflen, flags):
rbuf.value = "hello"