Merge "Support SASL protocol for memcached"
This commit is contained in:
62
oslo_cache/_bmemcache_pool.py
Normal file
62
oslo_cache/_bmemcache_pool.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# Copyright 2022 Inspur
|
||||||
|
# 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
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Thread-safe connection pool for python-binary-memcached."""
|
||||||
|
try:
|
||||||
|
import eventlet
|
||||||
|
except ImportError:
|
||||||
|
eventlet = None
|
||||||
|
import bmemcached
|
||||||
|
from oslo_cache._memcache_pool import MemcacheClientPool
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class _BMemcacheClient(bmemcached.Client):
|
||||||
|
"""Thread global memcache client
|
||||||
|
|
||||||
|
As client is inherited from threading.local we have to restore object
|
||||||
|
methods overloaded by threading.local so we can reuse clients in
|
||||||
|
different threads
|
||||||
|
"""
|
||||||
|
__delattr__ = object.__delattr__
|
||||||
|
__getattribute__ = object.__getattribute__
|
||||||
|
__setattr__ = object.__setattr__
|
||||||
|
|
||||||
|
# Hack for lp 1812935
|
||||||
|
if eventlet and eventlet.patcher.is_monkey_patched('thread'):
|
||||||
|
# NOTE(bnemec): I'm not entirely sure why this works in a
|
||||||
|
# monkey-patched environment and not with vanilla stdlib, but it does.
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
return object.__new__(cls)
|
||||||
|
else:
|
||||||
|
__new__ = object.__new__
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BMemcacheClientPool(MemcacheClientPool):
|
||||||
|
def __init__(self, urls, arguments, **kwargs):
|
||||||
|
MemcacheClientPool.__init__(self, urls, arguments, **kwargs)
|
||||||
|
self._arguments = {
|
||||||
|
'username': arguments.get('username', None),
|
||||||
|
'password': arguments.get('password', None),
|
||||||
|
'tls_context': arguments.get('tls_context', None),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _create_connection(self):
|
||||||
|
return _BMemcacheClient(self.urls, **self._arguments)
|
||||||
@@ -204,7 +204,14 @@ class MemcacheClientPool(ConnectionPool):
|
|||||||
# old-style class
|
# old-style class
|
||||||
ConnectionPool.__init__(self, **kwargs)
|
ConnectionPool.__init__(self, **kwargs)
|
||||||
self.urls = urls
|
self.urls = urls
|
||||||
self._arguments = arguments
|
self._arguments = {
|
||||||
|
'dead_retry': arguments.get('dead_retry', 5 * 60),
|
||||||
|
'socket_timeout': arguments.get('socket_timeout', 3.0),
|
||||||
|
'server_max_value_length':
|
||||||
|
arguments.get('server_max_value_length'),
|
||||||
|
'flush_on_reconnect': arguments.get(
|
||||||
|
'pool_flush_on_reconnect', False),
|
||||||
|
}
|
||||||
# NOTE(morganfainberg): The host objects expect an int for the
|
# NOTE(morganfainberg): The host objects expect an int for the
|
||||||
# deaduntil value. Initialize this at 0 for each host with 0 indicating
|
# deaduntil value. Initialize this at 0 for each host with 0 indicating
|
||||||
# the host is not dead.
|
# the host is not dead.
|
||||||
|
|||||||
@@ -117,6 +117,16 @@ FILE_OPTIONS = {
|
|||||||
help='Global toggle if memcache will be flushed'
|
help='Global toggle if memcache will be flushed'
|
||||||
' on reconnect.'
|
' on reconnect.'
|
||||||
' (oslo_cache.memcache_pool backend only).'),
|
' (oslo_cache.memcache_pool backend only).'),
|
||||||
|
cfg.BoolOpt('memcache_sasl_enabled',
|
||||||
|
default=False,
|
||||||
|
help='Enable the SASL(Simple Authentication and Security'
|
||||||
|
'Layer) if the SASL_enable is true, else disable.'),
|
||||||
|
cfg.StrOpt('memcache_username',
|
||||||
|
default='',
|
||||||
|
help='the user name for the memcached which SASL enabled'),
|
||||||
|
cfg.StrOpt('memcache_password',
|
||||||
|
default='',
|
||||||
|
help='the password for the memcached which SASL enabled'),
|
||||||
cfg.BoolOpt('tls_enabled',
|
cfg.BoolOpt('tls_enabled',
|
||||||
default=False,
|
default=False,
|
||||||
help='Global toggle for TLS usage when comunicating with'
|
help='Global toggle for TLS usage when comunicating with'
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import functools
|
|||||||
|
|
||||||
from dogpile.cache.backends import memcached as memcached_backend
|
from dogpile.cache.backends import memcached as memcached_backend
|
||||||
|
|
||||||
|
from oslo_cache import _bmemcache_pool
|
||||||
from oslo_cache import _memcache_pool
|
from oslo_cache import _memcache_pool
|
||||||
|
|
||||||
|
|
||||||
@@ -55,20 +56,24 @@ class PooledMemcachedBackend(memcached_backend.MemcachedBackend):
|
|||||||
# Composed from GenericMemcachedBackend's and MemcacheArgs's __init__
|
# Composed from GenericMemcachedBackend's and MemcacheArgs's __init__
|
||||||
def __init__(self, arguments):
|
def __init__(self, arguments):
|
||||||
super(PooledMemcachedBackend, self).__init__(arguments)
|
super(PooledMemcachedBackend, self).__init__(arguments)
|
||||||
self.client_pool = _memcache_pool.MemcacheClientPool(
|
if arguments.get('sasl_enabled', False):
|
||||||
self.url,
|
self.client_pool = _bmemcache_pool.BMemcacheClientPool(
|
||||||
arguments={
|
self.url,
|
||||||
'dead_retry': arguments.get('dead_retry', 5 * 60),
|
arguments,
|
||||||
'socket_timeout': arguments.get('socket_timeout', 3.0),
|
maxsize=arguments.get('pool_maxsize', 10),
|
||||||
'server_max_value_length':
|
unused_timeout=arguments.get('pool_unused_timeout', 60),
|
||||||
arguments.get('server_max_value_length'),
|
conn_get_timeout=arguments.get('pool_connection_get_timeout',
|
||||||
'flush_on_reconnect': arguments.get('pool_flush_on_reconnect',
|
10),
|
||||||
False),
|
)
|
||||||
},
|
else:
|
||||||
maxsize=arguments.get('pool_maxsize', 10),
|
self.client_pool = _memcache_pool.MemcacheClientPool(
|
||||||
unused_timeout=arguments.get('pool_unused_timeout', 60),
|
self.url,
|
||||||
conn_get_timeout=arguments.get('pool_connection_get_timeout', 10),
|
arguments,
|
||||||
)
|
maxsize=arguments.get('pool_maxsize', 10),
|
||||||
|
unused_timeout=arguments.get('pool_unused_timeout', 60),
|
||||||
|
conn_get_timeout=arguments.get('pool_connection_get_timeout',
|
||||||
|
10),
|
||||||
|
)
|
||||||
|
|
||||||
# Since all methods in backend just call one of methods of client, this
|
# Since all methods in backend just call one of methods of client, this
|
||||||
# lets us avoid need to hack it too much
|
# lets us avoid need to hack it too much
|
||||||
|
|||||||
@@ -163,7 +163,8 @@ def _build_cache_config(conf):
|
|||||||
conf.cache.memcache_servers)
|
conf.cache.memcache_servers)
|
||||||
for arg in ('dead_retry', 'socket_timeout', 'pool_maxsize',
|
for arg in ('dead_retry', 'socket_timeout', 'pool_maxsize',
|
||||||
'pool_unused_timeout', 'pool_connection_get_timeout',
|
'pool_unused_timeout', 'pool_connection_get_timeout',
|
||||||
'pool_flush_on_reconnect'):
|
'pool_flush_on_reconnect', 'sasl_enabled', 'username',
|
||||||
|
'password'):
|
||||||
value = getattr(conf.cache, 'memcache_' + arg)
|
value = getattr(conf.cache, 'memcache_' + arg)
|
||||||
conf_dict['%s.arguments.%s' % (prefix, arg)] = value
|
conf_dict['%s.arguments.%s' % (prefix, arg)] = value
|
||||||
|
|
||||||
|
|||||||
@@ -30,3 +30,20 @@ class TestMemcachePoolCacheBackend(test_base.BaseTestCaseCacheBackend):
|
|||||||
# config fixture is properly initialized with value related to
|
# config fixture is properly initialized with value related to
|
||||||
# the current backend in use.
|
# the current backend in use.
|
||||||
super(TestMemcachePoolCacheBackend, self).setUp()
|
super(TestMemcachePoolCacheBackend, self).setUp()
|
||||||
|
|
||||||
|
|
||||||
|
class TestBMemcachePoolCacheBackend(test_base.BaseTestCaseCacheBackend):
|
||||||
|
def setUp(self):
|
||||||
|
MEMCACHED_PORT = os.getenv("OSLO_CACHE_TEST_MEMCACHED_PORT", "11211")
|
||||||
|
# If the cache support the sasl, the memcache_sasl_enabled
|
||||||
|
# should be True.
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='cache',
|
||||||
|
backend='oslo_cache.memcache_pool',
|
||||||
|
enabled=True,
|
||||||
|
memcache_servers=[f'localhost:{MEMCACHED_PORT}'],
|
||||||
|
memcache_sasl_enabled=False,
|
||||||
|
memcache_username='sasl_name',
|
||||||
|
memcache_password='sasl_pswd'
|
||||||
|
)
|
||||||
|
super(TestBMemcachePoolCacheBackend, self).setUp()
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from unittest import mock
|
|||||||
import testtools
|
import testtools
|
||||||
from testtools import matchers
|
from testtools import matchers
|
||||||
|
|
||||||
|
from oslo_cache import _bmemcache_pool
|
||||||
from oslo_cache import _memcache_pool
|
from oslo_cache import _memcache_pool
|
||||||
from oslo_cache import exception
|
from oslo_cache import exception
|
||||||
from oslo_cache.tests import test_cache
|
from oslo_cache.tests import test_cache
|
||||||
@@ -161,3 +162,13 @@ class TestMemcacheClientOverrides(test_cache.BaseTestCase):
|
|||||||
self.assertFalse(client.do_check_key)
|
self.assertFalse(client.do_check_key)
|
||||||
# Make sure our __new__ override still results in the right type
|
# Make sure our __new__ override still results in the right type
|
||||||
self.assertIsInstance(client, _memcache_pool._MemcacheClient)
|
self.assertIsInstance(client, _memcache_pool._MemcacheClient)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBMemcacheClient(test_cache.BaseTestCase):
|
||||||
|
|
||||||
|
def test_can_create_with_kwargs(self):
|
||||||
|
client = _bmemcache_pool._BMemcacheClient('foo', password='123456')
|
||||||
|
# Make sure kwargs are properly processed by the client
|
||||||
|
self.assertEqual('123456', client.password)
|
||||||
|
# Make sure our __new__ override still results in the right type
|
||||||
|
self.assertIsInstance(client, _bmemcache_pool._BMemcacheClient)
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add the feature to support SASL for olso.cache to improve the security
|
||||||
|
of authority.
|
||||||
Reference in New Issue
Block a user