mirror of
https://github.com/ceph/ceph
synced 2025-01-17 08:21:47 +00:00
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: #6114 Signed-off-by: Mehdi Abaakouk <sileht@redhat.com>
This commit is contained in:
parent
9029813b27
commit
f5bf75fa41
src
@ -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:
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user