From: Venky Shankar Date: Tue, 20 Oct 2020 06:04:36 +0000 (-0400) Subject: pybind/cephfs: python bindings for new snapshot APIs X-Git-Tag: v16.1.0~184^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=5d72894c449d8505d0ea764d7342f9e76c7ae1fe;p=ceph.git pybind/cephfs: python bindings for new snapshot APIs Signed-off-by: Venky Shankar --- diff --git a/src/pybind/cephfs/c_cephfs.pxd b/src/pybind/cephfs/c_cephfs.pxd index 4dfcb49d589..b4b2c625144 100644 --- a/src/pybind/cephfs/c_cephfs.pxd +++ b/src/pybind/cephfs/c_cephfs.pxd @@ -27,6 +27,15 @@ cdef extern from "cephfs/libcephfs.h" nogil: cdef struct ceph_dir_result: pass + cdef struct snap_metadata: + const char *key + const char *value + + cdef struct snap_info: + uint64_t id + size_t nr_snap_metadata + snap_metadata *snap_metadata + ctypedef void* rados_t const char *ceph_version(int *major, int *minor, int *patch) @@ -88,6 +97,10 @@ cdef extern from "cephfs/libcephfs.h" nogil: int ceph_close(ceph_mount_info *cmount, int fd) int ceph_open(ceph_mount_info *cmount, const char *path, int flags, mode_t mode) int ceph_mkdir(ceph_mount_info *cmount, const char *path, mode_t mode) + int ceph_mksnap(ceph_mount_info *cmount, const char *path, const char *name, mode_t mode, snap_metadata *snap_metadata, size_t nr_snap_metadata) + int ceph_rmsnap(ceph_mount_info *cmount, const char *path, const char *name) + int ceph_get_snap_info(ceph_mount_info *cmount, const char *path, snap_info *snap_info) + void ceph_free_snap_info_buffer(snap_info *snap_info) int ceph_mkdirs(ceph_mount_info *cmount, const char *path, mode_t mode) int ceph_closedir(ceph_mount_info *cmount, ceph_dir_result *dirp) int ceph_opendir(ceph_mount_info *cmount, const char *name, ceph_dir_result **dirpp) diff --git a/src/pybind/cephfs/cephfs.pyx b/src/pybind/cephfs/cephfs.pyx index c7342d70fef..2771f1dee14 100644 --- a/src/pybind/cephfs/cephfs.pyx +++ b/src/pybind/cephfs/cephfs.pyx @@ -969,6 +969,96 @@ cdef class LibCephFS(object): if ret < 0: raise make_ex(ret, "error in mkdir {}".format(path.decode('utf-8'))) + def mksnap(self, path, name, mode, metadata={}): + """ + Create a snapshot. + + :param path: path of the directory to snapshot. + :param name: snapshot name + :param mode: permission of the snapshot + :param metadata: metadata key/value to store with the snapshot + + :raises: :class: `TypeError` + :raises: :class: `Error` + :returns: int: 0 on success + """ + + self.require_state("mounted") + path = cstr(path, 'path') + name = cstr(name, 'name') + if not isinstance(mode, int): + raise TypeError('mode must be an int') + if not isinstance(metadata, dict): + raise TypeError('metadata must be an dictionary') + md = {} + for key, value in metadata.items(): + if not isinstance(key, str) or not isinstance(value, str): + raise TypeError('metadata key and values should be strings') + md[key.encode('utf-8')] = value.encode('utf-8') + cdef: + char* _path = path + char* _name = name + int _mode = mode + size_t nr = len(md) + snap_metadata *_snap_meta = malloc(nr * sizeof(snap_metadata)) + if nr and _snap_meta == NULL: + raise MemoryError("malloc failed") + i = 0 + for key, value in md.items(): + _snap_meta[i] = snap_metadata(key, value) + i += 1 + with nogil: + ret = ceph_mksnap(self.cluster, _path, _name, _mode, _snap_meta, nr) + free(_snap_meta) + if ret < 0: + raise make_ex(ret, "mksnap error") + return 0 + + def rmsnap(self, path, name): + """ + Remove a snapshot. + + :param path: path of the directory for removing snapshot + :param name: snapshot name + + :raises: :class: `Error` + :returns: int: 0 on success + """ + self.require_state("mounted") + path = cstr(path, 'path') + name = cstr(name, 'name') + cdef: + char* _path = path + char* _name = name + ret = ceph_rmsnap(self.cluster, _path, _name) + if ret < 0: + raise make_ex(ret, "rmsnap error") + return 0 + + def snap_info(self, path): + """ + Fetch sapshot info + + :param path: snapshot path + + :raises: :class: `Error` + :returns: dict: snapshot metadata + """ + self.require_state("mounted") + path = cstr(path, 'path') + cdef: + char* _path = path + snap_info info + ret = ceph_get_snap_info(self.cluster, _path, &info) + if ret < 0: + raise make_ex(ret, "snap_info error") + md = {} + if info.nr_snap_metadata: + md = {snap_meta.key.decode('utf-8'): snap_meta.value.decode('utf-8') for snap_meta in + info.snap_metadata[:info.nr_snap_metadata]} + ceph_free_snap_info_buffer(&info) + return {'id': info.id, 'metadata': md} + def chmod(self, path, mode) : """ Change directory mode. diff --git a/src/pybind/cephfs/mock_cephfs.pxi b/src/pybind/cephfs/mock_cephfs.pxi index c1c93ac100a..f4ff5b7360c 100644 --- a/src/pybind/cephfs/mock_cephfs.pxi +++ b/src/pybind/cephfs/mock_cephfs.pxi @@ -30,6 +30,15 @@ cdef nogil: cdef struct ceph_dir_result: int dummy + cdef struct snap_metadata: + const char *key + const char *value + + cdef struct snap_info: + uint64_t id + size_t nr_snap_metadata + snap_metadata *snap_metadata + ctypedef void* rados_t const char *ceph_version(int *major, int *minor, int *patch): @@ -138,6 +147,14 @@ cdef nogil: pass int ceph_mkdir(ceph_mount_info *cmount, const char *path, mode_t mode): pass + int ceph_mksnap(ceph_mount_info *cmount, const char *path, const char *name, mode_t mode, snap_metadata *snap_metadata, size_t nr_snap_metadata): + pass + int ceph_rmsnap(ceph_mount_info *cmount, const char *path, const char *name): + pass + int ceph_get_snap_info(ceph_mount_info *cmount, const char *path, snap_info *snap_info): + pass + void ceph_free_snap_info_buffer(snap_info *snap_info): + pass int ceph_mkdirs(ceph_mount_info *cmount, const char *path, mode_t mode): pass int ceph_closedir(ceph_mount_info *cmount, ceph_dir_result *dirp):