File: cache_utils.py

package info (click to toggle)
nova 2%3A13.1.0-2~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 31,120 kB
  • sloc: python: 301,406; sh: 1,298; xml: 1,184; makefile: 129; sql: 43
file content (174 lines) | stat: -rw-r--r-- 6,512 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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.

"""Super simple fake memcache client."""

import copy

from oslo_cache import core as cache
from oslo_config import cfg

from nova.i18n import _


# NOTE(dims): There are many copies of memcache_opts with memcached_servers
# in various projects as this used to be in a copy of memory_cache.py
# Since we are making a change in just our copy, oslo-config-generator fails
# with cfg.DuplicateOptError unless we override the comparison check
class _DeprecatedListOpt(cfg.ListOpt):
    def __ne__(self, another):
        self_dict = copy.deepcopy(vars(self))
        another_dict = copy.deepcopy(vars(another))
        self_dict.pop('help')
        self_dict.pop('deprecated_for_removal')
        another_dict.pop('help')
        another_dict.pop('deprecated_for_removal')
        return self_dict != another_dict


memcache_opts = [
    _DeprecatedListOpt('memcached_servers',
                       help='DEPRECATED: Memcached servers or None for in '
                            'process cache. "memcached_servers" opt is '
                            'deprecated in Mitaka. In Newton release '
                            'oslo.cache config options should be used as '
                            'this option will be removed. Please add a '
                            '[cache] group in your nova.conf file and '
                            'add "enable" and "memcache_servers" option in '
                            'this section.',
                       deprecated_for_removal=True),
]

CONF = cfg.CONF
CONF.register_opts(memcache_opts)

WEEK = 604800


def list_opts():
    """Entry point for oslo-config-generator."""
    return [(None, copy.deepcopy(memcache_opts))]


def get_memcached_client(expiration_time=0):
    """Used ONLY when memcached is explicitly needed."""
    # If the operator uses the old style [DEFAULT]/memcached_servers
    # then we just respect that setting
    if CONF.memcached_servers:
        return CacheClient(
                _get_custom_cache_region(expiration_time=expiration_time,
                                         backend='dogpile.cache.memcached',
                                         url=CONF.memcached_servers))
    # If the operator still uses the new style [cache]/memcache_servers
    # and has [cache]/enabled flag on then we let oslo_cache configure
    # the region from the configuration settings
    elif CONF.cache.enabled and CONF.cache.memcache_servers:
        return CacheClient(
                _get_default_cache_region(expiration_time=expiration_time))
    raise RuntimeError(_('memcached_servers not defined'))


def get_client(expiration_time=0):
    """Used to get a caching client."""
    # If the operator still uses the old style [DEFAULT]/memcached_servers
    # then we just respect that setting
    if CONF.memcached_servers:
        return CacheClient(
                _get_custom_cache_region(expiration_time=expiration_time,
                                         backend='dogpile.cache.memcached',
                                         url=CONF.memcached_servers))
    # If the operator has [cache]/enabled flag on then we let oslo_cache
    # configure the region from configuration settings.
    elif CONF.cache.enabled:
        return CacheClient(
                _get_default_cache_region(expiration_time=expiration_time))
    # If [cache]/enabled flag is off and [DEFAULT]/memcached_servers is
    # absent we use the dictionary backend
    return CacheClient(
            _get_custom_cache_region(expiration_time=expiration_time,
                                     backend='oslo_cache.dict'))


def _get_default_cache_region(expiration_time):
    region = cache.create_region()
    if expiration_time != 0:
        CONF.cache.expiration_time = expiration_time
    cache.configure_cache_region(CONF, region)
    return region


def _get_custom_cache_region(expiration_time=WEEK,
                             backend=None,
                             url=None):
    """Create instance of oslo_cache client.

    For backends you can pass specific parameters by kwargs.
    For 'dogpile.cache.memcached' backend 'url' parameter must be specified.

    :param backend: backend name
    :param expiration_time: interval in seconds to indicate maximum
        time-to-live value for each key
    :param url: memcached url(s)
    """

    region = cache.create_region()
    region_params = {}
    if expiration_time != 0:
        region_params['expiration_time'] = expiration_time

    if backend == 'oslo_cache.dict':
        region_params['arguments'] = {'expiration_time': expiration_time}
    elif backend == 'dogpile.cache.memcached':
        region_params['arguments'] = {'url': url}
    else:
        raise RuntimeError(_('old style configuration can use '
                             'only dictionary or memcached backends'))

    region.configure(backend, **region_params)
    return region


class CacheClient(object):
    """Replicates a tiny subset of memcached client interface."""

    def __init__(self, region):
        self.region = region

    def get(self, key):
        value = self.region.get(key)
        if value == cache.NO_VALUE:
            return None
        return value

    def get_or_create(self, key, creator):
        return self.region.get_or_create(key, creator)

    def set(self, key, value):
        return self.region.set(key, value)

    def add(self, key, value):
        return self.region.get_or_create(key, lambda: value)

    def delete(self, key):
        return self.region.delete(key)

    def get_multi(self, keys):
        values = self.region.get_multi(keys)
        return [None if value is cache.NO_VALUE else value for value in
                values]

    def delete_multi(self, keys):
        return self.region.delete_multi(keys)