diff options
author | Thiago da Silva <thiago@redhat.com> | 2014-03-26 16:31:28 -0400 |
---|---|---|
committer | Thiago da Silva <thiago@redhat.com> | 2014-03-27 15:21:50 -0400 |
commit | bd683d93cb8d967f761d53f7a6b4243eb8eaedea (patch) | |
tree | 5f73d7390690a9de7e3bf2371fb6590d5ecce95b | |
parent | f50cec44586e851c3d9e29dd59574696ae6f0c3a (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.py | 48 | ||||
-rw-r--r-- | test/functional/libgfapi-python-tests.py | 24 | ||||
-rw-r--r-- | test/unit/gluster/test_gfapi.py | 20 |
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" |