]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
pybind/rbd: add image metadata methods
authorMykola Golub <mgolub@mirantis.com>
Tue, 11 Apr 2017 20:34:34 +0000 (22:34 +0200)
committerMykola Golub <mgolub@mirantis.com>
Wed, 12 Apr 2017 08:47:09 +0000 (10:47 +0200)
Fixes: http://tracker.ceph.com/issues/19451
Signed-off-by: Mykola Golub <mgolub@mirantis.com>
src/pybind/rbd/rbd.pyx
src/test/pybind/test_rbd.py

index 90f855e502e82787ef99757fbb3e0e2944e6e050..08d067bf5347934ce8231ba0110f589dee4e3039 100644 (file)
@@ -326,6 +326,14 @@ cdef extern from "rbd/librbd.h" nogil:
     void rbd_aio_release(rbd_completion_t c)
     int rbd_aio_flush(rbd_image_t image, rbd_completion_t c)
 
+    int rbd_metadata_get(rbd_image_t image, const char *key, char *value,
+                         size_t *val_len)
+    int rbd_metadata_set(rbd_image_t image, const char *key, const char *value)
+    int rbd_metadata_remove(rbd_image_t image, const char *key)
+    int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
+                          char *keys, size_t *key_len, char *values,
+                          size_t *vals_len)
+
 RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING
 RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2
 RBD_FEATURE_EXCLUSIVE_LOCK = _RBD_FEATURE_EXCLUSIVE_LOCK
@@ -2552,6 +2560,81 @@ written." % (self.name, ret, length))
 
         return completion
 
+    def metadata_get(self, key):
+        """
+        Get image metadata for the given key.
+
+        :param key: metadata key
+        :type key: str
+        :returns: str - image id
+        """
+        key = cstr(key, 'key')
+        cdef:
+            char *_key = key
+            size_t size = 4096
+            char *value = NULL
+            int ret
+        try:
+            while True:
+                value = <char *>realloc_chk(value, size)
+                with nogil:
+                    ret = rbd_metadata_get(self.image, _key, value, &size)
+                if ret != -errno.ERANGE:
+                    break
+            if ret != 0:
+                raise make_ex(ret, 'error getting metadata %s for image %s' %
+                              (self.key, self.name,))
+            return decode_cstr(value)
+        finally:
+            free(value)
+
+    def metadata_set(self, key, value):
+        """
+        Set image metadata for the given key.
+
+        :param key: metadata key
+        :type key: str
+        :param value: metadata value
+        :type value: str
+        """
+        key = cstr(key, 'key')
+        value = cstr(value, 'value')
+        cdef:
+            char *_key = key
+            char *_value = value
+        with nogil:
+            ret = rbd_metadata_set(self.image, _key, _value)
+
+        if ret != 0:
+            raise make_ex(ret, 'error setting metadata %s for image %s' %
+                          (self.key, self.name,))
+
+
+    def metadata_remove(self, key):
+        """
+        Remove image metadata for the given key.
+
+        :param key: metadata key
+        :type key: str
+        """
+        key = cstr(key, 'key')
+        cdef:
+            char *_key = key
+        with nogil:
+            ret = rbd_metadata_remove(self.image, _key)
+
+        if ret != 0:
+            raise make_ex(ret, 'error removing metadata %s for image %s' %
+                          (self.key, self.name,))
+
+    def metadata_list(self):
+        """
+        List image metadata.
+
+        :returns: :class:`MetadataIterator`
+        """
+        return MetadataIterator(self)
+
 cdef class LockOwnerIterator(object):
     """
     Iterator over managed lock owners for an image
@@ -2600,6 +2683,69 @@ cdef class LockOwnerIterator(object):
             rbd_lock_get_owners_cleanup(self.lock_owners, self.num_lock_owners)
             free(self.lock_owners)
 
+cdef class MetadataIterator(object):
+    """
+    Iterator over metadata list for an image.
+
+    Yields ``(key, value)`` tuple.
+
+    * ``key`` (str) - metadata key
+    * ``value`` (str) - metadata value
+    """
+
+    cdef:
+        object image_name
+        rbd_image_t image
+        char *last_read
+        uint64_t max_read
+        object next_chunk
+
+    def __init__(self, Image image):
+        self.image_name = image.name
+        self.image = image.image
+        self.last_read = strdup("")
+        self.max_read = 32
+        self.get_next_chunk()
+
+    def __iter__(self):
+        while len(self.next_chunk) > 0:
+            for pair in self.next_chunk:
+                yield pair
+            if len(self.next_chunk) < self.max_read:
+                break
+            self.get_next_chunk()
+
+    def get_next_chunk(self):
+        cdef:
+            char *c_keys = NULL
+            size_t keys_size = 4096
+            char *c_vals = NULL
+            size_t vals_size = 4096
+        try:
+            while True:
+                c_keys = <char *>realloc_chk(c_keys, keys_size)
+                c_vals = <char *>realloc_chk(c_vals, vals_size)
+                with nogil:
+                    ret = rbd_metadata_list(self.image, self.last_read,
+                                            self.max_read, c_keys, &keys_size,
+                                            c_vals, &vals_size)
+                if ret >= 0:
+                    break
+                elif ret != -errno.ERANGE:
+                    raise make_ex(ret, 'error listing metadata for image %s' %
+                                  (self.image_name,))
+            keys = [decode_cstr(key) for key in
+                        c_keys[:keys_size].split(b'\0') if key]
+            vals = [decode_cstr(val) for val in
+                        c_vals[:vals_size].split(b'\0') if val]
+            if len(keys) > 0:
+                free(self.last_read)
+                self.last_read = strdup(keys[-1])
+            self.next_chunk = zip(keys, vals)
+        finally:
+            free(c_keys)
+            free(c_vals)
+
 cdef class SnapIterator(object):
     """
     Iterator over snapshot info for an image.
index e1961687d74c5713928009adbdad7934b1365b51..c3eb371d5e62317fb981d96ee678c380c91316bf 100644 (file)
@@ -819,7 +819,34 @@ class TestImage(object):
         eq(retval[0], 0)
         eq(sys.getrefcount(comp), 2)
 
+    def test_metadata(self):
+        metadata = list(self.image.metadata_list())
+        eq(len(metadata), 0)
+        self.image.metadata_set("key1", "value1")
+        self.image.metadata_set("key2", "value2")
+        value = self.image.metadata_get("key1")
+        eq(value, "value1")
+        value = self.image.metadata_get("key2")
+        eq(value, "value2")
+        metadata = list(self.image.metadata_list())
+        eq(len(metadata), 2)
+        self.image.metadata_remove("key1")
+        metadata = list(self.image.metadata_list())
+        eq(len(metadata), 1)
+        eq(metadata[0], ("key2", "value2"))
+        self.image.metadata_remove("key2")
+        metadata = list(self.image.metadata_list())
+        eq(len(metadata), 0)
 
+        N = 65
+        for i in xrange(N):
+            self.image.metadata_set("key" + str(i), "X" * 1025)
+        metadata = list(self.image.metadata_list())
+        eq(len(metadata), N)
+        for i in xrange(N):
+            self.image.metadata_remove("key" + str(i))
+            metadata = list(self.image.metadata_list())
+            eq(len(metadata), N - i - 1)
 
 def check_diff(image, offset, length, from_snapshot, expected):
     extents = []