Add a file based driver
Change-Id: Ie299a8a27045526c27907cdf97b8a240325d908c
This commit is contained in:
		@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										131
									
								
								tooz/drivers/file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								tooz/drivers/file.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -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}),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user