aboutsummaryrefslogtreecommitdiffstats
path: root/keystone-moon/keystone/common/kvs
diff options
context:
space:
mode:
Diffstat (limited to 'keystone-moon/keystone/common/kvs')
-rw-r--r--keystone-moon/keystone/common/kvs/__init__.py32
-rw-r--r--keystone-moon/keystone/common/kvs/backends/__init__.py0
-rw-r--r--keystone-moon/keystone/common/kvs/backends/inmemdb.py68
-rw-r--r--keystone-moon/keystone/common/kvs/backends/memcached.py195
-rw-r--r--keystone-moon/keystone/common/kvs/core.py450
-rw-r--r--keystone-moon/keystone/common/kvs/legacy.py61
6 files changed, 0 insertions, 806 deletions
diff --git a/keystone-moon/keystone/common/kvs/__init__.py b/keystone-moon/keystone/common/kvs/__init__.py
deleted file mode 100644
index 354bbd8a..00000000
--- a/keystone-moon/keystone/common/kvs/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2013 Metacloud, Inc.
-#
-# 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.
-
-from dogpile.cache import region
-
-from keystone.common.kvs.core import * # noqa
-
-
-# NOTE(morganfainberg): Provided backends are registered here in the __init__
-# for the kvs system. Any out-of-tree backends should be registered via the
-# ``backends`` option in the ``[kvs]`` section of the Keystone configuration
-# file.
-region.register_backend(
- 'openstack.kvs.Memory',
- 'keystone.common.kvs.backends.inmemdb',
- 'MemoryBackend')
-
-region.register_backend(
- 'openstack.kvs.Memcached',
- 'keystone.common.kvs.backends.memcached',
- 'MemcachedBackend')
diff --git a/keystone-moon/keystone/common/kvs/backends/__init__.py b/keystone-moon/keystone/common/kvs/backends/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/keystone-moon/keystone/common/kvs/backends/__init__.py
+++ /dev/null
diff --git a/keystone-moon/keystone/common/kvs/backends/inmemdb.py b/keystone-moon/keystone/common/kvs/backends/inmemdb.py
deleted file mode 100644
index 379b54bf..00000000
--- a/keystone-moon/keystone/common/kvs/backends/inmemdb.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2013 Metacloud, Inc.
-#
-# 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.
-
-"""Keystone In-Memory Dogpile.cache backend implementation."""
-
-import copy
-
-from dogpile.cache import api
-
-
-NO_VALUE = api.NO_VALUE
-
-
-class MemoryBackend(api.CacheBackend):
- """A backend that uses a plain dictionary.
-
- There is no size management, and values which are placed into the
- dictionary will remain until explicitly removed. Note that Dogpile's
- expiration of items is based on timestamps and does not remove them from
- the cache.
-
- E.g.::
-
- from dogpile.cache import make_region
-
- region = make_region().configure(
- 'keystone.common.kvs.Memory'
- )
- """
-
- def __init__(self, arguments):
- self._db = {}
-
- def _isolate_value(self, value):
- if value is not NO_VALUE:
- return copy.deepcopy(value)
- return value
-
- def get(self, key):
- return self._isolate_value(self._db.get(key, NO_VALUE))
-
- def get_multi(self, keys):
- return [self.get(key) for key in keys]
-
- def set(self, key, value):
- self._db[key] = self._isolate_value(value)
-
- def set_multi(self, mapping):
- for key, value in mapping.items():
- self.set(key, value)
-
- def delete(self, key):
- self._db.pop(key, None)
-
- def delete_multi(self, keys):
- for key in keys:
- self.delete(key)
diff --git a/keystone-moon/keystone/common/kvs/backends/memcached.py b/keystone-moon/keystone/common/kvs/backends/memcached.py
deleted file mode 100644
index a65cf877..00000000
--- a/keystone-moon/keystone/common/kvs/backends/memcached.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# Copyright 2013 Metacloud, Inc.
-#
-# 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.
-
-"""Keystone Memcached dogpile.cache backend implementation."""
-
-import random as _random
-import time
-
-from dogpile.cache import api
-from dogpile.cache.backends import memcached
-from oslo_cache.backends import memcache_pool
-from oslo_config import cfg
-from six.moves import range
-
-from keystone import exception
-from keystone.i18n import _
-
-
-CONF = cfg.CONF
-NO_VALUE = api.NO_VALUE
-random = _random.SystemRandom()
-
-VALID_DOGPILE_BACKENDS = dict(
- pylibmc=memcached.PylibmcBackend,
- bmemcached=memcached.BMemcachedBackend,
- memcached=memcached.MemcachedBackend,
- pooled_memcached=memcache_pool.PooledMemcachedBackend)
-
-
-class MemcachedLock(object):
- """Simple distributed lock using memcached.
-
- This is an adaptation of the lock featured at
- http://amix.dk/blog/post/19386
-
- """
-
- def __init__(self, client_fn, key, lock_timeout, max_lock_attempts):
- self.client_fn = client_fn
- self.key = "_lock" + key
- self.lock_timeout = lock_timeout
- self.max_lock_attempts = max_lock_attempts
-
- def acquire(self, wait=True):
- client = self.client_fn()
- for i in range(self.max_lock_attempts):
- if client.add(self.key, 1, self.lock_timeout):
- return True
- elif not wait:
- return False
- else:
- sleep_time = random.random() # nosec : random is not used for
- # crypto or security, it's just the time to delay between
- # retries.
- time.sleep(sleep_time)
- raise exception.UnexpectedError(
- _('Maximum lock attempts on %s occurred.') % self.key)
-
- def release(self):
- client = self.client_fn()
- client.delete(self.key)
-
-
-class MemcachedBackend(object):
- """Pivot point to leverage the various dogpile.cache memcached backends.
-
- To specify a specific dogpile.cache memcached backend, pass the argument
- `memcached_backend` set to one of the provided memcached backends (at this
- time `memcached`, `bmemcached`, `pylibmc` and `pooled_memcached` are
- valid).
- """
-
- def __init__(self, arguments):
- self._key_mangler = None
- self.raw_no_expiry_keys = set(arguments.pop('no_expiry_keys', set()))
- self.no_expiry_hashed_keys = set()
-
- self.lock_timeout = arguments.pop('lock_timeout', None)
- self.max_lock_attempts = arguments.pop('max_lock_attempts', 15)
- # NOTE(morganfainberg): Remove distributed locking from the arguments
- # passed to the "real" backend if it exists.
- arguments.pop('distributed_lock', None)
- backend = arguments.pop('memcached_backend', None)
- if 'url' not in arguments:
- # FIXME(morganfainberg): Log deprecation warning for old-style
- # configuration once full dict_config style configuration for
- # KVS backends is supported. For now use the current memcache
- # section of the configuration.
- arguments['url'] = CONF.memcache.servers
-
- if backend is None:
- # NOTE(morganfainberg): Use the basic memcached backend if nothing
- # else is supplied.
- self.driver = VALID_DOGPILE_BACKENDS['memcached'](arguments)
- else:
- if backend not in VALID_DOGPILE_BACKENDS:
- raise ValueError(
- _('Backend `%(backend)s` is not a valid memcached '
- 'backend. Valid backends: %(backend_list)s') %
- {'backend': backend,
- 'backend_list': ','.join(VALID_DOGPILE_BACKENDS.keys())})
- else:
- self.driver = VALID_DOGPILE_BACKENDS[backend](arguments)
-
- def __getattr__(self, name):
- """Forward calls to the underlying driver."""
- f = getattr(self.driver, name)
- setattr(self, name, f)
- return f
-
- def _get_set_arguments_driver_attr(self, exclude_expiry=False):
-
- # NOTE(morganfainberg): Shallow copy the .set_arguments dict to
- # ensure no changes cause the values to change in the instance
- # variable.
- set_arguments = getattr(self.driver, 'set_arguments', {}).copy()
-
- if exclude_expiry:
- # NOTE(morganfainberg): Explicitly strip out the 'time' key/value
- # from the set_arguments in the case that this key isn't meant
- # to expire
- set_arguments.pop('time', None)
- return set_arguments
-
- def set(self, key, value):
- mapping = {key: value}
- self.set_multi(mapping)
-
- def set_multi(self, mapping):
- mapping_keys = set(mapping.keys())
- no_expiry_keys = mapping_keys.intersection(self.no_expiry_hashed_keys)
- has_expiry_keys = mapping_keys.difference(self.no_expiry_hashed_keys)
-
- if no_expiry_keys:
- # NOTE(morganfainberg): For keys that have expiry excluded,
- # bypass the backend and directly call the client. Bypass directly
- # to the client is required as the 'set_arguments' are applied to
- # all ``set`` and ``set_multi`` calls by the driver, by calling
- # the client directly it is possible to exclude the ``time``
- # argument to the memcached server.
- new_mapping = {k: mapping[k] for k in no_expiry_keys}
- set_arguments = self._get_set_arguments_driver_attr(
- exclude_expiry=True)
- self.driver.client.set_multi(new_mapping, **set_arguments)
-
- if has_expiry_keys:
- new_mapping = {k: mapping[k] for k in has_expiry_keys}
- self.driver.set_multi(new_mapping)
-
- @classmethod
- def from_config_dict(cls, config_dict, prefix):
- prefix_len = len(prefix)
- return cls(
- {key[prefix_len:]: config_dict[key] for key in config_dict
- if key.startswith(prefix)})
-
- @property
- def key_mangler(self):
- if self._key_mangler is None:
- self._key_mangler = self.driver.key_mangler
- return self._key_mangler
-
- @key_mangler.setter
- def key_mangler(self, key_mangler):
- if callable(key_mangler):
- self._key_mangler = key_mangler
- self._rehash_keys()
- elif key_mangler is None:
- # NOTE(morganfainberg): Set the hashed key map to the unhashed
- # list since we no longer have a key_mangler.
- self._key_mangler = None
- self.no_expiry_hashed_keys = self.raw_no_expiry_keys
- else:
- raise TypeError(_('`key_mangler` functions must be callable.'))
-
- def _rehash_keys(self):
- no_expire = set()
- for key in self.raw_no_expiry_keys:
- no_expire.add(self._key_mangler(key))
- self.no_expiry_hashed_keys = no_expire
-
- def get_mutex(self, key):
- return MemcachedLock(lambda: self.driver.client, key,
- self.lock_timeout, self.max_lock_attempts)
diff --git a/keystone-moon/keystone/common/kvs/core.py b/keystone-moon/keystone/common/kvs/core.py
deleted file mode 100644
index 064825f8..00000000
--- a/keystone-moon/keystone/common/kvs/core.py
+++ /dev/null
@@ -1,450 +0,0 @@
-# Copyright 2013 Metacloud, Inc.
-#
-# 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 contextlib
-import threading
-import time
-import weakref
-
-from dogpile.cache import api
-from dogpile.cache import proxy
-from dogpile.cache import region
-from dogpile.cache import util as dogpile_util
-from dogpile.core import nameregistry
-from oslo_config import cfg
-from oslo_log import log
-from oslo_utils import importutils
-from oslo_utils import reflection
-
-from keystone import exception
-from keystone.i18n import _
-from keystone.i18n import _LI
-from keystone.i18n import _LW
-
-
-__all__ = ('KeyValueStore', 'KeyValueStoreLock', 'LockTimeout',
- 'get_key_value_store')
-
-
-BACKENDS_REGISTERED = False
-CONF = cfg.CONF
-KEY_VALUE_STORE_REGISTRY = weakref.WeakValueDictionary()
-LOCK_WINDOW = 1
-LOG = log.getLogger(__name__)
-NO_VALUE = api.NO_VALUE
-
-
-def _register_backends():
- # NOTE(morganfainberg): This function exists to ensure we do not try and
- # register the backends prior to the configuration object being fully
- # available. We also need to ensure we do not register a given backend
- # more than one time. All backends will be prefixed with openstack.kvs
- # as the "short" name to reference them for configuration purposes. This
- # function is used in addition to the pre-registered backends in the
- # __init__ file for the KVS system.
- global BACKENDS_REGISTERED
-
- if not BACKENDS_REGISTERED:
- prefix = 'openstack.kvs.%s'
- for backend in CONF.kvs.backends:
- module, cls = backend.rsplit('.', 1)
- backend_name = prefix % cls
- LOG.debug(('Registering Dogpile Backend %(backend_path)s as '
- '%(backend_name)s'),
- {'backend_path': backend, 'backend_name': backend_name})
- region.register_backend(backend_name, module, cls)
- BACKENDS_REGISTERED = True
-
-
-def sha1_mangle_key(key):
- """Wrapper for dogpile's sha1_mangle_key.
-
- Taken from oslo_cache.core._sha1_mangle_key
-
- dogpile's sha1_mangle_key function expects an encoded string, so we
- should take steps to properly handle multiple inputs before passing
- the key through.
- """
- try:
- key = key.encode('utf-8', errors='xmlcharrefreplace')
- except (UnicodeError, AttributeError): # nosec
- # NOTE(stevemar): if encoding fails just continue anyway.
- pass
- return dogpile_util.sha1_mangle_key(key)
-
-
-class LockTimeout(exception.UnexpectedError):
- debug_message_format = _('Lock Timeout occurred for key, %(target)s')
-
-
-class KeyValueStore(object):
- """Basic KVS manager object to support Keystone Key-Value-Store systems.
-
- This manager also supports the concept of locking a given key resource to
- allow for a guaranteed atomic transaction to the backend.
- """
-
- def __init__(self, kvs_region):
- self.locking = True
- self._lock_timeout = 0
- self._region = kvs_region
- self._security_strategy = None
- self._secret_key = None
- self._lock_registry = nameregistry.NameRegistry(self._create_mutex)
-
- def configure(self, backing_store, key_mangler=None, proxy_list=None,
- locking=True, **region_config_args):
- """Configure the KeyValueStore instance.
-
- :param backing_store: dogpile.cache short name of the region backend
- :param key_mangler: key_mangler function
- :param proxy_list: list of proxy classes to apply to the region
- :param locking: boolean that allows disabling of locking mechanism for
- this instantiation
- :param region_config_args: key-word args passed to the dogpile.cache
- backend for configuration
- """
- if self.is_configured:
- # NOTE(morganfainberg): It is a bad idea to reconfigure a backend,
- # there are a lot of pitfalls and potential memory leaks that could
- # occur. By far the best approach is to re-create the KVS object
- # with the new configuration.
- raise RuntimeError(_('KVS region %s is already configured. '
- 'Cannot reconfigure.') % self._region.name)
-
- self.locking = locking
- self._lock_timeout = region_config_args.pop(
- 'lock_timeout', CONF.kvs.default_lock_timeout)
- self._configure_region(backing_store, **region_config_args)
- self._set_key_mangler(key_mangler)
- self._apply_region_proxy(proxy_list)
-
- @property
- def is_configured(self):
- return 'backend' in self._region.__dict__
-
- def _apply_region_proxy(self, proxy_list):
- if isinstance(proxy_list, list):
- proxies = []
-
- for item in proxy_list:
- if isinstance(item, str):
- LOG.debug('Importing class %s as KVS proxy.', item)
- pxy = importutils.import_class(item)
- else:
- pxy = item
-
- if issubclass(pxy, proxy.ProxyBackend):
- proxies.append(pxy)
- else:
- pxy_cls_name = reflection.get_class_name(
- pxy, fully_qualified=False)
- LOG.warning(_LW('%s is not a dogpile.proxy.ProxyBackend'),
- pxy_cls_name)
-
- for proxy_cls in reversed(proxies):
- proxy_cls_name = reflection.get_class_name(
- proxy_cls, fully_qualified=False)
- LOG.info(_LI('Adding proxy \'%(proxy)s\' to KVS %(name)s.'),
- {'proxy': proxy_cls_name,
- 'name': self._region.name})
- self._region.wrap(proxy_cls)
-
- def _assert_configured(self):
- if'backend' not in self._region.__dict__:
- raise exception.UnexpectedError(_('Key Value Store not '
- 'configured: %s'),
- self._region.name)
-
- def _set_keymangler_on_backend(self, key_mangler):
- try:
- self._region.backend.key_mangler = key_mangler
- except Exception as e:
- # NOTE(morganfainberg): The setting of the key_mangler on the
- # backend is used to allow the backend to
- # calculate a hashed key value as needed. Not all backends
- # require the ability to calculate hashed keys. If the
- # backend does not support/require this feature log a
- # debug line and move on otherwise raise the proper exception.
- # Support of the feature is implied by the existence of the
- # 'raw_no_expiry_keys' attribute.
- if not hasattr(self._region.backend, 'raw_no_expiry_keys'):
- LOG.debug(('Non-expiring keys not supported/required by '
- '%(region)s backend; unable to set '
- 'key_mangler for backend: %(err)s'),
- {'region': self._region.name, 'err': e})
- else:
- raise
-
- def _set_key_mangler(self, key_mangler):
- # Set the key_mangler that is appropriate for the given region being
- # configured here. The key_mangler function is called prior to storing
- # the value(s) in the backend. This is to help prevent collisions and
- # limit issues such as memcache's limited cache_key size.
- use_backend_key_mangler = getattr(self._region.backend,
- 'use_backend_key_mangler', False)
- if ((key_mangler is None or use_backend_key_mangler) and
- (self._region.backend.key_mangler is not None)):
- # NOTE(morganfainberg): Use the configured key_mangler as a first
- # choice. Second choice would be the key_mangler defined by the
- # backend itself. Finally, fall back to the defaults. The one
- # exception is if the backend defines `use_backend_key_mangler`
- # as True, which indicates the backend's key_mangler should be
- # the first choice.
- key_mangler = self._region.backend.key_mangler
-
- if CONF.kvs.enable_key_mangler:
- if key_mangler is not None:
- msg = _LI('Using %(func)s as KVS region %(name)s key_mangler')
- if callable(key_mangler):
- self._region.key_mangler = key_mangler
- LOG.info(msg, {'func': key_mangler.__name__,
- 'name': self._region.name})
- else:
- # NOTE(morganfainberg): We failed to set the key_mangler,
- # we should error out here to ensure we aren't causing
- # key-length or collision issues.
- raise exception.ValidationError(
- _('`key_mangler` option must be a function reference'))
- else:
- msg = _LI('Using default keystone.common.kvs.sha1_mangle_key '
- 'as KVS region %s key_mangler')
- LOG.info(msg, self._region.name)
- # NOTE(morganfainberg): Use 'default' keymangler to ensure
- # that unless explicitly changed, we mangle keys. This helps
- # to limit unintended cases of exceeding cache-key in backends
- # such as memcache.
- self._region.key_mangler = sha1_mangle_key
- self._set_keymangler_on_backend(self._region.key_mangler)
- else:
- LOG.info(_LI('KVS region %s key_mangler disabled.'),
- self._region.name)
- self._set_keymangler_on_backend(None)
-
- def _configure_region(self, backend, **config_args):
- prefix = CONF.kvs.config_prefix
- conf_dict = {}
- conf_dict['%s.backend' % prefix] = backend
-
- if 'distributed_lock' not in config_args:
- config_args['distributed_lock'] = True
-
- config_args['lock_timeout'] = self._lock_timeout
-
- # NOTE(morganfainberg): To mitigate race conditions on comparing
- # the timeout and current time on the lock mutex, we are building
- # in a static 1 second overlap where the lock will still be valid
- # in the backend but not from the perspective of the context
- # manager. Since we must develop to the lowest-common-denominator
- # when it comes to the backends, memcache's cache store is not more
- # refined than 1 second, therefore we must build in at least a 1
- # second overlap. `lock_timeout` of 0 means locks never expire.
- if config_args['lock_timeout'] > 0:
- config_args['lock_timeout'] += LOCK_WINDOW
-
- for argument, value in config_args.items():
- arg_key = '.'.join([prefix, 'arguments', argument])
- conf_dict[arg_key] = value
-
- LOG.debug('KVS region configuration for %(name)s: %(config)r',
- {'name': self._region.name, 'config': conf_dict})
- self._region.configure_from_config(conf_dict, '%s.' % prefix)
-
- def _mutex(self, key):
- return self._lock_registry.get(key)
-
- def _create_mutex(self, key):
- mutex = self._region.backend.get_mutex(key)
- if mutex is not None:
- return mutex
- else:
- return self._LockWrapper(lock_timeout=self._lock_timeout)
-
- class _LockWrapper(object):
- """weakref-capable threading.Lock wrapper."""
-
- def __init__(self, lock_timeout):
- self.lock = threading.Lock()
- self.lock_timeout = lock_timeout
-
- def acquire(self, wait=True):
- return self.lock.acquire(wait)
-
- def release(self):
- self.lock.release()
-
- def get(self, key):
- """Get a single value from the KVS backend."""
- self._assert_configured()
- value = self._region.get(key)
- if value is NO_VALUE:
- raise exception.NotFound(target=key)
- return value
-
- def get_multi(self, keys):
- """Get multiple values in a single call from the KVS backend."""
- self._assert_configured()
- values = self._region.get_multi(keys)
- not_found = []
- for index, key in enumerate(keys):
- if values[index] is NO_VALUE:
- not_found.append(key)
- if not_found:
- # NOTE(morganfainberg): If any of the multi-get values are non-
- # existent, we should raise a NotFound error to mimic the .get()
- # method's behavior. In all cases the internal dogpile NO_VALUE
- # should be masked from the consumer of the KeyValueStore.
- raise exception.NotFound(target=not_found)
- return values
-
- def set(self, key, value, lock=None):
- """Set a single value in the KVS backend."""
- self._assert_configured()
- with self._action_with_lock(key, lock):
- self._region.set(key, value)
-
- def set_multi(self, mapping):
- """Set multiple key/value pairs in the KVS backend at once.
-
- Like delete_multi, this call does not serialize through the
- KeyValueStoreLock mechanism (locking cannot occur on more than one
- key in a given context without significant deadlock potential).
- """
- self._assert_configured()
- self._region.set_multi(mapping)
-
- def delete(self, key, lock=None):
- """Delete a single key from the KVS backend.
-
- This method will raise NotFound if the key doesn't exist. The get and
- delete are done in a single transaction (via KeyValueStoreLock
- mechanism).
- """
- self._assert_configured()
-
- with self._action_with_lock(key, lock):
- self.get(key)
- self._region.delete(key)
-
- def delete_multi(self, keys):
- """Delete multiple keys from the KVS backend in a single call.
-
- Like set_multi, this call does not serialize through the
- KeyValueStoreLock mechanism (locking cannot occur on more than one
- key in a given context without significant deadlock potential).
- """
- self._assert_configured()
- self._region.delete_multi(keys)
-
- def get_lock(self, key):
- """Get a write lock on the KVS value referenced by `key`.
-
- The ability to get a context manager to pass into the set/delete
- methods allows for a single-transaction to occur while guaranteeing the
- backing store will not change between the start of the 'lock' and the
- end. Lock timeout is fixed to the KeyValueStore configured lock
- timeout.
- """
- self._assert_configured()
- return KeyValueStoreLock(self._mutex(key), key, self.locking,
- self._lock_timeout)
-
- @contextlib.contextmanager
- def _action_with_lock(self, key, lock=None):
- """Wrapper context manager.
-
- Validates and handles the lock and lock timeout if passed in.
- """
- if not isinstance(lock, KeyValueStoreLock):
- # NOTE(morganfainberg): Locking only matters if a lock is passed in
- # to this method. If lock isn't a KeyValueStoreLock, treat this as
- # if no locking needs to occur.
- yield
- else:
- if not lock.key == key:
- raise ValueError(_('Lock key must match target key: %(lock)s '
- '!= %(target)s') %
- {'lock': lock.key, 'target': key})
- if not lock.active:
- raise exception.ValidationError(_('Must be called within an '
- 'active lock context.'))
- if not lock.expired:
- yield
- else:
- raise LockTimeout(target=key)
-
-
-class KeyValueStoreLock(object):
- """Basic KeyValueStoreLock context manager.
-
- Hooks into the dogpile.cache backend mutex allowing for distributed locking
- on resources. This is only a write lock, and will not prevent reads from
- occurring.
- """
-
- def __init__(self, mutex, key, locking_enabled=True, lock_timeout=0):
- self.mutex = mutex
- self.key = key
- self.enabled = locking_enabled
- self.lock_timeout = lock_timeout
- self.active = False
- self.acquire_time = 0
-
- def acquire(self):
- if self.enabled:
- self.mutex.acquire()
- LOG.debug('KVS lock acquired for: %s', self.key)
- self.active = True
- self.acquire_time = time.time()
- return self
-
- __enter__ = acquire
-
- @property
- def expired(self):
- if self.lock_timeout:
- calculated = time.time() - self.acquire_time + LOCK_WINDOW
- return calculated > self.lock_timeout
- else:
- return False
-
- def release(self):
- if self.enabled:
- self.mutex.release()
- if not self.expired:
- LOG.debug('KVS lock released for: %s', self.key)
- else:
- LOG.warning(_LW('KVS lock released (timeout reached) for: %s'),
- self.key)
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- self.release()
-
-
-def get_key_value_store(name, kvs_region=None):
- """Retrieve key value store.
-
- Instantiate a new :class:`.KeyValueStore` or return a previous
- instantiation that has the same name.
- """
- global KEY_VALUE_STORE_REGISTRY
-
- _register_backends()
- key_value_store = KEY_VALUE_STORE_REGISTRY.get(name)
- if key_value_store is None:
- if kvs_region is None:
- kvs_region = region.make_region(name=name)
- key_value_store = KeyValueStore(kvs_region)
- KEY_VALUE_STORE_REGISTRY[name] = key_value_store
- return key_value_store
diff --git a/keystone-moon/keystone/common/kvs/legacy.py b/keystone-moon/keystone/common/kvs/legacy.py
deleted file mode 100644
index 7e27d97f..00000000
--- a/keystone-moon/keystone/common/kvs/legacy.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright 2012 OpenStack Foundation
-#
-# 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.
-
-from oslo_log import versionutils
-
-from keystone import exception
-
-
-class DictKvs(dict):
- def get(self, key, default=None):
- try:
- if isinstance(self[key], dict):
- return self[key].copy()
- else:
- return self[key][:]
- except KeyError:
- if default is not None:
- return default
- raise exception.NotFound(target=key)
-
- def set(self, key, value):
- if isinstance(value, dict):
- self[key] = value.copy()
- else:
- self[key] = value[:]
-
- def delete(self, key):
- """Deletes an item, returning True on success, False otherwise."""
- try:
- del self[key]
- except KeyError:
- raise exception.NotFound(target=key)
-
-
-INMEMDB = DictKvs()
-
-
-class Base(object):
- @versionutils.deprecated(versionutils.deprecated.ICEHOUSE,
- in_favor_of='keystone.common.kvs.KeyValueStore',
- remove_in=+2,
- what='keystone.common.kvs.Base')
- def __init__(self, db=None):
- if db is None:
- db = INMEMDB
- elif isinstance(db, DictKvs):
- db = db
- elif isinstance(db, dict):
- db = DictKvs(db)
- self.db = db