/usr/lib/python2.7/dist-packages/Bcfg2/Server/Cache.py is in bcfg2-server 1.4.0~pre2+git141-g6d40dace6358-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 175 176 177 178 179 180 | """ ``Bcfg2.Server.Cache`` is an implementation of a simple
memory-backed cache. Right now this doesn't provide many features, but
more (time-based expiration, etc.) can be added as necessary.
The normal workflow is to get a Cache object, which is simply a dict
interface to the unified cache that automatically uses a certain tag
set. For instance:
.. code-block:: python
groupcache = Bcfg2.Server.Cache.Cache("Probes", "probegroups")
groupcache['foo.example.com'] = ['group1', 'group2']
This would create a Cache object that automatically tags its entries
with ``frozenset(["Probes", "probegroups"])``, and store the list
``['group1', 'group1']`` with the *additional* tag
``foo.example.com``. So the unified backend cache would then contain
a single entry:
.. code-block:: python
{frozenset(["Probes", "probegroups", "foo.example.com"]):
['group1', 'group2']}
In addition to the dict interface, Cache objects (returned from
:func:`Bcfg2.Server.Cache.Cache`) have one additional method,
``expire()``, which is mostly identical to
:func:`Bcfg2.Server.Cache.expire`, except that it is specific to the
tag set of the cache object. E.g., to expire all ``foo.example.com``
records for a given cache, you could do:
.. code-block:: python
groupcache = Bcfg2.Server.Cache.Cache("Probes", "probegroups")
groupcache.expire("foo.example.com")
This is mostly functionally identical to:
.. code-block:: python
Bcfg2.Server.Cache.expire("Probes", "probegroups", "foo.example.com")
It's not completely identical, though; the first example will expire,
at most, exactly one item from the cache. The second example will
expire all items that are tagged with a superset of the given tags.
To illustrate the difference, consider the following two examples:
.. code-block:: python
groupcache = Bcfg2.Server.Cache.Cache("Probes")
groupcache.expire("probegroups")
Bcfg2.Server.Cache.expire("Probes", "probegroups")
The former will not expire any data, because there is no single datum
tagged with ``"Probes", "probegroups"``. The latter will expire *all*
items tagged with ``"Probes", "probegroups"`` -- i.e., the entire
cache. In this case, the latter call is equivalent to:
.. code-block:: python
groupcache = Bcfg2.Server.Cache.Cache("Probes", "probegroups")
groupcache.expire()
"""
from Bcfg2.Compat import MutableMapping
class _Cache(MutableMapping):
""" The object returned by :func:`Bcfg2.Server.Cache.Cache` that
presents a dict-like interface to the portion of the unified cache
that uses the specified tags. """
def __init__(self, registry, tags):
self._registry = registry
self._tags = tags
def __getitem__(self, key):
return self._registry[self._tags | set([key])]
def __setitem__(self, key, value):
self._registry[self._tags | set([key])] = value
def __delitem__(self, key):
del self._registry[self._tags | set([key])]
def __iter__(self):
for item in self._registry.iterate(*self._tags):
yield list(item.difference(self._tags))[0]
def keys(self):
""" List cache keys """
return list(iter(self))
def __len__(self):
return len(list(iter(self)))
def expire(self, key=None):
""" expire all items, or a specific item, from the cache """
if key is None:
expire(*self._tags)
else:
tags = self._tags | set([key])
# py 2.5 doesn't support mixing *args and explicit keyword
# args
kwargs = dict(exact=True)
expire(*tags, **kwargs)
def __repr__(self):
return repr(dict(self))
def __str__(self):
return str(dict(self))
class _CacheRegistry(dict):
""" The grand unified cache backend which contains all cache
items. """
def iterate(self, *tags):
""" Iterate over all items that match the given tags *and*
have exactly one additional tag. This is used to get items
for :class:`Bcfg2.Server.Cache._Cache` objects that have been
instantiated via :func:`Bcfg2.Server.Cache.Cache`. """
tags = frozenset(tags)
for key in self.keys():
if key.issuperset(tags) and len(key.difference(tags)) == 1:
yield key
def iter_all(self, *tags):
""" Iterate over all items that match the given tags,
regardless of how many additional tags they have (or don't
have). This is used to expire all cache data that matches a
set of tags. """
tags = frozenset(tags)
for key in list(self.keys()):
if key.issuperset(tags):
yield key
_cache = _CacheRegistry() # pylint: disable=C0103
_hooks = [] # pylint: disable=C0103
def Cache(*tags): # pylint: disable=C0103
""" A dict interface to the cache data tagged with the given
tags. """
return _Cache(_cache, frozenset(tags))
def expire(*tags, **kwargs):
""" Expire all items, a set of items, or one specific item from
the cache. If ``exact`` is set to True, then if the given tag set
doesn't match exactly one item in the cache, nothing will be
expired. """
exact = kwargs.pop("exact", False)
count = 0
if not tags:
count = len(_cache)
_cache.clear()
elif exact:
if frozenset(tags) in _cache:
count = 1
del _cache[frozenset(tags)]
else:
for match in _cache.iter_all(*tags):
count += 1
del _cache[match]
for hook in _hooks:
hook(tags, exact, count)
def add_expire_hook(func):
""" Add a hook that will be called when an item is expired from
the cache. The callable passed in must take three options: the
first will be the tag set that was expired; the second will be the
state of the ``exact`` flag (True or False); and the third will be
the number of items that were expired from the cache. """
_hooks.append(func)
|