]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind/cephfs: add snapdiff api binding
authorIgor Fedotov <igor.fedotov@croit.io>
Sun, 11 Sep 2022 22:04:17 +0000 (01:04 +0300)
committerIgor Fedotov <igor.fedotov@croit.io>
Wed, 30 Aug 2023 16:26:19 +0000 (19:26 +0300)
Signed-off-by: Igor Fedotov <ifedotov@croit.io>
(cherry picked from commit a67a41c68ab68494a6bab8b084bfc4c5f1a02a0d)

 Conflicts:
src/test/pybind/test_cephfs.py

src/pybind/cephfs/c_cephfs.pxd
src/pybind/cephfs/cephfs.pyx
src/pybind/cephfs/mock_cephfs.pxi
src/test/pybind/test_cephfs.py

index 4636b4bf45d26aefcd493316eb89f1bfab790f0c..69d24912b4c492a0448b3e8f6a4fcebd2c664769 100644 (file)
@@ -36,6 +36,13 @@ cdef extern from "cephfs/libcephfs.h" nogil:
         size_t nr_snap_metadata
         snap_metadata *snap_metadata
 
+    cdef struct ceph_snapdiff_info:
+        pass
+
+    cdef struct ceph_snapdiff_entry_t:
+        dirent dir_entry
+        uint64_t snapid
+
     ctypedef void* rados_t
 
     const char *ceph_version(int *major, int *minor, int *patch)
@@ -111,6 +118,14 @@ cdef extern from "cephfs/libcephfs.h" nogil:
     void ceph_seekdir(ceph_mount_info *cmount, ceph_dir_result *dirp, int64_t offset)
     int ceph_chdir(ceph_mount_info *cmount, const char *path)
     dirent * ceph_readdir(ceph_mount_info *cmount, ceph_dir_result *dirp)
+    int ceph_open_snapdiff(ceph_mount_info *cmount,
+                           const char *root_path,
+                           const char *rel_path,
+                           const char *snap1,
+                           const char *snap2,
+                           ceph_snapdiff_info *out)
+    int ceph_readdir_snapdiff(ceph_snapdiff_info *snapdiff, ceph_snapdiff_entry_t *out);
+    int ceph_close_snapdiff(ceph_snapdiff_info *snapdiff)
     int ceph_rmdir(ceph_mount_info *cmount, const char *path)
     const char* ceph_getcwd(ceph_mount_info *cmount)
     int ceph_sync_fs(ceph_mount_info *cmount)
index fca3846de64a6a79fec4e1c62965f9cc24c08b84..793d88b985013770442013ea91ee0fc576f356a9 100644 (file)
@@ -57,6 +57,8 @@ CEPH_SETATTR_SIZE  = 0x20
 CEPH_SETATTR_CTIME = 0x40
 CEPH_SETATTR_BTIME = 0x200
 
+CEPH_NOSNAP = -2
+
 # errno definitions
 cdef enum:
     CEPHFS_EBLOCKLISTED = 108
@@ -219,7 +221,7 @@ cdef make_ex(ret, msg):
 
 
 class DirEntry(namedtuple('DirEntry',
-               ['d_ino', 'd_off', 'd_reclen', 'd_type', 'd_name'])):
+               ['d_ino', 'd_off', 'd_reclen', 'd_type', 'd_name', 'd_snapid'])):
     DT_DIR = 0x4
     DT_REG = 0x8
     DT_LNK = 0xA
@@ -277,13 +279,15 @@ cdef class DirResult(object):
                             d_off=0,
                             d_reclen=dirent.d_reclen,
                             d_type=dirent.d_type,
-                            d_name=dirent.d_name)
+                            d_name=dirent.d_name,
+                            d_snapid=CEPH_NOSNAP)
         ELSE:
             return DirEntry(d_ino=dirent.d_ino,
                             d_off=dirent.d_off,
                             d_reclen=dirent.d_reclen,
                             d_type=dirent.d_type,
-                            d_name=dirent.d_name)
+                            d_name=dirent.d_name,
+                            d_snapid=CEPH_NOSNAP)
 
     def close(self):
         if self.handle:
@@ -321,6 +325,56 @@ cdef class DirResult(object):
         with nogil:
             ceph_seekdir(self.lib.cluster, self.handle, _offset)
 
+cdef class SnapDiffHandle(object):
+    cdef LibCephFS lib
+    cdef ceph_snapdiff_info handle
+    cdef int opened
+
+    def __cinit__(self, _lib):
+        self.opened = 0
+        self.lib = _lib
+
+    def __dealloc__(self):
+        self.close()
+
+    def readdir(self):
+        self.lib.require_state("mounted")
+
+        cdef:
+            ceph_snapdiff_entry_t difent
+        with nogil:
+            ret = ceph_readdir_snapdiff(&self.handle, &difent)
+        if ret < 0:
+            raise make_ex(ret, "ceph_readdir_snapdiff failed, ret {}"
+                .format(ret))
+        if ret == 0:
+            return None
+
+        IF UNAME_SYSNAME == "FreeBSD" or UNAME_SYSNAME == "Darwin":
+            return DirEntry(d_ino=difent.dir_entry.d_ino,
+                            d_off=0,
+                            d_reclen=difent.dir_entry.d_reclen,
+                            d_type=difent.dir_entry.d_type,
+                            d_name=difent.dir_entry.d_name,
+                            d_snapid=difent.snapid)
+        ELSE:
+            return DirEntry(d_ino=difent.dir_entry.d_ino,
+                            d_off=difent.dir_entry.d_off,
+                            d_reclen=difent.dir_entry.d_reclen,
+                            d_type=difent.dir_entry.d_type,
+                            d_name=difent.dir_entry.d_name,
+                            d_snapid=difent.snapid)
+
+    def close(self):
+        if (not self.opened):
+            return
+        self.lib.require_state("mounted")
+        with nogil:
+            ret = ceph_close_snapdiff(&self.handle)
+        if ret < 0:
+            raise make_ex(ret, "closesnapdiff failed")
+        self.opened = 0
+
 
 def cstr(val, name, encoding="utf-8", opt=False) -> bytes:
     """
@@ -974,6 +1028,34 @@ cdef class LibCephFS(object):
 
         return handle.close()
 
+    def opensnapdiff(self, root_path, rel_path, snap1name, snap2name) -> SnapDiffHandle:
+        """
+        Open the given directory.
+
+        :param path: the path name of the directory to open.  Must be either an absolute path
+                     or a path relative to the current working directory.
+        :returns: the open directory stream handle
+        """
+        self.require_state("mounted")
+
+        h = SnapDiffHandle(self)
+        root = cstr(root_path, 'root')
+        relp = cstr(rel_path, 'relp')
+        snap1 = cstr(snap1name, 'snap1')
+        snap2 = cstr(snap2name, 'snap2')
+        cdef:
+            char* _root = root
+            char* _relp = relp
+            char* _snap1 = snap1
+            char* _snap2 = snap2
+        with nogil:
+            ret = ceph_open_snapdiff(self.cluster, _root, _relp, _snap1, _snap2, &h.handle);
+        if ret < 0:
+            raise make_ex(ret, "open_snapdiff failed for {} vs. {}"
+                .format(snap1.decode('utf-8'), snap2.decode('utf-8')))
+        h.opened = 1
+        return h
+
     def rewinddir(self, DirResult handle):
         """
         Rewind the directory stream to the beginning of the directory.
index 1dec0d50d5476584c5f8de812b56628ce33fa019..54b27d04c6742b7dc57a0f34babd33e54f1f05ef 100644 (file)
@@ -39,6 +39,12 @@ cdef nogil:
         size_t nr_snap_metadata
         snap_metadata *snap_metadata
 
+    cdef struct ceph_snapdiff_info:
+        int dummy
+
+    cdef struct ceph_snapdiff_entry_t:
+        int dummy
+
     ctypedef void* rados_t
 
     const char *ceph_version(int *major, int *minor, int *patch):
@@ -175,6 +181,12 @@ cdef nogil:
         pass
     dirent * ceph_readdir(ceph_mount_info *cmount, ceph_dir_result *dirp):
         pass
+    int ceph_open_snapdiff(ceph_mount_info *cmount, const char *root_path, const char *rel_path, const char *snap1path, const char *snap2root, ceph_snapdiff_info *out):
+        pass
+    int ceph_readdir_snapdiff(ceph_snapdiff_info *snapdiff, ceph_snapdiff_entry_t *out):
+        pass
+    int ceph_close_snapdiff(ceph_snapdiff_info *snapdiff):
+        pass
     int ceph_rmdir(ceph_mount_info *cmount, const char *path):
         pass
     const char* ceph_getcwd(ceph_mount_info *cmount):
index 0991730706d63846b5c52e761c64b5362567ce70..d16807de97d8e4bbd9d9b6fed1fffdf31907c029 100644 (file)
@@ -1,5 +1,7 @@
 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
 from assertions import assert_raises, assert_equal, assert_not_equal, assert_greater
+import collections
+collections.Callable = collections.abc.Callable
 import cephfs as libcephfs
 import fcntl
 import os
@@ -21,21 +23,37 @@ def teardown_module():
     global cephfs
     cephfs.shutdown()
 
-@pytest.fixture
-def testdir():
-    d = cephfs.opendir(b"/")
+def purge_dir(path, is_snap = False):
+    print(b"Purge " + path)
+    d = cephfs.opendir(path)
+    if (not path.endswith(b"/")):
+        path = path + b"/"
     dent = cephfs.readdir(d)
     while dent:
         if (dent.d_name not in [b".", b".."]):
+            print(path + dent.d_name)
             if dent.is_dir():
-                cephfs.rmdir(b"/" + dent.d_name)
+                if (not is_snap):
+                    try:
+                        snappath = path + dent.d_name + b"/.snap"
+                        cephfs.stat(snappath)
+                        purge_dir(snappath, True)
+                    except:
+                        pass
+                    purge_dir(path + dent.d_name, False)
+                    cephfs.rmdir(path + dent.d_name)
+                else:
+                    print("rmsnap on {} snap {}".format(path, dent.d_name))
+                    cephfs.rmsnap(path, dent.d_name);
             else:
-                cephfs.unlink(b"/" + dent.d_name)
-
+                cephfs.unlink(path + dent.d_name)
         dent = cephfs.readdir(d)
-
     cephfs.closedir(d)
 
+@pytest.fixture
+def testdir():
+    purge_dir(b"/")
+
     cephfs.chdir(b"/")
     _, ret_buf = cephfs.listxattr("/")
     print(f'ret_buf={ret_buf}')
@@ -854,3 +872,38 @@ def test_set_mount_timeout_lt0(testdir):
     cephfs.unmount()
     assert_raises(libcephfs.InvalidValue, cephfs.set_mount_timeout, -5)
     cephfs.mount()
+
+def test_snapdiff(testdir):
+    cephfs.mkdir("/snapdiff_test", 0o755)
+    fd = cephfs.open('/snapdiff_test/file-1', 'w', 0o755)
+    cephfs.write(fd, b"1111", 0)
+    cephfs.close(fd)
+    fd = cephfs.open('/snapdiff_test/file-2', 'w', 0o755)
+    cephfs.write(fd, b"2222", 0)
+    cephfs.close(fd)
+    cephfs.mksnap("/snapdiff_test", "snap1", 0o755)
+    fd = cephfs.open('/snapdiff_test/file-1', 'w', 0o755)
+    cephfs.write(fd, b"1222", 0)
+    cephfs.close(fd)
+    cephfs.unlink('/snapdiff_test/file-2')
+    cephfs.mksnap("/snapdiff_test", "snap2", 0o755)
+    snap1id = cephfs.snap_info(b"/snapdiff_test/.snap/snap1")['id']
+    snap2id = cephfs.snap_info(b"/snapdiff_test/.snap/snap2")['id']
+    diff = cephfs.opensnapdiff(b"/snapdiff_test", b"/", b"snap2", b"snap1")
+    cnt = 0
+    e = diff.readdir()
+    while e is not None:
+        if (e.d_name == b"file-1"):
+            cnt = cnt + 1
+            assert_equal(snap2id, e.d_snapid)
+        elif (e.d_name == b"file-2"):
+            cnt = cnt + 1
+            assert_equal(snap1id, e.d_snapid)
+        elif (e.d_name != b"." and e.d_name != b".."):
+            cnt = cnt + 1
+        e = diff.readdir()
+    assert_equal(cnt, 2)
+    diff.close()
+
+    # remove directory
+    purge_dir(b"/snapdiff_test");