oslo.memcache如何支持SASL详解

backend = oslo_cache.memcache_pool
the backend is <oslo_cache.backends.memcache_pool.PooledMemcachedBackend object at 0x7f46bb3b2d68>
		

前面可以看到,如果.conf文件中的cache 组中的backend = oslo_cache.memcache_pool

那么得到的backend就是oslo_cache.backends.memcache_pool.PooledMemcachedBackend

然后,我又在代码中将backend.set这个方法打印出来:

the backend.set is 
<bound method GenericMemcachedBackend.set of 
<oslo_cache.backends.memcache_pool.PooledMemcachedBackend object at 0x7fad77e497b8>>

可以看到backend.set是GenericMemcachedBackend.set中的方法,在dogpile/cache/backends/memcached.py文件中定义的

GenericMemcachedBackend.set中调用的是self.client.set(key, value, **self.set_arguments),而self.client又是一个属性method

oslo_cache.backends.memcache_pool.PooledMemcachedBackend在oslo_cache/backends/memcache_pool.py中定义的,继承的是GenericMemcachedBackend,所有可以调用其中的set方法。这个类又对client属性方法进行二次重载

the backend.client is 
<oslo_cache.backends.memcache_pool.ClientProxy object at 0x7fce5f48a710>
backend.client返回的是ClientProxy类对象

在ClientProxy类中,传入的是oslo_cache._memcache_pool.MemcacheClientPool参数

class ClientProxy(object):
    def __init__(self, client_pool):
        self.client_pool = client_pool

    def _run_method(self, __name, *args, **kwargs):
        with self.client_pool.acquire() as client:
            return getattr(client, __name)(*args, **kwargs)

    def __getattr__(self, name):
        return functools.partial(self._run_method, name)

这个函数暂时没有看懂,应该在调用ClientProxy(self.client_pool)的时候,直接调用__getattr__(self, name)函数吧,然后把client_pool中的conn对象直接返回了,这个时候,PooledMemcachedBackend.client就直接是client_pool中的conn的了,就可以直接调用其中的set、get等方法了!

现在的应用场景是想打开memcached服务的SALA安全认证,前面已经说了如何开启这个SALA安全认证的!

现在的想法是:

由于oslo_cache/_memcache_pool.py文件中的_MemcacheClient类继承的是memcache类
想着能不能创建一个_bmemcacge_pool.py文件,在这个文件中建立一个_bMemcacheClient类,让这个类继承bmemcache类
然后在memcache_pool.py文件中,用conf.sala_enable来判断调用哪个类!

现在在centos环境下进行验证:

把原来的demo代码copy到centos环境下进行验证,由于该环境没有安装openstack,因此并没有对应的库,需要执行:

pip install oslo_cache

执行:pip install python-memcached

安装memcache

执行demo之前,需要确认一下memcached服务是否已经开启了

netstat -an |grep 11211

如果没有开启的话,则执行命令

cd /opt/system/portaluser/memcached/bin
bash start

然后执行 python app.py

执行结果如下:

[jorh@localhost cache]$ python app.py 
the caching is True and the cache_time is 7200
_get_default_cache_region
<dogpile.cache.region.CacheRegion object at 0x7fd9f951fd50>
_CACHE is <cacheutils.CacheClient instance at 0x7fda0790bc20>
value is none!
Traceback (most recent call last):
  File "app.py", line 80, in <module>
    value = id_to_glance_id(context,image_id)
  File "app.py", line 26, in memorizer
    val = _CACHE.set(key,value)
  File "/home/jorh/cache/cacheutils.py", line 78, in set
    return self.region.set(key,value)
  File "/home/jorh/.local/lib/python2.7/site-packages/dogpile/cache/region.py", line 1112, in set
    self.backend.set(key, self._value(value))
  File "/home/jorh/.local/lib/python2.7/site-packages/dogpile/cache/backends/memcached.py", line 179, in set
    self.client.set(key, value, **self.set_arguments)
  File "/home/jorh/.local/lib/python2.7/site-packages/oslo_cache/backends/memcache_pool.py", line 32, in _run_method
    return getattr(client, __name)(*args, **kwargs)
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 727, in set
    return self._set("set", key, val, time, min_compress_len, noreply)
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1016, in _set
    server, key = self._get_server(key)
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 433, in _get_server
    if server.connect():
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1381, in connect
    if self._get_socket():
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1413, in _get_socket
    self.flush()
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1488, in flush
    self.expect(b'OK')
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1463, in expect
    line = self.readline(raise_exception)
  File "/home/jorh/.local/lib/python2.7/site-packages/memcache.py", line 1449, in readline
    data = recv(4096)
socket.timeout: timed out

可以看到上面没有读出相应的值的,并且出现错误!

如果关闭SASL之后,再执行代码会出现什么情况呢!

首先说明一下,如何关闭memcached的SALA认证,将start文件sudo ./memcached -d -f 1.1 -L -S -m 2048 -u os-user中的-S去掉,然后再将memcached这个服务重启一下就行了!有可能不会成功,这个时候,可能需要重启一下电脑才行!

执行python app.py命令,发现可以正常读出数据

[jorh@localhost cache]$ python app.py 
the caching is True and the cache_time is 7200
_get_default_cache_region
<dogpile.cache.region.CacheRegion object at 0x7fc649207d50>
_CACHE is <cacheutils.CacheClient instance at 0x7fc6575f3c20>
value is none!
the set return value is None
the value is this is image_id:1234
_get_default_cache_region
<dogpile.cache.region.CacheRegion object at 0x7fc64719ca10>
cl.add return value: val = 90
key = mykey
VAL1 is 90
VAL2 is jihhhhsnna
_get_default_cache_region
<dogpile.cache.region.CacheRegion object at 0x7fc649207dd0>
cl1 VAL3 is jihhhhsnna
_get_default_cache_region
<dogpile.cache.region.CacheRegion object at 0x7fc6471a6250>
key = mykey
value2 is jihhhhsnna

走到这里,就非常高兴了,说明可以正常开启和关闭带有SALA认证的memcached服务

下面,我们将修改代码,让oslo_cache模块支持开启SALA认证的memcached服务!

首先修改cache.conf文件,里面增加如下的三个参数

memcache_sasl_enable = true
memcache_usrname = username
memcache_password = Password

之后,通过位于oslo_cache/core.py文件中的configure(conf)函数,将conf中的参数进行配置:

在configure函数中添加如下的内容(下面内容在oslo_cache/_opts.py文件中):

FILE_OPTIONS = {
    'cache': [
        ...........
        cfg.BoolOpt('memcache_sasl_enable',default=False,
                    help='Enable the SASL(Simple Authentication and Security Layer) if the sasl_enable is true, '
                         'else disable!'),
        cfg.StrOpt('memcache_usrname',default='usrname',
                   help='the user name for the SASL'),
        cfg.StrOpt('memcache_password',default='password',
                   help="the user's password for SASL!"),
    ],
}

之后,configure函数会将cache组中的所有配置项进行注册,然后就可以通过CONF.cache.SALA_enable, CONF.cache.os_user以及CONF.cache.passwd来调用相应的参数!

之后,会创建一个region对象,然后将这个CONF和region进行结合

region = cache.create_region()
cache.configure_cache_region(CONF,region)

下面我们看一下configure_cache_region(CONF,region)这个函数是如何修改(oslo_cache/core.py文件中):

在这个函数中主要调用两个函数:

config_dict = _build_cache_config(conf)
region.configure_from_config(config_dict,'%s.' % conf.cache.config_prefix)

第一个函数主要用于配置config_dict这个字典参数。

然后修改并添加如下的代码:

'''
在括号内,添加如下的三个关键字,'sala_enable', 'os_user', 'passwd'
'''    
for arg in ('dead_retry', 'socket_timeout', 'pool_maxsize',
                'pool_unused_timeout', 'pool_connection_get_timeout',
                'sasl_enable', 'usrname', 'password'):
        value = getattr(conf.cache, 'memcache_' + arg)
        conf_dict['%s.arguments.%s' % (prefix, arg)] = value

后面就会将这个conf_dict返回,然后我们的三个参数就在这个字典中了!

虽然现在看起来字典中的key = oslo.cache.arguments,不知道为什么一定要加上这个prefix

后面当这个字典传到backend类中的时候,会将字典中key中的这个前缀去掉(感觉上面多此一举!)

region.configure_from_config这个函数主要用来确定存储后端backend的,这里不用进行改动!

现在工作已经完成一半了,因为我们已经得到了我们想要的参数,并且将这个参数放到字典中,后面只要能得到这个字典,我们就可以得到我们需要的这三个参数!

因为我们这里设置的后端存储是:backend=oslo_cache.memcache_pool,上面也有打印相应的参数实际对应的类对象实例!

因此对应的方法是:oslo_cache.backends.memcache_pool.PooledMemcachedBackend

下面看一下PooledMemcachedBackend中是如何调用的,可以看如下的代码:

    from oslo_cache import _bmemcache_pool
    def __init__(self, arguments):
        #初始化父类中的__init__函数,后面可以直接调用父类中的属性
        super(PooledMemcachedBackend, self).__init__(arguments)
        if arguments.get()
        self.client_pool = _memcache_pool.MemcacheClientPool(
            self.url,#这个参数是在GenericMemcachedBackend中__init__的时候得到的!
            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'),
            },
            maxsize=arguments.get('pool_maxsize', 10),
            unused_timeout=arguments.get('pool_unused_timeout', 60),
            conn_get_timeout=arguments.get('pool_connection_get_timeout', 10),
        )

那么,现在我们只需要重新构造一个类,让他继承bmemcached就可以了,然后根据arguments中的sala_enable是否为True来判断执行那个flow!

下面修改方案:增加一个_bmemcache_pool.py文件,在里面导入bmemcached模块,之后所有的代码都和_memcache_pool.py中的一样,只不过其中的类前面都添加b字符,以示区分;

    def __init__(self, arguments):
        #初始化父类中的__init__函数,后面可以直接调用父类中的属性
        super(PooledMemcachedBackend, self).__init__(arguments)
        if arguments.get('sala_enable'):
            self.client_pool = _bmemcache_pool.bMemcacheClientPool(
                self.url,  # 这个参数是在GenericMemcachedBackend中__init__的时候得到的!
                username=arguments.get('os_name'),
                password=arguments.get('passwd'),
                arguments={
                    'socket_timeout': arguments.get('socket_timeout', 3.0),
                },
                maxsize=arguments.get('pool_maxsize', 10),
                unused_timeout=arguments.get('pool_unused_timeout', 60),
                conn_get_timeout=arguments.get('pool_connection_get_timeout', 10),
            )
        else:
            self.client_pool = _memcache_pool.MemcacheClientPool(
                self.url,#这个参数是在GenericMemcachedBackend中__init__的时候得到的!
                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'),
                },
                maxsize=arguments.get('pool_maxsize', 10),
                unused_timeout=arguments.get('pool_unused_timeout', 60),
                conn_get_timeout=arguments.get('pool_connection_get_timeout', 10),
            )

_bmemcache_pool.py文件的改动如下:

import bmemcached

.......

'''
class _MemcacheClient类改为class _bMemcacheClient,继承bmemcached.Client
'''
class _bMemcacheClient(bmemcached.Client): #
...........

............
#将class ConnectionPool(queue.Queue)改为class bConnectionPool(queue.Queue)
class bConnectionPool(queue.Queue):
...............

#将class MemcacheClientPool改为class bMemcacheClientPool,并继承bConnectionPool
class bMemcacheClientPool(bConnectionPool):
#在这个类中添加两个变量
    def __init__(self, urls,username,password, arguments, **kwargs):
        # super() cannot be used here because Queue in stdlib is an
        # old-style class
        ConnectionPool.__init__(self, **kwargs)
        self.urls = urls
        #添加如下两个变量!
        self.username = username
        self.password = password
        self._arguments = arguments
        # NOTE(morganfainberg): The host objects expect an int for the
        # deaduntil value. Initialize this at 0 for each host with 0 indicating
        # the host is not dead.
        self._hosts_deaduntil = [0] * len(urls)
    def _create_connection(self):   #这里返回bmemcached对象!
        return _bMemcacheClient(self.urls, self.username,self.password,
                               **self._arguments)
    def _get(self):
        # super() cannot be used here because Queue in stdlib is an
        # old-style class
        conn = bConnectionPool._get(self)
      ..........
            bConnectionPool._put(self, conn)
            raise
        return conn
   def _put(self, conn):
        try:
          ......
            bConnectionPool._put(self, conn)


这样整个代码就改动完成了,下面开始正式测试流程!

测试出现如下的错误:

status = 32
Traceback (most recent call last):
  File "app.py", line 85, in <module>
    myfun2('mykey')
  File "app.py", line 73, in myfun2
    cl2.set(key,'jihhhhsnna')
  File "/home/jorh/cache/cacheutils.py", line 82, in set
    return self.region.set(key,value)
  File "/home/jorh/.local/lib/python2.7/site-packages/dogpile/cache/region.py", line 1112, in set
    self.backend.set(key, self._value(value))
  File "/home/jorh/.local/lib/python2.7/site-packages/dogpile/cache/backends/memcached.py", line 179, in set
    self.client.set(key, value, **self.set_arguments)
  File "/home/jorh/.local/lib/python2.7/site-packages/oslo_cache/backends/memcache_pool.py", line 33, in _run_method
    print("getattr(client, __name)(*args, **kwargs) = %s"%(getattr(client, __name)(*args, **kwargs)))
  File "/usr/lib/python2.7/site-packages/bmemcached/client/replicating.py", line 112, in set
    returns.append(server.set(key, value, time, compress_level=compress_level))
  File "/usr/lib/python2.7/site-packages/bmemcached/protocol.py", line 612, in set
    return self._set_add_replace('set', key, value, time, compress_level=compress_level)
  File "/usr/lib/python2.7/site-packages/bmemcached/protocol.py", line 591, in _set_add_replace
    raise MemcachedException('Code: %d Message: %s' % (status, extra_content), status)
bmemcached.exceptions.MemcachedException

现在发现在关闭SALA的时候,执行是可以的,打开SALA之后,就出现上述的错误,现在的可能原因是username和password参数可能传入不对!

在_bmemcache_pool.py文件中,直接将__init__函数中的self._os-name和self._password设置为对应的值之后,发现读取正常了,那么出现上述现象的原因就是传入的认证参数不对!

添加代码链接:https://download.csdn.net/download/qq_33970713/12558596

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章