summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Cfg
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-23 14:50:09 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-04-23 14:50:09 -0400
commit46a47b4120b3d892b8149a5e181e4d976ad87f99 (patch)
treef2697f233fc7f5ad5022864222a5ca87715a651b /src/lib/Bcfg2/Server/Plugins/Cfg
parente1f99d1d5045e0511db42debb30aa97da2018796 (diff)
parent3d06f311274d6b942ee89d8cdb13b2ecc99af1b0 (diff)
downloadbcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.tar.gz
bcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.tar.bz2
bcfg2-46a47b4120b3d892b8149a5e181e4d976ad87f99.zip
Merge branch '1.4.x'
Conflicts: debian/bcfg2-server.install doc/server/plugins/grouping/metadata.txt src/lib/Bcfg2/Client/Client.py src/lib/Bcfg2/Client/Tools/Portage.py src/lib/Bcfg2/Client/Tools/RcUpdate.py src/lib/Bcfg2/Client/Tools/YUM24.py src/lib/Bcfg2/Client/Tools/__init__.py src/lib/Bcfg2/Client/Tools/launchd.py src/lib/Bcfg2/Options.py src/lib/Bcfg2/Server/Core.py src/lib/Bcfg2/Server/Plugin/helpers.py src/lib/Bcfg2/Server/Plugins/Metadata.py src/lib/Bcfg2/Server/models.py src/lib/Bcfg2/Utils.py src/sbin/bcfg2-info src/sbin/bcfg2-test testsuite/Testsrc/Testlib/TestClient/TestTools/Test_init.py testsuite/Testsrc/test_code_checks.py
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins/Cfg')
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgCatFilter.py28
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py35
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py8
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py22
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py104
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py16
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgLegacyInfo.py46
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py91
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py3
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py95
13 files changed, 100 insertions, 373 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py
index 824d01023..a859da0ba 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py
@@ -4,7 +4,7 @@ access. """
import lxml.etree
from Bcfg2.Server.Plugin import StructFile, PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP, CFG
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator, CFG
from Bcfg2.Server.Plugins.Metadata import ClientMetadata
@@ -20,10 +20,6 @@ class CfgAuthorizedKeysGenerator(CfgGenerator, StructFile):
#: Handle authorized keys XML files
__basenames__ = ['authorizedkeys.xml', 'authorized_keys.xml']
- #: This handler is experimental, in part because it depends upon
- #: the (experimental) CfgPrivateKeyCreator handler
- experimental = True
-
def __init__(self, fname):
CfgGenerator.__init__(self, fname, None, None)
StructFile.__init__(self, fname)
@@ -35,9 +31,9 @@ class CfgAuthorizedKeysGenerator(CfgGenerator, StructFile):
def category(self):
""" The name of the metadata category that generated keys are
specific to """
- if (SETUP.cfp.has_section("sshkeys") and
- SETUP.cfp.has_option("sshkeys", "category")):
- return SETUP.cfp.get("sshkeys", "category")
+ if (self.setup.cfp.has_section("sshkeys") and
+ self.setup.cfp.has_option("sshkeys", "category")):
+ return self.setup.cfp.get("sshkeys", "category")
return None
def handle_event(self, event):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCatFilter.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCatFilter.py
deleted file mode 100644
index 49a5a85b3..000000000
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCatFilter.py
+++ /dev/null
@@ -1,28 +0,0 @@
-""" Handle .cat files, which append lines to and remove lines from
-plaintext files """
-
-from Bcfg2.Server.Plugins.Cfg import CfgFilter
-
-
-class CfgCatFilter(CfgFilter):
- """ CfgCatFilter appends lines to and remove lines from plaintext
- :ref:`server-plugins-generators-Cfg` files"""
-
- #: Handle .cat files
- __extensions__ = ['cat']
-
- #: .cat files are deprecated
- deprecated = True
-
- def modify_data(self, entry, metadata, data):
- datalines = data.strip().split('\n')
- for line in self.data.split('\n'):
- if not line:
- continue
- if line.startswith('+'):
- datalines.append(line[1:])
- elif line.startswith('-'):
- if line[1:] in datalines:
- datalines.remove(line[1:])
- return "\n".join(datalines) + "\n"
- modify_data.__doc__ = CfgFilter.modify_data.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
index 724164cf5..4c8adceec 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
@@ -3,7 +3,7 @@
:ref:`server-plugins-generators-cfg` files. """
from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator
try:
from Cheetah.Template import Template
@@ -40,6 +40,6 @@ class CfgCheetahGenerator(CfgGenerator):
template.name = entry.get('realname', entry.get('name'))
template.path = entry.get('realname', entry.get('name'))
template.source_path = self.name
- template.repo = SETUP['repo']
+ template.repo = self.setup['repo']
return template.respond()
get_data.__doc__ = CfgGenerator.get_data.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py
deleted file mode 100644
index da506a195..000000000
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgDiffFilter.py
+++ /dev/null
@@ -1,35 +0,0 @@
-""" Handle .diff files, which apply diffs to plaintext files """
-
-import os
-import tempfile
-from Bcfg2.Server.Plugin import PluginExecutionError
-from subprocess import Popen, PIPE
-from Bcfg2.Server.Plugins.Cfg import CfgFilter
-
-
-class CfgDiffFilter(CfgFilter):
- """ CfgDiffFilter applies diffs to plaintext
- :ref:`server-plugins-generators-Cfg` files """
-
- #: Handle .diff files
- __extensions__ = ['diff']
-
- #: .diff files are deprecated
- deprecated = True
-
- def modify_data(self, entry, metadata, data):
- basehandle, basename = tempfile.mkstemp()
- open(basename, 'w').write(data)
- os.close(basehandle)
-
- cmd = ["patch", "-u", "-f", basename]
- patch = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- stderr = patch.communicate(input=self.data)[1]
- ret = patch.wait()
- output = open(basename, 'r').read()
- os.unlink(basename)
- if ret != 0:
- 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 3b4703ddb..516eba2f6 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenerator.py
@@ -2,10 +2,9 @@
:ref:`server-plugins-generators-cfg` files on the server. """
from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator
try:
- from Bcfg2.Encryption import bruteforce_decrypt, EVPError, \
- get_algorithm
+ from Bcfg2.Server.Encryption import bruteforce_decrypt, EVPError
HAS_CRYPTO = True
except ImportError:
HAS_CRYPTO = False
@@ -34,8 +33,7 @@ class CfgEncryptedGenerator(CfgGenerator):
return
# todo: let the user specify a passphrase by name
try:
- self.data = bruteforce_decrypt(self.data, setup=SETUP,
- algorithm=get_algorithm(SETUP))
+ self.data = bruteforce_decrypt(self.data)
except EVPError:
raise PluginExecutionError("Failed to decrypt %s" % self.name)
handle_event.__doc__ = CfgGenerator.handle_event.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
index 130652aef..a285eecd8 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgEncryptedGenshiGenerator.py
@@ -3,11 +3,10 @@ files) """
from Bcfg2.Compat import StringIO
from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import SETUP
from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator
try:
- from Bcfg2.Encryption import bruteforce_decrypt, get_algorithm
+ from Bcfg2.Server.Encryption import bruteforce_decrypt
HAS_CRYPTO = True
except ImportError:
HAS_CRYPTO = False
@@ -22,11 +21,9 @@ except ImportError:
class EncryptedTemplateLoader(TemplateLoader):
""" Subclass :class:`genshi.template.TemplateLoader` to decrypt
the data on the fly as it's read in using
- :func:`Bcfg2.Encryption.bruteforce_decrypt` """
+ :func:`Bcfg2.Server.Encryption.bruteforce_decrypt` """
def _instantiate(self, cls, fileobj, filepath, filename, encoding=None):
- plaintext = \
- StringIO(bruteforce_decrypt(fileobj.read(),
- algorithm=get_algorithm(SETUP)))
+ plaintext = StringIO(bruteforce_decrypt(fileobj.read()))
return TemplateLoader._instantiate(self, cls, plaintext, filepath,
filename, encoding=encoding)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
index 313e53ee9..d06b864ac 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
@@ -3,8 +3,8 @@
import os
import sys
import shlex
+from Bcfg2.Utils import Executor
from Bcfg2.Server.Plugin import PluginExecutionError
-from subprocess import Popen, PIPE
from Bcfg2.Server.Plugins.Cfg import CfgVerifier, CfgVerificationError
@@ -18,24 +18,16 @@ class CfgExternalCommandVerifier(CfgVerifier):
def __init__(self, name, specific, encoding):
CfgVerifier.__init__(self, name, specific, encoding)
self.cmd = []
+ self.exc = Executor(timeout=30)
__init__.__doc__ = CfgVerifier.__init__.__doc__
def verify_entry(self, entry, metadata, data):
try:
- proc = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- out, err = proc.communicate(input=data)
- rv = proc.wait()
- if rv != 0:
- # pylint: disable=E1103
- raise CfgVerificationError(err.strip() or out.strip() or
- "Non-zero return value %s" % rv)
- # pylint: enable=E1103
- except CfgVerificationError:
- raise
- except:
- err = sys.exc_info()[1]
- raise CfgVerificationError("Error running external command "
- "verifier: %s" % err)
+ result = self.exc.run(self.cmd, inputdata=data)
+ if not result.success:
+ raise CfgVerificationError(result.error)
+ except OSError:
+ raise CfgVerificationError(sys.exc_info()[1])
verify_entry.__doc__ = CfgVerifier.verify_entry.__doc__
def handle_event(self, event):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index c2e5afbad..b3781e299 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -5,63 +5,41 @@
import re
import sys
import traceback
-from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
-
-try:
- import genshi.core
- from genshi.template import TemplateLoader, NewTextTemplate
- from genshi.template.eval import UndefinedError, Suite
- #: True if Genshi libraries are available
- HAS_GENSHI = True
-
- def _genshi_removes_blank_lines():
- """ Genshi 0.5 uses the Python :mod:`compiler` package to
- compile genshi snippets to AST. Genshi 0.6 uses some bespoke
- magic, because compiler has been deprecated.
- :func:`compiler.parse` produces an AST that removes all excess
- whitespace (e.g., blank lines), while
- :func:`genshi.template.astutil.parse` does not. In order to
- determine which actual line of code an error occurs on, we
- need to know which is in use and how it treats blank lines.
- I've beat my head against this for hours and the best/only way
- I can find is to compile some genshi code with an error and
- see which line it's on."""
- code = """d = dict()
-
+from Bcfg2.Server.Plugin import PluginExecutionError, removecomment
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator
+
+from genshi.template import TemplateLoader, NewTextTemplate
+from genshi.template.eval import UndefinedError, Suite
+
+
+def _genshi_removes_blank_lines():
+ """ Genshi 0.5 uses the Python :mod:`compiler` package to
+ compile genshi snippets to AST. Genshi 0.6 uses some bespoke
+ magic, because compiler has been deprecated.
+ :func:`compiler.parse` produces an AST that removes all excess
+ whitespace (e.g., blank lines), while
+ :func:`genshi.template.astutil.parse` does not. In order to
+ determine which actual line of code an error occurs on, we
+ need to know which is in use and how it treats blank lines.
+ I've beat my head against this for hours and the best/only way
+ I can find is to compile some genshi code with an error and
+ see which line it's on."""
+ code = """d = dict()
d['a']"""
- try:
- Suite(code).execute(dict())
- except KeyError:
- line = traceback.extract_tb(sys.exc_info()[2])[-1][1]
- if line == 2:
- return True
- else:
- return False
-
- #: True if Genshi removes all blank lines from a code block before
- #: executing it; False indicates that Genshi only removes leading
- #: and trailing blank lines. See
- #: :func:`_genshi_removes_blank_lines` for an explanation of this.
- GENSHI_REMOVES_BLANK_LINES = _genshi_removes_blank_lines()
-except ImportError:
- TemplateLoader = None # pylint: disable=C0103
- HAS_GENSHI = False
-
-
-def removecomment(stream):
- """ A Genshi filter that removes comments from the stream. This
- function is a generator.
-
- :param stream: The Genshi stream to remove comments from
- :type stream: genshi.core.Stream
- :returns: tuple of ``(kind, data, pos)``, as when iterating
- through a Genshi stream
- """
- for kind, data, pos in stream:
- if kind is genshi.core.COMMENT:
- continue
- yield kind, data, pos
+ try:
+ Suite(code).execute(dict())
+ except KeyError:
+ line = traceback.extract_tb(sys.exc_info()[2])[-1][1]
+ if line == 2:
+ return True
+ else:
+ return False
+
+#: True if Genshi removes all blank lines from a code block before
+#: executing it; False indicates that Genshi only removes leading
+#: and trailing blank lines. See
+#: :func:`_genshi_removes_blank_lines` for an explanation of this.
+GENSHI_REMOVES_BLANK_LINES = _genshi_removes_blank_lines()
class CfgGenshiGenerator(CfgGenerator):
@@ -94,8 +72,6 @@ class CfgGenshiGenerator(CfgGenerator):
def __init__(self, fname, spec, encoding):
CfgGenerator.__init__(self, fname, spec, encoding)
- if not HAS_GENSHI:
- raise PluginExecutionError("Genshi is not available")
self.template = None
self.loader = self.__loader_cls__(max_cache_size=0)
__init__.__doc__ = CfgGenerator.__init__.__doc__
@@ -106,12 +82,12 @@ class CfgGenshiGenerator(CfgGenerator):
self.name)
fname = entry.get('realname', entry.get('name'))
- stream = \
- self.template.generate(name=fname,
- metadata=metadata,
- path=self.name,
- source_path=self.name,
- repo=SETUP['repo']).filter(removecomment)
+ stream = self.template.generate(
+ name=fname,
+ metadata=metadata,
+ path=self.name,
+ source_path=self.name,
+ repo=self.setup['repo']).filter(removecomment)
try:
try:
return stream.render('text', encoding=self.encoding,
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
index 3b6fc8fa0..886b3993b 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgInfoXML.py
@@ -1,6 +1,6 @@
""" Handle info.xml files """
-from Bcfg2.Server.Plugin import PluginExecutionError, InfoXML
+from Bcfg2.Server.Plugin import InfoXML
from Bcfg2.Server.Plugins.Cfg import CfgInfo
@@ -17,21 +17,9 @@ class CfgInfoXML(CfgInfo):
__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:
- raise PluginExecutionError("Failed to set metadata for file %s" %
- entry.get('name'))
- self._set_info(entry, mdata['Info'][None])
+ self.infoxml.BindEntry(entry, metadata)
bind_info_to_entry.__doc__ = CfgInfo.bind_info_to_entry.__doc__
def handle_event(self, event):
self.infoxml.HandleEvent()
handle_event.__doc__ = CfgInfo.handle_event.__doc__
-
- def _set_info(self, entry, info):
- CfgInfo._set_info(self, entry, info)
- if '__children__' in info:
- for child in info['__children__']:
- entry.append(child)
- _set_info.__doc__ = CfgInfo._set_info.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgLegacyInfo.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgLegacyInfo.py
deleted file mode 100644
index 5122d9aa1..000000000
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgLegacyInfo.py
+++ /dev/null
@@ -1,46 +0,0 @@
-""" Handle info and :info files """
-
-import Bcfg2.Server.Plugin
-from Bcfg2.Server.Plugins.Cfg import CfgInfo
-
-
-class CfgLegacyInfo(CfgInfo):
- """ CfgLegacyInfo handles :file:`info` and :file:`:info` files for
- :ref:`server-plugins-generators-cfg` """
-
- #: Handle :file:`info` and :file:`:info`
- __basenames__ = ['info', ':info']
-
- #: CfgLegacyInfo is deprecated. Use
- #: :class:`Bcfg2.Server.Plugins.Cfg.CfgInfoXML.CfgInfoXML` instead.
- deprecated = True
-
- def __init__(self, path):
- CfgInfo.__init__(self, path)
- self.path = path
-
- #: The set of info metadata stored in the file
- self.metadata = None
- __init__.__doc__ = CfgInfo.__init__.__doc__
-
- def bind_info_to_entry(self, entry, metadata):
- self._set_info(entry, self.metadata)
- bind_info_to_entry.__doc__ = CfgInfo.bind_info_to_entry.__doc__
-
- def handle_event(self, event):
- if event.code2str() == 'deleted':
- return
- self.metadata = dict()
- for line in open(self.path).readlines():
- match = Bcfg2.Server.Plugin.INFO_REGEX.match(line)
- if not match:
- self.logger.warning("Failed to parse line in %s: %s" %
- (event.filename, line))
- continue
- else:
- for key, value in list(match.groupdict().items()):
- if value:
- self.metadata[key] = value
- if ('mode' in self.metadata and len(self.metadata['mode']) == 3):
- self.metadata['mode'] = "0%s" % self.metadata['mode']
- handle_event.__doc__ = CfgInfo.handle_event.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
index aaeb65cd6..7ebce192c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
@@ -3,12 +3,13 @@
import os
import shutil
import tempfile
-import subprocess
-from Bcfg2.Server.Plugin import PluginExecutionError, StructFile
-from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError, SETUP
+from Bcfg2.Utils import Executor
+from Bcfg2.Options import get_option_parser
+from Bcfg2.Server.Plugin import StructFile
+from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError
from Bcfg2.Server.Plugins.Cfg.CfgPublicKeyCreator import CfgPublicKeyCreator
try:
- import Bcfg2.Encryption
+ from Bcfg2.Server.Encryption import get_passphrases, ssl_encrypt
HAS_CRYPTO = True
except ImportError:
HAS_CRYPTO = False
@@ -31,26 +32,27 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
pubkey_path = os.path.dirname(self.name) + ".pub"
pubkey_name = os.path.join(pubkey_path, os.path.basename(pubkey_path))
self.pubkey_creator = CfgPublicKeyCreator(pubkey_name)
+ self.setup = get_option_parser()
+ self.cmd = Executor()
__init__.__doc__ = CfgCreator.__init__.__doc__
@property
def category(self):
""" The name of the metadata category that generated keys are
specific to """
- if (SETUP.cfp.has_section("sshkeys") and
- SETUP.cfp.has_option("sshkeys", "category")):
- return SETUP.cfp.get("sshkeys", "category")
+ if (self.setup.cfp.has_section("sshkeys") and
+ self.setup.cfp.has_option("sshkeys", "category")):
+ return self.setup.cfp.get("sshkeys", "category")
return None
@property
def passphrase(self):
""" The passphrase used to encrypt private keys """
if (HAS_CRYPTO and
- SETUP.cfp.has_section("sshkeys") and
- SETUP.cfp.has_option("sshkeys", "passphrase")):
- return Bcfg2.Encryption.get_passphrases(SETUP)[SETUP.cfp.get(
- "sshkeys",
- "passphrase")]
+ self.setup.cfp.has_section("sshkeys") and
+ self.setup.cfp.has_option("sshkeys", "passphrase")):
+ return get_passphrases()[self.setup.cfp.get("sshkeys",
+ "passphrase")]
return None
def handle_event(self, event):
@@ -103,18 +105,17 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
log_cmd.append("''")
self.debug_log("Cfg: Generating new SSH key pair: %s" %
" ".join(log_cmd))
- proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- err = proc.communicate()[1]
- if proc.wait():
+ result = self.cmd.run(cmd)
+ if not result.success:
raise CfgCreationError("Cfg: Failed to generate SSH key pair "
"at %s for %s: %s" %
- (filename, metadata.hostname, err))
- elif err:
+ (filename, metadata.hostname,
+ result.error))
+ elif result.stderr:
self.logger.warning("Cfg: Generated SSH key pair at %s for %s "
"with errors: %s" % (filename,
metadata.hostname,
- err))
+ result.stderr))
return filename
except:
shutil.rmtree(tempdir)
@@ -196,10 +197,7 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
privkey = open(filename).read()
if HAS_CRYPTO and self.passphrase:
self.debug_log("Cfg: Encrypting key data at %s" % filename)
- privkey = Bcfg2.Encryption.ssl_encrypt(
- privkey,
- self.passphrase,
- algorithm=Bcfg2.Encryption.get_algorithm(SETUP))
+ privkey = ssl_encrypt(privkey, self.passphrase)
specificity['ext'] = '.crypt'
self.write_data(privkey, **specificity)
@@ -211,50 +209,3 @@ class CfgPrivateKeyCreator(CfgCreator, StructFile):
finally:
shutil.rmtree(os.path.dirname(filename))
# pylint: enable=W0221
-
- def Index(self):
- StructFile.Index(self)
- if HAS_CRYPTO:
- strict = self.xdata.get(
- "decrypt",
- SETUP.cfp.get(Bcfg2.Encryption.CFG_SECTION, "decrypt",
- default="strict")) == "strict"
- for el in self.xdata.xpath("//*[@encrypted]"):
- try:
- el.text = self._decrypt(el).encode('ascii',
- 'xmlcharrefreplace')
- except UnicodeDecodeError:
- self.logger.info("Cfg: Decrypted %s to gibberish, skipping"
- % el.tag)
- except Bcfg2.Encryption.EVPError:
- msg = "Cfg: Failed to decrypt %s element in %s" % \
- (el.tag, self.name)
- if strict:
- raise PluginExecutionError(msg)
- else:
- self.logger.warning(msg)
- Index.__doc__ = StructFile.Index.__doc__
-
- def _decrypt(self, element):
- """ Decrypt a single encrypted element """
- if not element.text or not element.text.strip():
- return
- passes = Bcfg2.Encryption.get_passphrases(SETUP)
- try:
- passphrase = passes[element.get("encrypted")]
- try:
- return Bcfg2.Encryption.ssl_decrypt(
- element.text,
- passphrase,
- algorithm=Bcfg2.Encryption.get_algorithm(SETUP))
- except Bcfg2.Encryption.EVPError:
- # error is raised below
- pass
- except KeyError:
- # bruteforce_decrypt raises an EVPError with a sensible
- # error message, so we just let it propagate up the stack
- return Bcfg2.Encryption.bruteforce_decrypt(
- element.text,
- passphrases=passes.values(),
- algorithm=Bcfg2.Encryption.get_algorithm(SETUP))
- raise Bcfg2.Encryption.EVPError("Failed to decrypt")
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py
index 6be438462..4c61e338e 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py
@@ -23,6 +23,9 @@ class CfgPublicKeyCreator(CfgCreator, StructFile):
#: Handle XML specifications of private keys
__basenames__ = ['pubkey.xml']
+ #: No text content on any tags, so encryption support disabled
+ encryption = False
+
def __init__(self, fname):
CfgCreator.__init__(self, fname)
StructFile.__init__(self, fname)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index ec3ba222c..2301de725 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -16,17 +16,6 @@ from Bcfg2.Compat import u_str, unicode, b64encode, walk_packages, \
any, oct_mode
# pylint: enable=W0622
-#: SETUP contains a reference to the
-#: :class:`Bcfg2.Options.OptionParser` created by the Bcfg2 core for
-#: parsing command-line and config file options.
-#: :class:`Bcfg2.Server.Plugins.Cfg.Cfg` stores it in a module global
-#: so that the handler objects can access it, because there is no other
-#: facility for passing a setup object from a
-#: :class:`Bcfg2.Server.Plugin.helpers.GroupSpool` to its
-#: :class:`Bcfg2.Server.Plugin.helpers.EntrySet` objects and thence to
-#: the EntrySet children.
-SETUP = None
-
#: CFG is a reference to the :class:`Bcfg2.Server.Plugins.Cfg.Cfg`
#: plugin object created by the Bcfg2 core. This is provided so that
#: the handler objects can access it as necessary, since the existing
@@ -86,6 +75,7 @@ class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
encoding)
Bcfg2.Server.Plugin.Debuggable.__init__(self)
self.encoding = encoding
+ self.setup = Bcfg2.Options.get_option_parser()
__init__.__doc__ = Bcfg2.Server.Plugin.SpecificData.__init__.__doc__ + \
"""
.. -----
@@ -228,10 +218,7 @@ class CfgFilter(CfgBaseFileMatcher):
class CfgInfo(CfgBaseFileMatcher):
""" CfgInfo handlers provide metadata (owner, group, paranoid,
- etc.) for a file entry.
-
- .. private-include: _set_info
- """
+ etc.) for a file entry. """
#: Whether or not the files handled by this handler are permitted
#: to have specificity indicators in their filenames -- e.g.,
@@ -261,20 +248,6 @@ class CfgInfo(CfgBaseFileMatcher):
"""
raise NotImplementedError
- def _set_info(self, entry, info):
- """ Helper function to assign a dict of info attributes to an
- entry object. ``entry`` is modified in-place.
-
- :param entry: The abstract entry to bind the info to
- :type entry: lxml.etree._Element
- :param info: A dict of attribute: value pairs
- :type info: dict
- :returns: None
- """
- for key, value in list(info.items()):
- if not key.startswith("__"):
- entry.attrib[key] = value
-
class CfgVerifier(CfgBaseFileMatcher):
""" CfgVerifier handlers validate entry data once it has been
@@ -317,9 +290,6 @@ class CfgCreator(CfgBaseFileMatcher):
#: file, and are thus not specific
__specific__ = False
- #: The CfgCreator interface is experimental at this time
- experimental = True
-
def __init__(self, fname):
"""
:param name: The full path to the file
@@ -432,22 +402,15 @@ class CfgDefaultInfo(CfgInfo):
""" :class:`Bcfg2.Server.Plugins.Cfg.Cfg` handler that supplies a
default set of file metadata """
- def __init__(self, defaults):
+ def __init__(self):
CfgInfo.__init__(self, '')
- self.defaults = defaults
__init__.__doc__ = CfgInfo.__init__.__doc__.split(".. -----")[0]
- def bind_info_to_entry(self, entry, metadata):
- self._set_info(entry, self.defaults)
+ def bind_info_to_entry(self, entry, _):
+ for key, value in Bcfg2.Server.Plugin.default_path_metadata().items():
+ entry.attrib[key] = value
bind_info_to_entry.__doc__ = CfgInfo.bind_info_to_entry.__doc__
-#: A :class:`CfgDefaultInfo` object instantiated with
-#: :attr:`Bcfg2.Server.Plugin.helper.DEFAULT_FILE_METADATA` as its
-#: default metadata. This is used to set a default file metadata set
-#: on an entry before a "real" :class:`CfgInfo` handler applies its
-#: metadata to the entry.
-DEFAULT_INFO = CfgDefaultInfo(Bcfg2.Server.Plugin.DEFAULT_FILE_METADATA)
-
class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
Bcfg2.Server.Plugin.Debuggable):
@@ -460,6 +423,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
Bcfg2.Server.Plugin.Debuggable.__init__(self)
self.specific = None
self._handlers = None
+ self.setup = Bcfg2.Options.get_option_parser()
__init__.__doc__ = Bcfg2.Server.Plugin.EntrySet.__doc__
def set_debug(self, debug):
@@ -585,7 +549,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
for fltr in self.get_handlers(metadata, CfgFilter):
data = fltr.modify_data(entry, metadata, data)
- if SETUP['validate']:
+ if self.setup['validate']:
try:
self._validate_data(entry, metadata, data)
except CfgVerificationError:
@@ -654,7 +618,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
:returns: None
"""
info_handlers = self.get_handlers(metadata, CfgInfo)
- DEFAULT_INFO.bind_info_to_entry(entry, metadata)
+ CfgDefaultInfo().bind_info_to_entry(entry, metadata)
if len(info_handlers) > 1:
self.logger.error("More than one info supplier found for %s: %s" %
(entry.get("name"), info_handlers))
@@ -701,13 +665,6 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
# raises an appropriate exception
return self._create_data(entry, metadata)
- if entry.get('mode').lower() == 'inherit':
- # use on-disk permissions
- self.logger.warning("Cfg: %s: Use of mode='inherit' is deprecated"
- % entry.get("name"))
- fname = os.path.join(self.path, generator.name)
- entry.set('mode',
- oct_mode(stat.S_IMODE(os.stat(fname).st_mode)))
try:
return generator.get_data(entry, metadata)
except:
@@ -796,13 +753,6 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
badattr = [attr for attr in ['owner', 'group', 'mode']
if attr in new_entry]
if badattr:
- # check for info files and inform user of their removal
- for ifile in ['info', ':info']:
- info = os.path.join(self.path, ifile)
- if os.path.exists(info):
- self.logger.info("Removing %s and replacing with info.xml"
- % info)
- os.remove(info)
metadata_updates = {}
metadata_updates.update(self.metadata)
for attr in badattr:
@@ -832,16 +782,16 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
es_child_cls = Bcfg2.Server.Plugin.SpecificData
def __init__(self, core, datastore):
- global SETUP, CFG # pylint: disable=W0603
+ global CFG # pylint: disable=W0603
Bcfg2.Server.Plugin.GroupSpool.__init__(self, core, datastore)
Bcfg2.Server.Plugin.PullTarget.__init__(self)
CFG = self
- SETUP = core.setup
- if 'validate' not in SETUP:
- SETUP.add_option('validate', Bcfg2.Options.CFG_VALIDATION)
- SETUP.reparse()
+ setup = Bcfg2.Options.get_option_parser()
+ if 'validate' not in setup:
+ setup.add_option('validate', Bcfg2.Options.CFG_VALIDATION)
+ setup.reparse()
__init__.__doc__ = Bcfg2.Server.Plugin.GroupSpool.__init__.__doc__
def has_generator(self, entry, metadata):
@@ -882,26 +832,11 @@ class CfgLint(Bcfg2.Server.Lint.ServerPlugin):
def Run(self):
for basename, entry in list(self.core.plugins['Cfg'].entries.items()):
- self.check_delta(basename, entry)
self.check_pubkey(basename, entry)
@classmethod
def Errors(cls):
- return {"cat-file-used": "warning",
- "diff-file-used": "warning",
- "no-pubkey-xml": "warning"}
-
- def check_delta(self, basename, entry):
- """ check that no .cat or .diff files are in use """
- for fname, handler in entry.entries.items():
- path = handler.name
- if self.HandlesFile(path) and isinstance(handler, CfgFilter):
- extension = fname.split(".")[-1]
- if extension in ["cat", "diff"]:
- self.LintError("%s-file-used" % extension,
- "%s file used on %s: %s" % (extension,
- basename,
- fname))
+ return {"no-pubkey-xml": "warning"}
def check_pubkey(self, basename, entry):
""" check that privkey.xml files have corresponding pubkey.xml