diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/Bcfg2/Server/Cache.py | 178 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Core.py | 20 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/MultiprocessingCore.py | 46 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugin/helpers.py | 2 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugin/interfaces.py | 19 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Metadata.py | 18 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 62 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/SSHbase.py | 5 |
8 files changed, 209 insertions, 141 deletions
diff --git a/src/lib/Bcfg2/Server/Cache.py b/src/lib/Bcfg2/Server/Cache.py index 842098eda..18b668b55 100644 --- a/src/lib/Bcfg2/Server/Cache.py +++ b/src/lib/Bcfg2/Server/Cache.py @@ -1,14 +1,176 @@ -""" 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. """ +""" ``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: -class Cache(dict): - """ an implementation of a simple memory-backed cache """ +.. 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): + 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: - self.clear() - elif key in self: - del self[key] + expire(*self._tags) + else: + tags = self._tags | set([key]) + expire(*tags, exact=True) + + 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 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) diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py index 501a78bc0..b0b80e956 100644 --- a/src/lib/Bcfg2/Server/Core.py +++ b/src/lib/Bcfg2/Server/Core.py @@ -207,7 +207,7 @@ class Core(object): #: A :class:`Bcfg2.Server.Cache.Cache` object for caching client #: metadata - self.metadata_cache = Cache() + self.metadata_cache = Cache("Metadata") #: Whether or not it's possible to use the Django database #: backend for plugins that have that capability @@ -227,20 +227,6 @@ class Core(object): self.logger.error("Updating database %s failed: %s" % (Bcfg2.Options.setup.db_name, err)) - def expire_caches_by_type(self, base_cls, key=None): - """ Expire caches for all - :class:`Bcfg2.Server.Plugin.interfaces.Caching` plugins that - are instances of ``base_cls``. - - :param base_cls: The base plugin interface class to match (see - :mod:`Bcfg2.Server.Plugin.interfaces`) - :type base_cls: type - :param key: The cache key to expire - """ - for plugin in self.plugins_by_type(base_cls): - if isinstance(plugin, Bcfg2.Server.Plugin.Caching): - plugin.expire_cache(key) - def plugins_by_type(self, base_cls): """ Return a list of loaded plugins that match the passed type. @@ -683,7 +669,7 @@ class Core(object): if event.code2str() == 'deleted': return Bcfg2.Options.get_parser().reparse() - self.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata) + self.metadata_cache.expire() def block_for_fam_events(self, handle_events=False): """ Block until all fam events have been handleed, optionally @@ -1070,7 +1056,7 @@ class Core(object): # that's created for RecvProbeData doesn't get cached. # I.e., the next metadata object that's built, after probe # data is processed, is cached. - self.expire_caches_by_type(Bcfg2.Server.Plugin.Metadata) + self.metadata_cache.expire(client) try: xpdata = lxml.etree.XML(probedata.encode('utf-8'), parser=Bcfg2.Server.XMLParser) diff --git a/src/lib/Bcfg2/Server/MultiprocessingCore.py b/src/lib/Bcfg2/Server/MultiprocessingCore.py index 517140178..c42009bdd 100644 --- a/src/lib/Bcfg2/Server/MultiprocessingCore.py +++ b/src/lib/Bcfg2/Server/MultiprocessingCore.py @@ -16,32 +16,15 @@ import threading import lxml.etree import multiprocessing import Bcfg2.Options +import Bcfg2.Server.Cache import Bcfg2.Server.Plugin from itertools import cycle -from Bcfg2.Server.Cache import Cache from Bcfg2.Compat import Queue, Empty, wraps from Bcfg2.Server.Core import Core, exposed from Bcfg2.Server.BuiltinCore import BuiltinCore from multiprocessing.connection import Listener, Client -class DispatchingCache(Cache, Bcfg2.Server.Plugin.Debuggable): - """ Implementation of :class:`Bcfg2.Cache.Cache` that propagates - cache expiration events to child nodes. """ - - #: The method to send over the pipe to expire the cache - method = "expire_metadata_cache" - - def __init__(self, *args, **kwargs): - self.rpc_q = kwargs.pop("queue") - Bcfg2.Server.Plugin.Debuggable.__init__(self) - Cache.__init__(self, *args, **kwargs) - - def expire(self, key=None): - self.rpc_q.publish(self.method, args=[key]) - Cache.expire(self, key=key) - - class RPCQueue(Bcfg2.Server.Plugin.Debuggable): """ An implementation of a :class:`multiprocessing.Queue` designed for several additional use patterns: @@ -304,16 +287,9 @@ class ChildCore(Core): return rmi @exposed - def expire_metadata_cache(self, client=None): - """ Expire the metadata cache for a client """ - self.metadata_cache.expire(client) - - @exposed - def RecvProbeData(self, address, _): - """ Expire the probe cache for a client """ - self.expire_caches_by_type(Bcfg2.Server.Plugin.Probing, - key=self.resolve_client(address, - metadata=False)[0]) + def expire_cache(self, *tags, **kwargs): + """ Expire cached data """ + Bcfg2.Server.Cache.expire(*tags, exact=kwargs.pop("exact", False)) @exposed def GetConfig(self, client): @@ -368,8 +344,6 @@ class MultiprocessingCore(BuiltinCore): #: used to send or publish commands to children. self.rpc_q = RPCQueue() - self.metadata_cache = DispatchingCache(queue=self.rpc_q) - #: A list of children that will be cycled through self._all_children = [] @@ -392,6 +366,7 @@ class MultiprocessingCore(BuiltinCore): self.logger.debug("Started %s children: %s" % (len(self._all_children), self._all_children)) self.children = cycle(self._all_children) + Bcfg2.Server.Cache.add_expire_hook(self.cache_dispatch) return BuiltinCore._run(self) def shutdown(self): @@ -464,16 +439,11 @@ class MultiprocessingCore(BuiltinCore): def set_debug(self, address, debug): self.rpc_q.set_debug(debug) self.rpc_q.publish("set_debug", args=[address, debug]) - self.metadata_cache.set_debug(debug) return BuiltinCore.set_debug(self, address, debug) - @exposed - def RecvProbeData(self, address, probedata): - rv = BuiltinCore.RecvProbeData(self, address, probedata) - # we don't want the children to actually process probe data, - # so we don't send the data, just the fact that we got some. - self.rpc_q.publish("RecvProbeData", args=[address, None]) - return rv + def cache_dispatch(self, tags, exact, _): + """ Publish cache expiration events to child nodes. """ + self.rpc_q.publish("expire_cache", args=tags, kwargs=dict(exact=exact)) @exposed def GetConfig(self, address): diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index cfdb25cd6..38491b05d 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -961,6 +961,8 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked): self.Entries = {} for src in self.entries.values(): for child in src.xdata.iterchildren(): + if child.tag in ['Group', 'Client']: + continue if child.tag not in self.Entries: self.Entries[child.tag] = dict() self.Entries[child.tag][child.get("name")] = self.BindEntry diff --git a/src/lib/Bcfg2/Server/Plugin/interfaces.py b/src/lib/Bcfg2/Server/Plugin/interfaces.py index 30275f6ad..619d72afd 100644 --- a/src/lib/Bcfg2/Server/Plugin/interfaces.py +++ b/src/lib/Bcfg2/Server/Plugin/interfaces.py @@ -632,22 +632,3 @@ class ClientACLs(object): :returns: bool """ return True - - -class Caching(object): - """ A plugin that caches more than just the data received from the - FAM. This presents a unified interface to clear the cache. """ - - def expire_cache(self, key=None): - """ Expire the cache associated with the given key. - - :param key: The key to expire the cache for. Because cache - implementations vary tremendously between plugins, - this could be any number of things, but generally - a hostname. It also may or may not be possible to - expire the cache for a single host; this interface - does not require any guarantee about that. - :type key: varies - :returns: None - """ - raise NotImplementedError diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py index 3a470b8de..75cf6454c 100644 --- a/src/lib/Bcfg2/Server/Plugins/Metadata.py +++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py @@ -17,10 +17,10 @@ import Bcfg2.Options import Bcfg2.Server.Plugin import Bcfg2.Server.FileMonitor from Bcfg2.Utils import locked +from Bcfg2.Server.Cache import Cache from Bcfg2.Compat import MutableMapping, all, wraps # pylint: disable=W0622 from Bcfg2.version import Bcfg2VersionInfo - # pylint: disable=C0103 ClientVersions = None MetadataClientModel = None @@ -90,7 +90,7 @@ def load_django_models(): def keys(self): """ Get keys for the mapping """ - return [c.hostname for c in MetadataClientModel.objects.all()] + return list(iter(self)) def __contains__(self, key): try: @@ -513,7 +513,6 @@ class MetadataGroup(tuple): # pylint: disable=E0012,R0924 class Metadata(Bcfg2.Server.Plugin.Metadata, - Bcfg2.Server.Plugin.Caching, Bcfg2.Server.Plugin.ClientRunHooks, Bcfg2.Server.Plugin.DatabaseBacked): """This class contains data for bcfg2 server metadata.""" @@ -533,7 +532,6 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, def __init__(self, core, datastore, watch_clients=True): Bcfg2.Server.Plugin.Metadata.__init__(self) - Bcfg2.Server.Plugin.Caching.__init__(self) Bcfg2.Server.Plugin.ClientRunHooks.__init__(self) Bcfg2.Server.Plugin.DatabaseBacked.__init__(self, core, datastore) self.watch_clients = watch_clients @@ -578,6 +576,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, self.versions = dict() self.uuid = {} self.session_cache = {} + self.cache = Cache("Metadata") self.default = None self.pdirty = False self.password = Bcfg2.Options.setup.password @@ -977,16 +976,13 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, self.groups[gname] self.states['groups.xml'] = True - def expire_cache(self, key=None): - self.core.metadata_cache.expire(key) - def HandleEvent(self, event): """Handle update events for data files.""" for handles, event_handler in self.handlers.items(): if handles(event): # clear the entire cache when we get an event for any # metadata file - self.expire_cache() + self.cache.expire() event_handler(event) if False not in list(self.states.values()) and self.debug_flag: @@ -1147,8 +1143,8 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, raise Bcfg2.Server.Plugin.MetadataRuntimeError("Metadata has not " "been read yet") client = client.lower() - if client in self.core.metadata_cache: - return self.core.metadata_cache[client] + if client in self.cache: + return self.cache[client] if client in self.aliases: client = self.aliases[client] @@ -1243,7 +1239,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata, addresses, categories, uuid, password, version, self.query) if self.core.metadata_cache_mode == 'initial': - self.core.metadata_cache[client] = rv + self.cache[client] = rv return rv def get_all_group_names(self): diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index 5af9c1591..56285705a 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -8,6 +8,7 @@ import glob import shutil import lxml.etree import Bcfg2.Options +import Bcfg2.Server.Cache import Bcfg2.Server.Plugin from Bcfg2.Compat import urlopen, HTTPError, URLError, MutableMapping from Bcfg2.Server.Plugins.Packages.Collection import Collection, \ @@ -81,7 +82,6 @@ class OnDemandDict(MutableMapping): class Packages(Bcfg2.Server.Plugin.Plugin, - Bcfg2.Server.Plugin.Caching, Bcfg2.Server.Plugin.StructureValidator, Bcfg2.Server.Plugin.Generator, Bcfg2.Server.Plugin.Connector, @@ -136,12 +136,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, #: and :func:`Reload` __rmi__ = Bcfg2.Server.Plugin.Plugin.__rmi__ + ['Refresh', 'Reload'] - __child_rmi__ = Bcfg2.Server.Plugin.Plugin.__child_rmi__ + \ - [('Refresh', 'expire_cache'), ('Reload', 'expire_cache')] - def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) - Bcfg2.Server.Plugin.Caching.__init__(self) Bcfg2.Server.Plugin.StructureValidator.__init__(self) Bcfg2.Server.Plugin.Generator.__init__(self) Bcfg2.Server.Plugin.Connector.__init__(self) @@ -185,7 +181,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, #: :attr:`Bcfg2.Server.Plugins.Packages.Collection.Collection.cachekey`, #: a unique key identifying the collection by its *config*, #: which could be shared among multiple clients. - self.collections = dict() + self.collections = Bcfg2.Server.Cache.Cache("Packages", "collections") #: clients is a cache mapping of hostname -> #: :attr:`Bcfg2.Server.Plugins.Packages.Collection.Collection.cachekey` @@ -193,21 +189,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection` #: object when one is requested, so each entry is very #: short-lived -- it's purged at the end of each client run. - self.clients = dict() - - #: groupcache caches group lookups. It maps Collections (via - #: :attr:`Bcfg2.Server.Plugins.Packages.Collection.Collection.cachekey`) - #: to sets of package groups, and thence to the packages - #: indicated by those groups. - self.groupcache = dict() - - #: pkgcache caches complete package sets. It maps Collections - #: (via - #: :attr:`Bcfg2.Server.Plugins.Packages.Collection.Collection.cachekey`) - #: to sets of initial packages, and thence to the final - #: (complete) package selections resolved from the initial - #: packages - self.pkgcache = dict() + self.clients = Bcfg2.Server.Cache.Cache("Packages", "cache") + # pylint: enable=C0301 __init__.__doc__ = Bcfg2.Server.Plugin.Plugin.__init__.__doc__ @@ -400,11 +383,12 @@ class Packages(Bcfg2.Server.Plugin.Plugin, groups.sort() # check for this set of groups in the group cache + gcache = Bcfg2.Server.Cache.Cache("Packages", "pkg_groups", + collection.cachekey) gkey = hash(tuple(groups)) - if gkey not in self.groupcache[collection.cachekey]: - self.groupcache[collection.cachekey][gkey] = \ - collection.get_groups(groups) - for pkgs in self.groupcache[collection.cachekey][gkey].values(): + if gkey not in gcache: + gcache[gkey] = collection.get_groups(groups) + for pkgs in gcache[gkey].values(): base.update(pkgs) # essential pkgs are those marked as such by the distribution @@ -412,10 +396,11 @@ class Packages(Bcfg2.Server.Plugin.Plugin, # check for this set of packages in the package cache pkey = hash(tuple(base)) - if pkey not in self.pkgcache[collection.cachekey]: - self.pkgcache[collection.cachekey][pkey] = \ - collection.complete(base) - packages, unknown = self.pkgcache[collection.cachekey][pkey] + pcache = Bcfg2.Server.Cache.Cache("Packages", "pkg_sets", + collection.cachekey) + if pkey not in pcache: + pcache[pkey] = collection.complete(base) + packages, unknown = pcache[pkey] if unknown: self.logger.info("Packages: Got %d unknown entries" % len(unknown)) self.logger.info("Packages: %s" % list(unknown)) @@ -441,7 +426,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, self._load_config() return True - def expire_cache(self, _=None): + def child_reload(self, _=None): + """ Reload the Packages configuration on a child process. """ self.Reload() def _load_config(self, force_update=False): @@ -472,10 +458,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, collection.setup_data(force_update) # clear Collection and package caches - self.clients = dict() - self.collections = dict() - self.groupcache = dict() - self.pkgcache = dict() + Bcfg2.Server.Cache.expire("Packages") for source in self.sources.entries: cachefiles.add(source.cachefile) @@ -551,11 +534,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, if not self.sources.loaded: # if sources.xml has not received a FAM event yet, defer; # instantiate a dummy Collection object - collection = Collection(metadata, [], self.cachepath, self.data) - ckey = collection.cachekey - self.groupcache.setdefault(ckey, dict()) - self.pkgcache.setdefault(ckey, dict()) - return collection + return Collection(metadata, [], self.cachepath, self.data) if metadata.hostname in self.clients: return self.collections[self.clients[metadata.hostname]] @@ -592,8 +571,6 @@ class Packages(Bcfg2.Server.Plugin.Plugin, if cclass != Collection: self.clients[metadata.hostname] = ckey self.collections[ckey] = collection - self.groupcache.setdefault(ckey, dict()) - self.pkgcache.setdefault(ckey, dict()) return collection def get_additional_data(self, metadata): @@ -642,8 +619,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, :param metadata: The client metadata :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata """ - if metadata.hostname in self.clients: - del self.clients[metadata.hostname] + self.clients.expire(metadata.hostname) def end_statistics(self, metadata): """ Hook to clear the cache for this client in :attr:`clients` diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py index 8ce4e8a54..374711ca5 100644 --- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py +++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py @@ -89,7 +89,6 @@ class KnownHostsEntrySet(Bcfg2.Server.Plugin.EntrySet): class SSHbase(Bcfg2.Server.Plugin.Plugin, - Bcfg2.Server.Plugin.Caching, Bcfg2.Server.Plugin.Generator, Bcfg2.Server.Plugin.PullTarget): """ @@ -123,7 +122,6 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) - Bcfg2.Server.Plugin.Caching.__init__(self) Bcfg2.Server.Plugin.Generator.__init__(self) Bcfg2.Server.Plugin.PullTarget.__init__(self) self.ipcache = {} @@ -150,9 +148,6 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin, self.Entries['Path']["/etc/ssh/" + keypattern] = self.build_hk self.cmd = Executor() - def expire_cache(self, key=None): - self.__skn = False - def get_skn(self): """Build memory cache of the ssh known hosts file.""" if not self.__skn: |