]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
python-rados: Add object lock support 3099/head
authorMehdi Abaakouk <sileht@sileht.net>
Fri, 5 Dec 2014 23:38:56 +0000 (23:38 +0000)
committerMehdi Abaakouk <sileht@redhat.com>
Sat, 6 Dec 2014 15:28:01 +0000 (16:28 +0100)
This change adds to the python binding the support of:
 - rados_lock_exclusive
 - rados_lock_shared
 - rados_unlock

http://tracker.ceph.com/issues/6114 Refs: #6114

Signed-off-by: Mehdi Abaakouk <sileht@redhat.com>
src/pybind/rados.py
src/test/pybind/test_rados.py

index 8f48938c574576c94e8b4422acb7975fcd518c66..82521cacb8e6eb32339da0200fd517323d8ebc39 100644 (file)
@@ -5,7 +5,7 @@ Copyright 2011, Hannu Valtonen <hannu.valtonen@ormod.com>
 """
 from ctypes import CDLL, c_char_p, c_size_t, c_void_p, c_char, c_int, c_long, \
     c_ulong, create_string_buffer, byref, Structure, c_uint64, c_ubyte, \
-    pointer, CFUNCTYPE, c_int64
+    pointer, CFUNCTYPE, c_int64, c_uint8
 from ctypes.util import find_library
 import ctypes
 import errno
@@ -41,6 +41,10 @@ class ObjectExists(Error):
     """ `ObjectExists` class, derived from `Error` """
     pass
 
+class ObjectBusy(Error):
+    """ `ObjectBusy` class, derived from `Error` """
+    pass
+
 class IOError(Error):
     """ `IOError` class, derived from `Error` """
     pass
@@ -90,6 +94,7 @@ def make_ex(ret, msg):
         errno.EIO       : IOError,
         errno.ENOSPC    : NoSpace,
         errno.EEXIST    : ObjectExists,
+        errno.EBUSY     : ObjectBusy,
         errno.ENODATA   : NoData,
         errno.EINTR     : InterruptedOrTimeoutError,
         errno.ETIMEDOUT : TimedOut
@@ -122,6 +127,10 @@ class rados_cluster_stat_t(Structure):
                 ("kb_avail", c_uint64),
                 ("num_objects", c_uint64)]
 
+class timeval(Structure):
+    _fields_ = [("tv_sec", c_long), ("tv_usec", c_long)]
+
+
 class Version(object):
     """ Version information """
     def __init__(self, major, minor, extra):
@@ -1777,6 +1786,126 @@ returned %d, but should return zero on success." % (self.name, ret))
         self.require_ioctx_open()
         return run_in_thread(self.librados.rados_get_last_version, (self.io,))
 
+    def lock_exclusive(self, key, name, cookie, desc="", duration=None, flags=0):
+
+        """
+        Take an exclusive lock on an object
+
+        :param key: name of the object
+        :type key: str
+        :param name: name of the lock
+        :type name: str
+        :param cookie: cookie of the lock
+        :type cookie: str
+        :param desc: description of the lock
+        :type desc: str
+        :param duration: duration of the lock in seconds
+        :type duration: int
+        :param flags: flags
+        :type flags: int
+
+        :raises: :class:`TypeError`
+        :raises: :class:`Error`
+        """
+        self.require_ioctx_open()
+        if not isinstance(key, str):
+            raise TypeError('key must be a string')
+        if not isinstance(name, str):
+            raise TypeError('name must be a string')
+        if not isinstance(cookie, str):
+            raise TypeError('cookie must be a string')
+        if not isinstance(desc, str):
+            raise TypeError('desc must be a string')
+        if duration is not None and not isinstance(duration, int):
+            raise TypeError('duration must be a integer')
+        if not isinstance(flags, int):
+            raise TypeError('flags must be a integer')
+
+        ret = run_in_thread(self.librados.rados_lock_exclusive,
+                            (self.io, c_char_p(key), c_char_p(name), c_char_p(cookie),
+                             c_char_p(desc),
+                             timeval(duration, None) if duration is None else None,
+                             c_uint8(flags)))
+        if ret < 0:
+            raise make_ex(ret, "Ioctx.rados_lock_exclusive(%s): failed to set lock %s on %s" % (self.name, name, key))
+
+    def lock_shared(self, key, name, cookie, tag, desc="", duration=None, flags=0):
+
+        """
+        Take a shared lock on an object
+
+        :param key: name of the object
+        :type key: str
+        :param name: name of the lock
+        :type name: str
+        :param cookie: cookie of the lock
+        :type cookie: str
+        :param tag: tag of the lock
+        :type tag: str
+        :param desc: description of the lock
+        :type desc: str
+        :param duration: duration of the lock in seconds
+        :type duration: int
+        :param flags: flags
+        :type flags: int
+
+        :raises: :class:`TypeError`
+        :raises: :class:`Error`
+        """
+        self.require_ioctx_open()
+        if not isinstance(key, str):
+            raise TypeError('key must be a string')
+        if not isinstance(name, str):
+            raise TypeError('name must be a string')
+        if not isinstance(cookie, str):
+            raise TypeError('cookie must be a string')
+        if not isinstance(tag, str):
+            raise TypeError('tag must be a string')
+        if not isinstance(desc, str):
+            raise TypeError('desc must be a string')
+        if duration is not None and not isinstance(duration, int):
+            raise TypeError('duration must be a integer')
+        if not isinstance(flags, int):
+            raise TypeError('flags must be a integer')
+
+        ret = run_in_thread(self.librados.rados_lock_shared,
+                            (self.io, c_char_p(key), c_char_p(name), c_char_p(cookie),
+                             c_char_p(tag), c_char_p(desc),
+                             timeval(duration, None) if duration is None else None,
+                             c_uint8(flags)))
+        if ret < 0:
+            raise make_ex(ret, "Ioctx.rados_lock_exclusive(%s): failed to set lock %s on %s" % (self.name, name, key))
+
+    def unlock(self, key, name, cookie):
+
+        """
+        Release a shared or exclusive lock on an object
+
+        :param key: name of the object
+        :type key: str
+        :param name: name of the lock
+        :type name: str
+        :param cookie: cookie of the lock
+        :type cookie: str
+
+        :raises: :class:`TypeError`
+        :raises: :class:`Error`
+        """
+        self.require_ioctx_open()
+        if not isinstance(key, str):
+            raise TypeError('key must be a string')
+        if not isinstance(name, str):
+            raise TypeError('name must be a string')
+        if not isinstance(cookie, str):
+            raise TypeError('cookie must be a string')
+
+        ret = run_in_thread(self.librados.rados_unlock,
+                            (self.io, c_char_p(key), c_char_p(name), c_char_p(cookie)))
+        if ret < 0:
+            raise make_ex(ret, "Ioctx.rados_lock_exclusive(%s): failed to set lock %s on %s" % (self.name, name, key))
+
+
+
 def set_object_locator(func):
     def retfunc(self, *args, **kwargs):
         if self.locator_key is not None:
index e96010968c250d269af722c8bd78af7dcc4bb10f..758993fbd894a1a5b90a24bbc6824c1162fbdd56 100644 (file)
@@ -1,5 +1,6 @@
 from nose.tools import eq_ as eq, assert_raises
 from rados import (Rados, Error, Object, ObjectExists, ObjectNotFound,
+                   ObjectBusy,
                    ANONYMOUS_AUID, ADMIN_AUID, LIBRADOS_ALL_NSPACES)
 import time
 import threading
@@ -408,6 +409,28 @@ class TestIoctx(object):
 
         [i.remove() for i in self.ioctx.list_objects()]
 
+    def test_lock(self):
+        self.ioctx.lock_exclusive("foo", "lock", "locker", "desc_lock",
+                                  10000, 0)
+        assert_raises(ObjectExists,
+                      self.ioctx.lock_exclusive,
+                      "foo", "lock", "locker", "desc_lock", 10000, 0)
+        self.ioctx.unlock("foo", "lock", "locker")
+        assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker")
+
+        self.ioctx.lock_shared("foo", "lock", "locker1", "tag", "desc_lock",
+                               10000, 0)
+        self.ioctx.lock_shared("foo", "lock", "locker2", "tag", "desc_lock",
+                               10000, 0)
+        assert_raises(ObjectBusy,
+                      self.ioctx.lock_exclusive,
+                      "foo", "lock", "locker3", "desc_lock", 10000, 0)
+        self.ioctx.unlock("foo", "lock", "locker1")
+        self.ioctx.unlock("foo", "lock", "locker2")
+        assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker1")
+        assert_raises(ObjectNotFound, self.ioctx.unlock, "foo", "lock", "locker2")
+
+
 class TestObject(object):
 
     def setUp(self):