summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins
diff options
context:
space:
mode:
authorJason Kincl <kincljc@ornl.gov>2012-11-19 10:37:34 -0500
committerJason Kincl <kincljc@ornl.gov>2012-11-19 10:37:34 -0500
commit894299b01b6138c54a99fd41f166554d175d6106 (patch)
tree88e11cb8c49d3f933cf5f622a93dfa123922960f /src/lib/Bcfg2/Server/Plugins
parentde0ae51b6dc635a3acd2491d4ca3fd021aa55873 (diff)
parentf4da37aa0a360add3f5c40f37cd3cc010ef8788f (diff)
downloadbcfg2-894299b01b6138c54a99fd41f166554d175d6106.tar.gz
bcfg2-894299b01b6138c54a99fd41f166554d175d6106.tar.bz2
bcfg2-894299b01b6138c54a99fd41f166554d175d6106.zip
Merge remote branch 'upstream/master' into jasons-hacking
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins')
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py36
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py188
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py7
-rw-r--r--src/lib/Bcfg2/Server/Plugins/NagiosGen.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py33
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/__init__.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSHbase.py48
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSLCA.py76
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py46
17 files changed, 234 insertions, 285 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
index 73c70901b..8ebd8d921 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
@@ -2,12 +2,9 @@
<http://www.cheetahtemplate.org/>`_ templating system to generate
:ref:`server-plugins-generators-cfg` files. """
-import logging
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import CfgGenerator
-LOGGER = logging.getLogger(__name__)
-
try:
from Cheetah.Template import Template
HAS_CHEETAH = True
@@ -33,9 +30,7 @@ class CfgCheetahGenerator(CfgGenerator):
def __init__(self, fname, spec, encoding):
CfgGenerator.__init__(self, fname, spec, encoding)
if not HAS_CHEETAH:
- msg = "Cfg: Cheetah is not available: %s" % self.name
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Cheetah is not available")
__init__.__doc__ = CfgGenerator.__init__.__doc__
def get_data(self, entry, metadata):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py
index 00b95c970..da506a195 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py
@@ -1,14 +1,11 @@
""" Handle .diff files, which apply diffs to plaintext files """
import os
-import logging
import tempfile
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import PluginExecutionError
from subprocess import Popen, PIPE
from Bcfg2.Server.Plugins.Cfg import CfgFilter
-LOGGER = logging.getLogger(__name__)
-
class CfgDiffFilter(CfgFilter):
""" CfgDiffFilter applies diffs to plaintext
@@ -32,8 +29,7 @@ class CfgDiffFilter(CfgFilter):
output = open(basename, 'r').read()
os.unlink(basename)
if ret != 0:
- msg = "Error applying diff %s: %s" % (self.name, stderr)
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Error applying diff %s: %s" %
+ (self.name, stderr))
return output
modify_data.__doc__ = CfgFilter.modify_data.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py
index 26faf6e2c..3b4703ddb 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py
@@ -1,7 +1,6 @@
""" CfgEncryptedGenerator lets you encrypt your plaintext
:ref:`server-plugins-generators-cfg` files on the server. """
-import logging
from Bcfg2.Server.Plugin import PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
try:
@@ -11,8 +10,6 @@ try:
except ImportError:
HAS_CRYPTO = False
-LOGGER = logging.getLogger(__name__)
-
class CfgEncryptedGenerator(CfgGenerator):
""" CfgEncryptedGenerator lets you encrypt your plaintext
@@ -28,9 +25,7 @@ class CfgEncryptedGenerator(CfgGenerator):
def __init__(self, fname, spec, encoding):
CfgGenerator.__init__(self, fname, spec, encoding)
if not HAS_CRYPTO:
- msg = "Cfg: M2Crypto is not available"
- LOGGER.error(msg)
- raise PluginExecutionError(msg)
+ raise PluginExecutionError("M2Crypto is not available")
__init__.__doc__ = CfgGenerator.__init__.__doc__
def handle_event(self, event):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
index feedbdb1b..130652aef 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
@@ -1,7 +1,6 @@
""" Handle encrypted Genshi templates (.crypt.genshi or .genshi.crypt
files) """
-import logging
from Bcfg2.Compat import StringIO
from Bcfg2.Server.Plugin import PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import SETUP
@@ -19,10 +18,6 @@ except ImportError:
# CfgGenshiGenerator will raise errors if genshi doesn't exist
TemplateLoader = object # pylint: disable=C0103
-LOGGER = logging.getLogger(__name__)
-
-LOGGER = logging.getLogger(__name__)
-
class EncryptedTemplateLoader(TemplateLoader):
""" Subclass :class:`genshi.template.TemplateLoader` to decrypt
@@ -53,6 +48,4 @@ class CfgEncryptedGenshiGenerator(CfgGenshiGenerator):
def __init__(self, fname, spec, encoding):
CfgGenshiGenerator.__init__(self, fname, spec, encoding)
if not HAS_CRYPTO:
- msg = "Cfg: M2Crypto is not available"
- LOGGER.error(msg)
- raise PluginExecutionError(msg)
+ raise PluginExecutionError("M2Crypto is not available")
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
index 023af7d4e..b702ac899 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
@@ -3,13 +3,10 @@
import os
import sys
import shlex
-import logging
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import PluginExecutionError
from subprocess import Popen, PIPE
from Bcfg2.Server.Plugins.Cfg import CfgVerifier, CfgVerificationError
-LOGGER = logging.getLogger(__name__)
-
class CfgExternalCommandVerifier(CfgVerifier):
""" Invoke an external script to verify
@@ -46,9 +43,6 @@ class CfgExternalCommandVerifier(CfgVerifier):
if bangpath.startswith("#!"):
self.cmd.extend(shlex.split(bangpath[2:].strip()))
else:
- msg = "%s: Cannot execute %s" % (self.__class__.__name__,
- self.name)
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Cannot execute %s" % self.name)
self.cmd.append(self.name)
handle_event.__doc__ = CfgVerifier.handle_event.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index ce77717da..df0c30c09 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -4,13 +4,10 @@
import re
import sys
-import logging
import traceback
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import PluginExecutionError
from Bcfg2.Server.Plugins.Cfg import CfgGenerator
-LOGGER = logging.getLogger(__name__)
-
try:
import genshi.core
from genshi.template import TemplateLoader, NewTextTemplate
@@ -67,11 +64,9 @@ class CfgGenshiGenerator(CfgGenerator):
def __init__(self, fname, spec, encoding):
CfgGenerator.__init__(self, fname, spec, encoding)
if not HAS_GENSHI:
- msg = "Cfg: Genshi is not available: %s" % fname
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- self.loader = self.__loader_cls__()
+ raise PluginExecutionError("Genshi is not available")
self.template = None
+ self.loader = self.__loader_cls__(max_cache_size=0)
__init__.__doc__ = CfgGenerator.__init__.__doc__
def get_data(self, entry, metadata):
@@ -92,15 +87,15 @@ class CfgGenshiGenerator(CfgGenerator):
stack = traceback.extract_tb(sys.exc_info()[2])
for quad in stack:
if quad[0] == self.name:
- LOGGER.error("Cfg: Error rendering %s at '%s': %s: %s" %
- (fname, quad[2], err.__class__.__name__, err))
- break
+ raise PluginExecutionError("%s: %s at '%s'" %
+ (err.__class__.__name__, err,
+ quad[2]))
raise
except:
- self._handle_genshi_exception(fname, sys.exc_info())
+ self._handle_genshi_exception(sys.exc_info())
get_data.__doc__ = CfgGenerator.get_data.__doc__
- def _handle_genshi_exception(self, fname, exc):
+ def _handle_genshi_exception(self, exc):
""" this is horrible, and I deeply apologize to whoever gets
to maintain this after I go to the Great Beer Garden in the
Sky. genshi is incredibly opaque about what's being executed,
@@ -140,21 +135,16 @@ class CfgGenshiGenerator(CfgGenerator):
# single line break)
real_lineno = lineno - contents.code.co_firstlineno
src = re.sub(r'\n\n+', '\n', contents.source).splitlines()
- LOGGER.error("Cfg: Error rendering %s at '%s': %s: %s" %
- (fname, src[real_lineno], err.__class__.__name__,
- err))
+ raise PluginExecutionError("%s: %s at '%s'" %
+ (err.__class__.__name__, err,
+ src[real_lineno]))
raise
def handle_event(self, event):
- CfgGenerator.handle_event(self, event)
- if self.data is None:
- return
try:
self.template = self.loader.load(self.name, cls=NewTextTemplate,
encoding=self.encoding)
except:
- msg = "Cfg: Could not load template %s: %s" % (self.name,
- sys.exc_info()[1])
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Failed to load template: %s" %
+ sys.exc_info()[1])
handle_event.__doc__ = CfgGenerator.handle_event.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
index e5ba0a51b..3b6fc8fa0 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
@@ -1,11 +1,8 @@
""" Handle info.xml files """
-import logging
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import PluginExecutionError, InfoXML
from Bcfg2.Server.Plugins.Cfg import CfgInfo
-LOGGER = logging.getLogger(__name__)
-
class CfgInfoXML(CfgInfo):
""" CfgInfoXML handles :file:`info.xml` files for
@@ -16,16 +13,15 @@ class CfgInfoXML(CfgInfo):
def __init__(self, path):
CfgInfo.__init__(self, path)
- self.infoxml = Bcfg2.Server.Plugin.InfoXML(path)
+ self.infoxml = InfoXML(path)
__init__.__doc__ = CfgInfo.__init__.__doc__
def bind_info_to_entry(self, entry, metadata):
mdata = dict()
self.infoxml.pnode.Match(metadata, mdata, entry=entry)
if 'Info' not in mdata:
- msg = "Failed to set metadata for file %s" % entry.get('name')
- LOGGER.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Failed to set metadata for file %s" %
+ entry.get('name'))
self._set_info(entry, mdata['Info'][None])
bind_info_to_entry.__doc__ = CfgInfo.bind_info_to_entry.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 58f6e1e42..db6810e7c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -542,8 +542,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet):
try:
return generator.get_data(entry, metadata)
except:
- msg = "Cfg: exception rendering %s with %s: %s" % \
- (entry.get("name"), generator, sys.exc_info()[1])
+ msg = "Cfg: Error rendering %s: %s" % (entry.get("name"),
+ sys.exc_info()[1])
LOGGER.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index 5faa6c018..8cc63a46f 100644
--- a/src/lib/Bcfg2/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -1,119 +1,15 @@
""" The Git plugin provides a revision interface for Bcfg2 repos using
git. """
-import os
import sys
import Bcfg2.Server.Plugin
-
-
-class GitAPIBase(object):
- """ Base class for the various Git APIs (dulwich, GitPython,
- subprocesses) """
- def __init__(self, path):
- self.path = path
-
- def revision(self):
- """ Get the current revision of the git repo as a string """
- raise NotImplementedError
-
- def pull(self):
- """ Pull the latest version of the upstream git repo and
- rebase against it. """
- raise NotImplementedError
-
+from subprocess import Popen, PIPE
try:
- from dulwich.client import get_transport_and_path
- from dulwich.repo import Repo
- from dulwich.file import GitFile, ensure_dir_exists
-
- class GitAPI(GitAPIBase):
- """ API for :class:`Git` using :mod:`dulwich` """
- def __init__(self, path):
- GitAPIBase.__init__(self, path)
- self.repo = Repo(self.path)
- self.client, self.origin_path = get_transport_and_path(
- self.repo.get_config().get(("remote", "origin"),
- "url"))
-
- def revision(self):
- return self.repo.head()
-
- def pull(self):
- try:
- remote_refs = self.client.fetch(
- self.origin_path, self.repo,
- determine_wants=self.repo.object_store.determine_wants_all)
- except KeyError:
- etype, err = sys.exc_info()[:2]
- # try to work around bug
- # https://bugs.launchpad.net/dulwich/+bug/1025886
- try:
- # pylint: disable=W0212
- self.client._fetch_capabilities.remove('thin-pack')
- # pylint: enable=W0212
- except KeyError:
- raise etype(err)
- remote_refs = self.client.fetch(
- self.origin_path, self.repo,
- determine_wants=self.repo.object_store.determine_wants_all)
-
- tree_id = self.repo[remote_refs['HEAD']].tree
- # iterate over tree content, giving path and blob sha.
- for entry in self.repo.object_store.iter_tree_contents(tree_id):
- entry_in_path = entry.in_path(self.repo.path)
- ensure_dir_exists(os.path.split(entry_in_path.path)[0])
- GitFile(entry_in_path.path,
- 'wb').write(self.repo[entry.sha].data)
-
+ import git
+ HAS_GITPYTHON = True
except ImportError:
- try:
- import git
-
- class GitAPI(GitAPIBase):
- """ API for :class:`Git` using :mod:`git` (GitPython) """
- def __init__(self, path):
- GitAPIBase.__init__(self, path)
- self.repo = git.Repo(path)
-
- def revision(self):
- return self.repo.head.commit.hexsha
-
- def pull(self):
- self.repo.git.pull("--rebase")
-
- except ImportError:
- from subprocess import Popen, PIPE
-
- try:
- Popen(["git"], stdout=PIPE, stderr=PIPE).wait()
-
- class GitAPI(GitAPIBase):
- """ API for :class:`Git` using subprocess to run git
- commands """
- def revision(self):
- proc = Popen(["git", "--work-tree",
- os.path.join(self.path, ".git"),
- "rev-parse", "HEAD"], stdout=PIPE,
- stderr=PIPE)
- rv, err = proc.communicate()
- if proc.wait():
- raise Exception("Git: Error getting revision from %s: "
- "%s" % (self.path, err))
- return rv.strip() # pylint: disable=E1103
-
- def pull(self):
- proc = Popen(["git", "--work-tree",
- os.path.join(self.path, ".git"),
- "pull", "--rebase"], stdout=PIPE,
- stderr=PIPE)
- err = proc.communicate()[1].strip()
- if proc.wait():
- raise Exception("Git: Error pulling: %s" % err)
-
- except OSError:
- raise ImportError("Could not import dulwich or GitPython "
- "libraries, and no 'git' command found in PATH")
+ HAS_GITPYTHON = False
class Git(Bcfg2.Server.Plugin.Version):
@@ -121,35 +17,83 @@ class Git(Bcfg2.Server.Plugin.Version):
using git. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".git"
- __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
+ if HAS_GITPYTHON:
+ __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
- self.repo = GitAPI(self.vcs_root)
+ if HAS_GITPYTHON:
+ self.repo = git.Repo(self.vcs_root)
+ else:
+ self.logger.debug("Git: GitPython not found, using CLI interface "
+ "to Git")
+ self.repo = None
self.logger.debug("Initialized git plugin with git directory %s" %
self.vcs_path)
def get_revision(self):
"""Read git revision information for the Bcfg2 repository."""
try:
- return self.repo.revision()
+ if HAS_GITPYTHON:
+ return self.repo.head.commit.hexsha
+ else:
+ cmd = ["git", "--git-dir", self.vcs_path,
+ "--work-tree", self.vcs_root, "rev-parse", "HEAD"]
+ self.debug_log("Git: Running cmd")
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
+ rv, err = proc.communicate()
+ if proc.wait():
+ raise Exception(err)
+ return rv
except:
err = sys.exc_info()[1]
- msg = "Failed to read git repository: %s" % err
+ msg = "Git: Error getting revision from %s: %s" % (self.vcs_root,
+ err)
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- def Update(self):
+ def Update(self, ref=None):
""" Git.Update() => True|False
Update the working copy against the upstream repository
"""
+ self.logger.info("Git: Git.Update(ref='%s')" % ref)
+ self.debug_log("Git: Performing garbage collection on repo at %s" %
+ self.vcs_root)
try:
- self.repo.pull()
- self.logger.info("Git repo at %s updated to %s" %
- (self.vcs_root, self.get_revision()))
- return True
- except: # pylint: disable=W0702
- err = sys.exc_info()[1]
- msg = "Failed to pull from git repository: %s" % err
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ self.repo.git.gc('--auto')
+ except git.GitCommandError:
+ self.logger.warning("Git: Failed to perform garbage collection: %s"
+ % sys.exc_info()[1])
+
+ if ref:
+ self.debug_log("Git: Checking out %s" % ref)
+ try:
+ self.repo.git.checkout('-f', ref)
+ except git.GitCommandError:
+ err = sys.exc_info()[1]
+ msg = "Git: Failed to checkout %s: %s" % (ref, err)
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+
+ # determine if we should try to pull to get the latest commit
+ # on this head
+ tracking = None
+ if not self.repo.head.is_detached:
+ self.debug_log("Git: Determining if %s is a tracking branch" %
+ self.repo.head.ref.name)
+ tracking = self.repo.head.ref.tracking_branch()
+
+ if tracking is not None:
+ self.debug_log("Git: %s is a tracking branch, pulling from %s" %
+ (self.repo.head.ref.name, tracking))
+ try:
+ self.repo.git.pull("--rebase")
+ except: # pylint: disable=W0702
+ err = sys.exc_info()[1]
+ msg = "Git: Failed to pull from upstream: %s" % err
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+
+ self.logger.info("Git: Repo at %s updated to %s" %
+ (self.vcs_root, self.get_revision()))
+ return True
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 8b0fc16ce..0ab72f2c5 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -967,9 +967,10 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
return self.aliases[cname]
return cname
except socket.herror:
- warning = "address resolution error for %s" % address
- self.logger.warning(warning)
- raise Bcfg2.Server.Plugin.MetadataConsistencyError(warning)
+ err = "Address resolution error for %s: %s" % (address,
+ sys.exc_info()[1])
+ self.logger.error(err)
+ raise Bcfg2.Server.Plugin.MetadataConsistencyError(err)
def _merge_groups(self, client, groups, categories=None):
""" set group membership based on the contents of groups.xml
diff --git a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
index fbad0a37b..023547b7e 100644
--- a/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
+++ b/src/lib/Bcfg2/Server/Plugins/NagiosGen.py
@@ -81,7 +81,7 @@ class NagiosGen(Bcfg2.Server.Plugin.Plugin,
if xtra:
host_config.extend([self.line_fmt % (opt, val)
for opt, val in list(xtra.items())])
- else:
+ if 'use' not in xtra:
host_config.append(self.line_fmt % ('use', 'default'))
host_config.append('}')
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
index 94dc6d2fd..2735e389a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py
@@ -75,11 +75,11 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile,
#: should be told to reload its data.
self.parsed = set()
- def toggle_debug(self):
- Bcfg2.Server.Plugin.Debuggable.toggle_debug(self)
+ def set_debug(self, debug):
+ Bcfg2.Server.Plugin.Debuggable.set_debug(self, debug)
for source in self.entries:
- source.toggle_debug()
- toggle_debug.__doc__ = Bcfg2.Server.Plugin.Plugin.toggle_debug.__doc__
+ source.set_debug(debug)
+ set_debug.__doc__ = Bcfg2.Server.Plugin.Plugin.set_debug.__doc__
def HandleEvent(self, event=None):
""" HandleEvent is called whenever the FAM registers an event.
@@ -121,8 +121,8 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile,
self.entries.append(source)
Index.__doc__ = Bcfg2.Server.Plugin.StructFile.Index.__doc__ + """
-``Index`` is responsible for calling :func:`source_from_xml` for each
-``Source`` tag in each file. """
+ ``Index`` is responsible for calling :func:`source_from_xml`
+ for each ``Source`` tag in each file. """
@Bcfg2.Server.Plugin.track_statistics()
def source_from_xml(self, xsource):
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index 59e7a206e..220146100 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -102,6 +102,9 @@ FL = '{http://linux.duke.edu/metadata/filelists}'
PULPSERVER = None
PULPCONFIG = None
+#: The path to bcfg2-yum-helper
+HELPER = None
+
def _setup_pulp(setup):
""" Connect to a Pulp server and pass authentication credentials.
@@ -308,8 +311,6 @@ class YumCollection(Collection):
(certdir, err))
self.pulp_cert_set = PulpCertificateSet(certdir, self.fam)
- self._helper = None
-
@property
def __package_groups__(self):
""" YumCollections support package groups only if
@@ -324,20 +325,20 @@ class YumCollection(Collection):
a call to it; I wish there was a way to do this without
forking, but apparently not); finally we check in /usr/sbin,
the default location. """
- try:
- return self.setup.cfp.get("packages:yum", "helper")
- except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
- pass
-
- if not self._helper:
- # first see if bcfg2-yum-helper is in PATH
+ global HELPER
+ if not HELPER:
try:
- Popen(['bcfg2-yum-helper'],
- stdin=PIPE, stdout=PIPE, stderr=PIPE).wait()
- self._helper = 'bcfg2-yum-helper'
- except OSError:
- self._helper = "/usr/sbin/bcfg2-yum-helper"
- return self._helper
+ HELPER = self.setup.cfp.get("packages:yum", "helper")
+ except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+ # first see if bcfg2-yum-helper is in PATH
+ try:
+ self.debug_log("Checking for bcfg2-yum-helper in $PATH")
+ Popen(['bcfg2-yum-helper'],
+ stdin=PIPE, stdout=PIPE, stderr=PIPE).wait()
+ HELPER = 'bcfg2-yum-helper'
+ except OSError:
+ HELPER = "/usr/sbin/bcfg2-yum-helper"
+ return HELPER
@property
def use_yum(self):
@@ -357,7 +358,7 @@ class YumCollection(Collection):
def cachefiles(self):
""" A list of the full path to all cachefiles used by this
collection."""
- cachefiles = set(Collection.cachefiles(self))
+ cachefiles = set(Collection.cachefiles.fget(self))
if self.cachefile:
cachefiles.add(self.cachefile)
return list(cachefiles)
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
index 5a193219c..f30e060bd 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py
@@ -113,13 +113,13 @@ class Packages(Bcfg2.Server.Plugin.Plugin,
__init__.__doc__ = Bcfg2.Server.Plugin.Plugin.__init__.__doc__
- def toggle_debug(self):
- rv = Bcfg2.Server.Plugin.Plugin.toggle_debug(self)
- self.sources.toggle_debug()
+ def set_debug(self, debug):
+ rv = Bcfg2.Server.Plugin.Plugin.set_debug(self, debug)
+ self.sources.set_debug(debug)
for collection in self.collections.values():
- collection.toggle_debug()
+ collection.set_debug(debug)
return rv
- toggle_debug.__doc__ = Bcfg2.Server.Plugin.Plugin.toggle_debug.__doc__
+ set_debug.__doc__ = Bcfg2.Server.Plugin.Plugin.set_debug.__doc__
@property
def disableResolver(self):
diff --git a/src/lib/Bcfg2/Server/Plugins/SSHbase.py b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
index bab7c4a4a..feb76aa57 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSHbase.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSHbase.py
@@ -9,7 +9,8 @@ import logging
import tempfile
from subprocess import Popen, PIPE
import Bcfg2.Server.Plugin
-from Bcfg2.Compat import u_str, reduce, b64encode # pylint: disable=W0622
+from Bcfg2.Server.Plugin import PluginExecutionError
+from Bcfg2.Compat import any, u_str, reduce, b64encode # pylint: disable=W0622
LOGGER = logging.getLogger(__name__)
@@ -111,9 +112,7 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
is regenerated each time a new key is generated.
"""
- name = 'SSHbase'
__author__ = 'bcfg-dev@mcs.anl.gov'
-
keypatterns = ["ssh_host_dsa_key",
"ssh_host_ecdsa_key",
"ssh_host_rsa_key",
@@ -250,9 +249,11 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
for entry in list(self.entries.values()):
if entry.specific.match(event.filename):
entry.handle_event(event)
- if event.filename.endswith(".pub"):
- self.logger.info("New public key %s; invalidating "
- "ssh_known_hosts cache" % event.filename)
+ if any(event.filename.startswith(kp)
+ for kp in self.keypatterns
+ if kp.endswith(".pub")):
+ self.debug_log("New public key %s; invalidating "
+ "ssh_known_hosts cache" % event.filename)
self.skn = False
return
@@ -365,8 +366,9 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
is_bound = False
while not is_bound:
if tries >= 10:
- self.logger.error("%s still not registered" % filename)
- raise Bcfg2.Server.Plugin.PluginExecutionError
+ msg = "%s still not registered" % filename
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
self.core.fam.handle_events_in_interval(1)
tries += 1
try:
@@ -385,26 +387,30 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
else:
keytype = 'rsa1'
else:
- self.logger.error("Unknown key filename: %s" % filename)
- return
+ raise PluginExecutionError("Unknown key filename: %s" % filename)
- fileloc = "%s/%s" % (self.data, hostkey)
- publoc = self.data + '/' + ".".join([hostkey.split('.')[0], 'pub',
- "H_%s" % client])
+ fileloc = os.path.join(self.data, hostkey)
+ publoc = os.path.join(self.data,
+ ".".join([hostkey.split('.')[0], 'pub',
+ "H_%s" % client]))
tempdir = tempfile.mkdtemp()
- temploc = "%s/%s" % (tempdir, hostkey)
+ temploc = os.path.join(tempdir, hostkey)
cmd = ["ssh-keygen", "-q", "-f", temploc, "-N", "",
"-t", keytype, "-C", "root@%s" % client]
+ self.debug_log("SSHbase: Running: %s" % " ".join(cmd))
proc = Popen(cmd, stdout=PIPE, stdin=PIPE)
- proc.communicate()
- proc.wait()
+ err = proc.communicate()[1]
+ if proc.wait():
+ raise PluginExecutionError("SSHbase: Error running ssh-keygen: %s"
+ % err)
try:
shutil.copy(temploc, fileloc)
shutil.copy("%s.pub" % temploc, publoc)
except IOError:
err = sys.exc_info()[1]
- self.logger.error("Temporary SSH keys not found: %s" % err)
+ raise PluginExecutionError("Temporary SSH keys not found: %s" %
+ err)
try:
os.unlink(temploc)
@@ -412,7 +418,8 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
os.rmdir(tempdir)
except OSError:
err = sys.exc_info()[1]
- self.logger.error("Failed to unlink temporary ssh keys: %s" % err)
+ raise PluginExecutionError("Failed to unlink temporary ssh keys: "
+ "%s" % err)
def AcceptChoices(self, _, metadata):
return [Bcfg2.Server.Plugin.Specificity(hostname=metadata.hostname)]
@@ -420,8 +427,9 @@ class SSHbase(Bcfg2.Server.Plugin.Plugin,
def AcceptPullData(self, specific, entry, log):
"""Per-plugin bcfg2-admin pull support."""
# specific will always be host specific
- filename = "%s/%s.H_%s" % (self.data, entry['name'].split('/')[-1],
- specific.hostname)
+ filename = os.path.join(self.data,
+ "%s.H_%s" % (entry['name'].split('/')[-1],
+ specific.hostname))
try:
open(filename, 'w').write(entry['text'])
if log:
diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
index ab55425a6..62396f860 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
@@ -1,13 +1,15 @@
""" The SSLCA generator handles the creation and management of ssl
certificates and their keys. """
+import os
+import sys
import Bcfg2.Server.Plugin
import Bcfg2.Options
import lxml.etree
import tempfile
-import os
from subprocess import Popen, PIPE, STDOUT
from Bcfg2.Compat import ConfigParser, md5
+from Bcfg2.Server.Plugin import PluginExecutionError
class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
@@ -107,6 +109,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
filename = os.path.join(path, "%s.H_%s" % (os.path.basename(path),
metadata.hostname))
if filename not in list(self.entries.keys()):
+ self.logger.info("SSLCA: Generating new key %s" % filename)
key = self.build_key(entry)
open(self.data + filename, 'w').write(key)
entry.text = key
@@ -130,6 +133,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
cmd = ["openssl", "genrsa", bits]
elif ktype == 'dsa':
cmd = ["openssl", "dsaparam", "-noout", "-genkey", bits]
+ self.debug_log("SSLCA: Generating new key: %s" % " ".join(cmd))
return Popen(cmd, stdout=PIPE).stdout.read()
def get_cert(self, entry, metadata):
@@ -151,10 +155,11 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
self.core.Bind(el, metadata)
# check if we have a valid hostfile
- if (filename in list(self.entries.keys()) and
+ if (filename in self.entries.keys() and
self.verify_cert(filename, key_filename, entry)):
entry.text = self.entries[filename].data
else:
+ self.logger.info("SSLCA: Generating new cert %s" % filename)
cert = self.build_cert(key_filename, entry, metadata)
open(self.data + filename, 'w').write(cert)
self.entries[filename] = self.__child__(self.data + filename)
@@ -231,22 +236,37 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
"""
creates a new certificate according to the specification
"""
- req_config = self.build_req_config(entry, metadata)
- req = self.build_request(key_filename, req_config, entry)
- ca = self.cert_specs[entry.get('name')]['ca']
- ca_config = self.CAs[ca]['config']
- days = self.cert_specs[entry.get('name')]['days']
- passphrase = self.CAs[ca].get('passphrase')
- cmd = ["openssl", "ca", "-config", ca_config, "-in", req,
- "-days", days, "-batch"]
- if passphrase:
- cmd.extend(["-passin", "pass:%s" % passphrase])
- cert = Popen(cmd, stdout=PIPE).stdout.read()
+ req_config = None
+ req = None
try:
- os.unlink(req_config)
- os.unlink(req)
- except OSError:
- self.logger.error("Failed to unlink temporary files")
+ req_config = self.build_req_config(entry, metadata)
+ req = self.build_request(key_filename, req_config, entry)
+ ca = self.cert_specs[entry.get('name')]['ca']
+ ca_config = self.CAs[ca]['config']
+ days = self.cert_specs[entry.get('name')]['days']
+ passphrase = self.CAs[ca].get('passphrase')
+ cmd = ["openssl", "ca", "-config", ca_config, "-in", req,
+ "-days", days, "-batch"]
+ if passphrase:
+ cmd.extend(["-passin", "pass:%s" % passphrase])
+ self.debug_log("SSLCA: Generating new certificate: %s" %
+ " ".join(cmd))
+ proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ (cert, err) = proc.communicate()
+ if proc.wait():
+ # pylint: disable=E1103
+ raise PluginExecutionError("SSLCA: Failed to generate cert: %s"
+ % err.splitlines()[-1])
+ # pylint: enable=E1103
+ finally:
+ try:
+ if req_config and os.path.exists(req_config):
+ os.unlink(req_config)
+ if req and os.path.exists(req):
+ os.unlink(req)
+ except OSError:
+ self.logger.error("SSLCA: Failed to unlink temporary files: %s"
+ % sys.exc_info()[1])
if (self.cert_specs[entry.get('name')]['append_chain'] and
self.CAs[ca]['chaincert']):
cert += open(self.CAs[ca]['chaincert']).read()
@@ -258,7 +278,7 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
used to generate the required certificate request
"""
# create temp request config file
- conffile = open(tempfile.mkstemp()[1], 'w')
+ fd, fname = tempfile.mkstemp()
cfp = ConfigParser.ConfigParser({})
cfp.optionxform = str
defaults = {
@@ -290,18 +310,28 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
cfp.set('req_distinguished_name', item,
self.cert_specs[entry.get('name')][item])
cfp.set('req_distinguished_name', 'CN', metadata.hostname)
- cfp.write(conffile)
- conffile.close()
- return conffile.name
+ self.debug_log("SSLCA: Writing temporary request config to %s" % fname)
+ try:
+ cfp.write(os.fdopen(fd, 'w'))
+ except IOError:
+ raise PluginExecutionError("SSLCA: Failed to write temporary CSR "
+ "config file: %s" % sys.exc_info()[1])
+ return fname
def build_request(self, key_filename, req_config, entry):
"""
creates the certificate request
"""
- req = tempfile.mkstemp()[1]
+ fd, req = tempfile.mkstemp()
+ os.close(fd)
days = self.cert_specs[entry.get('name')]['days']
key = self.data + key_filename
cmd = ["openssl", "req", "-new", "-config", req_config,
"-days", days, "-key", key, "-text", "-out", req]
- Popen(cmd, stdout=PIPE).wait()
+ self.debug_log("SSLCA: Generating new CSR: %s" % " ".join(cmd))
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
+ err = proc.communicate()[1]
+ if proc.wait():
+ raise PluginExecutionError("SSLCA: Failed to generate CSR: %s" %
+ err)
return req
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
index 627c82f25..f09d4839e 100644
--- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -13,15 +13,25 @@ LOGGER = logging.getLogger(__name__)
MODULE_RE = re.compile(r'(?P<filename>(?P<module>[^\/]+)\.py)$')
-class HelperModule(Bcfg2.Server.Plugin.FileBacked):
+class HelperModule(object):
""" Representation of a TemplateHelper module """
def __init__(self, name, fam=None):
- Bcfg2.Server.Plugin.FileBacked.__init__(self, name, fam=fam)
+ self.name = name
+ self.fam = fam
self._module_name = MODULE_RE.search(self.name).group('module')
self._attrs = []
- def Index(self):
+ def HandleEvent(self, event=None):
+ """ HandleEvent is called whenever the FAM registers an event.
+
+ :param event: The event object
+ :type event: Bcfg2.Server.FileMonitor.Event
+ :returns: None
+ """
+ if event and event.code2str() not in ['exists', 'changed', 'created']:
+ return
+
try:
module = imp.load_source(self._module_name, self.name)
except: # pylint: disable=W0702
@@ -54,27 +64,23 @@ class HelperModule(Bcfg2.Server.Plugin.FileBacked):
self._attrs = newattrs
-class HelperSet(Bcfg2.Server.Plugin.DirectoryBacked):
- """ A set of template helper modules """
- ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\.py[co])$")
- patterns = MODULE_RE
- __child__ = HelperModule
-
-
class TemplateHelper(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Connector):
+ Bcfg2.Server.Plugin.Connector,
+ Bcfg2.Server.Plugin.DirectoryBacked):
""" A plugin to provide helper classes and functions to templates """
- name = 'TemplateHelper'
__author__ = 'chris.a.st.pierre@gmail.com'
+ ignore = re.compile("^(\.#.*|.*~|\\..*\\.(sw[px])|.*\.py[co])$")
+ patterns = MODULE_RE
+ __child__ = HelperModule
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
Bcfg2.Server.Plugin.Connector.__init__(self)
- self.helpers = HelperSet(self.data, core.fam)
+ Bcfg2.Server.Plugin.DirectoryBacked.__init__(self, self.data, core.fam)
def get_additional_data(self, _):
return dict([(h._module_name, h) # pylint: disable=W0212
- for h in self.helpers.entries.values()])
+ for h in self.entries.values()])
class TemplateHelperLint(Bcfg2.Server.Lint.ServerlessPlugin):
@@ -130,9 +136,9 @@ class TemplateHelperLint(Bcfg2.Server.Lint.ServerlessPlugin):
@classmethod
def Errors(cls):
- return {"templatehelper-import-error":"error",
- "templatehelper-no-export":"error",
- "templatehelper-nonlist-export":"error",
- "templatehelper-nonexistent-export":"error",
- "templatehelper-reserved-export":"error",
- "templatehelper-underscore-export":"warning"}
+ return {"templatehelper-import-error": "error",
+ "templatehelper-no-export": "error",
+ "templatehelper-nonlist-export": "error",
+ "templatehelper-nonexistent-export": "error",
+ "templatehelper-reserved-export": "error",
+ "templatehelper-underscore-export": "warning"}