neutron/neutron/common/cache_utils.py
Vasyl Saienko 7ce6a5c740 Fix return correct cache when reusing port
The patch fixes issue when the same port is requested for multiple
instances and second one can't get metadata due to cached instance_id.

Closes-Bug: 1868867

Conflicts:
    neutron/common/cache_utils.py

Change-Id: If6a5866e4406c9c6c30e989c79ffb4ee1a88cecf
(cherry picked from commit 72a5b5b61f)
2020-04-08 12:08:13 +00:00

127 lines
4.4 KiB
Python

# 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 functools
from neutron_lib.utils import helpers
from oslo_cache import core as cache
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import reflection
from neutron._i18n import _
LOG = logging.getLogger(__name__)
def register_oslo_configs(conf):
cache.configure(conf)
def get_cache(conf):
"""Used to get cache client"""
if conf.cache.enabled:
return _get_cache_region(conf)
else:
return False
def _get_cache_region(conf):
region = cache.create_region()
cache.configure_cache_region(conf, region)
return region
def _get_memory_cache_region(expiration_time=5):
conf = cfg.ConfigOpts()
register_oslo_configs(conf)
cache_conf_dict = {
'enabled': True,
'backend': 'oslo_cache.dict',
'expiration_time': expiration_time,
}
for k, v in cache_conf_dict.items():
conf.set_override(k, v, group='cache')
return _get_cache_region(conf)
class cache_method_results(object):
"""This decorator is intended for object methods only."""
def __init__(self, func):
self.func = func
functools.update_wrapper(self, func)
self._first_call = True
self._not_cached = cache.NO_VALUE
def _get_from_cache(self, target_self, *args, **kwargs):
target_self_cls_name = reflection.get_class_name(target_self,
fully_qualified=False)
func_name = "%(module)s.%(class)s.%(func_name)s" % {
'module': target_self.__module__,
'class': target_self_cls_name,
'func_name': self.func.__name__,
}
skip_cache = kwargs.pop('skip_cache', False)
key = (func_name,) + args
if kwargs:
key += helpers.dict2tuple(kwargs)
# oslo.cache expects a string or a buffer
key = str(key)
if not skip_cache:
try:
item = target_self._cache.get(key)
except TypeError:
LOG.debug("Method %(func_name)s cannot be cached due to "
"unhashable parameters: args: %(args)s, kwargs: "
"%(kwargs)s",
{'func_name': func_name,
'args': args,
'kwargs': kwargs})
return self.func(target_self, *args, **kwargs)
else:
LOG.debug('Skipping getting result from cache for %s.', func_name)
item = self._not_cached
if item is self._not_cached:
item = self.func(target_self, *args, **kwargs)
target_self._cache.set(key, item)
return item
def __call__(self, target_self, *args, **kwargs):
target_self_cls_name = reflection.get_class_name(target_self,
fully_qualified=False)
if not hasattr(target_self, '_cache'):
raise NotImplementedError(
_("Instance of class %(module)s.%(class)s must contain _cache "
"attribute") % {
'module': target_self.__module__,
'class': target_self_cls_name})
if not target_self._cache:
if self._first_call:
LOG.debug("Instance of class %(module)s.%(class)s doesn't "
"contain attribute _cache therefore results "
"cannot be cached for %(func_name)s.",
{'module': target_self.__module__,
'class': target_self_cls_name,
'func_name': self.func.__name__})
self._first_call = False
return self.func(target_self, *args, **kwargs)
return self._get_from_cache(target_self, *args, **kwargs)
def __get__(self, obj, objtype):
return functools.partial(self.__call__, obj)