summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Danjou <julien@danjou.info>2015-01-12 13:35:04 +0100
committerJulien Danjou <julien@danjou.info>2015-01-16 10:31:13 +0100
commit405dfec672cd036ab4905cd27c1bbd8180cb8228 (patch)
tree4a65b771bff2fcda5cd0909e29f8ea06c4a7f225
parent26c39acd31725aaa8ac0bbc0b35cede70cd7133f (diff)
downloadtooz-405dfec672cd036ab4905cd27c1bbd8180cb8228.tar.gz
Add a file based driver0.11
Change-Id: Ie299a8a27045526c27907cdf97b8a240325d908c
-rw-r--r--doc/source/drivers.rst4
-rw-r--r--setup.cfg1
-rw-r--r--tooz/drivers/file.py131
-rw-r--r--tooz/tests/test_coordination.py3
4 files changed, 138 insertions, 1 deletions
diff --git a/doc/source/drivers.rst b/doc/source/drivers.rst
index 75a2524..75404bd 100644
--- a/doc/source/drivers.rst
+++ b/doc/source/drivers.rst
@@ -24,6 +24,10 @@ API, some of them have different properties:
some basic group primitives (with huge limitations). The lock can only be
distributed locally to a computer processes.
+* `file` is based on file and only implements a lock based on POSIX or Window
+ file locking for now. The lock can only be distributed locally to a computer
+ processes.
+
* `zake`_ is a driver using a fake implementation of ZooKeeper and can be
used to use Tooz in your unit tests suite for example.
diff --git a/setup.cfg b/setup.cfg
index 5221a3b..8b0bc11 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -32,6 +32,7 @@ tooz.backends =
redis = tooz.drivers.redis:RedisDriver
postgresql = tooz.drivers.pgsql:PostgresDriver
mysql = tooz.drivers.mysql:MySQLDriver
+ file = tooz.drivers.file:FileDriver
[build_sphinx]
all_files = 1
diff --git a/tooz/drivers/file.py b/tooz/drivers/file.py
new file mode 100644
index 0000000..17dccc5
--- /dev/null
+++ b/tooz/drivers/file.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright © 2015 eNovance
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import errno
+import os
+import weakref
+
+import tooz
+from tooz import coordination
+from tooz.drivers import _retry
+from tooz import locking
+
+
+class FileLock(locking.Lock):
+ """A file based lock."""
+
+ def __init__(self, path):
+ super(FileLock, self).__init__(path)
+ self.acquired = False
+
+ def acquire(self, blocking=True):
+ self.lockfile = open(self.name, 'a')
+
+ @_retry.retry(stop_max_delay=blocking)
+ def _lock():
+ # NOTE(jd) If the same process try to grab the lock, the call to
+ # self.lock() will succeed, so we track internally if the process
+ # already has the lock.
+ if self.acquired is True:
+ if blocking:
+ raise _retry.Retry
+ return False
+ try:
+ self.lock()
+ except IOError as e:
+ if e.errno in (errno.EACCESS, errno.EAGAIN):
+ if blocking:
+ raise _retry.Retry
+ return False
+ else:
+ self.acquired = True
+ return True
+
+ return _lock()
+
+ def release(self):
+ self.unlock()
+ self.lockfile.close()
+ self.acquired = False
+
+ def lock(self):
+ raise NotImplementedError
+
+ def unlock(self):
+ raise NotImplementedError
+
+
+class WindowsFileLock(FileLock):
+ def lock(self):
+ msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
+
+ def unlock(self):
+ msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
+
+
+class PosixFileLock(FileLock):
+ def lock(self):
+ fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+
+ def unlock(self):
+ fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
+
+
+if os.name == 'nt':
+ import msvcrt
+ LockClass = WindowsFileLock
+else:
+ import fcntl
+ LockClass = PosixFileLock
+
+
+class FileDriver(coordination.CoordinationDriver):
+ """A file based driver."""
+
+ LOCKS = weakref.WeakValueDictionary()
+
+ def __init__(self, member_id, parsed_url, options):
+ """Initialize the file driver."""
+ super(FileDriver, self).__init__()
+ self._lockdir = parsed_url.path
+
+ def get_lock(self, name):
+ path = os.path.abspath(os.path.join(self._lockdir, name.decode()))
+ lock = LockClass(path)
+ return self.LOCKS.setdefault(path, lock)
+
+ @staticmethod
+ def watch_join_group(group_id, callback):
+ raise tooz.NotImplemented
+
+ @staticmethod
+ def unwatch_join_group(group_id, callback):
+ raise tooz.NotImplemented
+
+ @staticmethod
+ def watch_leave_group(group_id, callback):
+ raise tooz.NotImplemented
+
+ @staticmethod
+ def unwatch_leave_group(group_id, callback):
+ raise tooz.NotImplemented
+
+ @staticmethod
+ def watch_elected_as_leader(group_id, callback):
+ raise tooz.NotImplemented
+
+ @staticmethod
+ def unwatch_elected_as_leader(group_id, callback):
+ raise tooz.NotImplemented
diff --git a/tooz/tests/test_coordination.py b/tooz/tests/test_coordination.py
index c118821..381b591 100644
--- a/tooz/tests/test_coordination.py
+++ b/tooz/tests/test_coordination.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright © 2013-2014 eNovance Inc. All Rights Reserved.
+# Copyright © 2013-2015 eNovance Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -36,6 +36,7 @@ class TestAPI(testscenarios.TestWithScenarios,
'bad_url': 'memcached://localhost:1',
'timeout_capable': True}),
('ipc', {'url': 'ipc://'}),
+ ('file', {'url': 'file:///tmp'}),
('redis', {'url': os.getenv("TOOZ_TEST_REDIS_URL"),
'bad_url': 'redis://localhost:1',
'timeout_capable': True}),