python-rados: Add object lock support

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: 

Signed-off-by: Mehdi Abaakouk <sileht@redhat.com>
This commit is contained in:
Mehdi Abaakouk 2014-12-05 23:38:56 +00:00 committed by Mehdi Abaakouk
parent 9029813b27
commit f5bf75fa41
2 changed files with 153 additions and 1 deletions
src
pybind
test/pybind

View 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:

View 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):