summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2013-01-03 13:40:24 -0600
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2013-01-03 13:40:24 -0600
commit14406cc14a4d832fe83df5da27937051e41dd093 (patch)
tree1fb429513bc5483251412af8251aa24517bcbb68 /src/lib
parent10326a34dd813b88c6c8816115e91977a93a1f10 (diff)
downloadbcfg2-14406cc14a4d832fe83df5da27937051e41dd093.tar.gz
bcfg2-14406cc14a4d832fe83df5da27937051e41dd093.tar.bz2
bcfg2-14406cc14a4d832fe83df5da27937051e41dd093.zip
Cfg: Added feature to provide generation of SSH keys, authorized_keys file
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Bcfg2/Server/Lint/Validate.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py101
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py258
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py63
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py95
5 files changed, 501 insertions, 20 deletions
diff --git a/src/lib/Bcfg2/Server/Lint/Validate.py b/src/lib/Bcfg2/Server/Lint/Validate.py
index e93338ae4..37bc230d1 100644
--- a/src/lib/Bcfg2/Server/Lint/Validate.py
+++ b/src/lib/Bcfg2/Server/Lint/Validate.py
@@ -18,6 +18,10 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
{"Metadata/groups.xml": "metadata.xsd",
"Metadata/clients.xml": "clients.xsd",
"Cfg/**/info.xml": "info.xsd",
+ "Cfg/**/privkey.xml": "privkey.xsd",
+ "Cfg/**/pubkey.xml": "pubkey.xsd",
+ "Cfg/**/authorizedkeys.xml": "authorizedkeys.xsd",
+ "Cfg/**/authorized_keys.xml": "authorizedkeys.xsd",
"SSHbase/**/info.xml": "info.xsd",
"SSLCA/**/info.xml": "info.xsd",
"TGenshi/**/info.xml": "info.xsd",
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py
new file mode 100644
index 000000000..824d01023
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgAuthorizedKeysGenerator.py
@@ -0,0 +1,101 @@
+""" The CfgAuthorizedKeysGenerator generates ``authorized_keys`` files
+based on an XML specification of which SSH keypairs should granted
+access. """
+
+import lxml.etree
+from Bcfg2.Server.Plugin import StructFile, PluginExecutionError
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP, CFG
+from Bcfg2.Server.Plugins.Metadata import ClientMetadata
+
+
+class CfgAuthorizedKeysGenerator(CfgGenerator, StructFile):
+ """ The CfgAuthorizedKeysGenerator generates authorized_keys files
+ based on an XML specification of which SSH keypairs should granted
+ access. """
+
+ #: Different configurations for different clients/groups can be
+ #: handled with Client and Group tags within authorizedkeys.xml
+ __specific__ = False
+
+ #: 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)
+ self.cache = dict()
+ self.core = CFG.core
+ __init__.__doc__ = CfgGenerator.__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")
+ return None
+
+ def handle_event(self, event):
+ CfgGenerator.handle_event(self, event)
+ StructFile.HandleEvent(self, event)
+ self.cache = dict()
+ handle_event.__doc__ = CfgGenerator.handle_event.__doc__
+
+ def get_data(self, entry, metadata):
+ spec = self.XMLMatch(metadata)
+ rv = []
+ for allow in spec.findall("Allow"):
+ params = ''
+ if allow.find("Params") is not None:
+ params = ",".join("=".join(p)
+ for p in allow.find("Params").attrib.items())
+
+ pubkey_name = allow.get("from")
+ if pubkey_name:
+ host = allow.get("host")
+ group = allow.get("group")
+ if host:
+ key_md = self.core.build_metadata(host)
+ elif group:
+ key_md = ClientMetadata("dummy", group, [group], [],
+ set(), set(), dict(), None,
+ None, None, None)
+ elif (self.category and
+ not metadata.group_in_category(self.category)):
+ self.logger.warning("Cfg: %s ignoring Allow from %s: "
+ "No group in category %s" %
+ (metadata.hostname, pubkey_name,
+ self.category))
+ continue
+ else:
+ key_md = metadata
+
+ key_entry = lxml.etree.Element("Path", name=pubkey_name)
+ try:
+ self.core.Bind(key_entry, key_md)
+ except PluginExecutionError:
+ self.logger.info("Cfg: %s skipping Allow from %s: "
+ "No key found" % (metadata.hostname,
+ pubkey_name))
+ continue
+ if not key_entry.text:
+ self.logger.warning("Cfg: %s skipping Allow from %s: "
+ "Empty public key" %
+ (metadata.hostname, pubkey_name))
+ continue
+ pubkey = key_entry.text
+ elif allow.text:
+ pubkey = allow.text.strip()
+ else:
+ self.logger.warning("Cfg: %s ignoring empty Allow tag: %s" %
+ (metadata.hostname,
+ lxml.etree.tostring(allow)))
+ continue
+ rv.append(" ".join([params, pubkey]).strip())
+ return "\n".join(rv)
+ get_data.__doc__ = CfgGenerator.get_data.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
new file mode 100644
index 000000000..bb54c6faa
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPrivateKeyCreator.py
@@ -0,0 +1,258 @@
+""" The CfgPrivateKeyCreator creates SSH keys on the fly. """
+
+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.Server.Plugins.Cfg.CfgPublicKeyCreator import CfgPublicKeyCreator
+try:
+ import Bcfg2.Encryption
+ HAS_CRYPTO = True
+except ImportError:
+ HAS_CRYPTO = False
+
+
+class CfgPrivateKeyCreator(CfgCreator, StructFile):
+ """The CfgPrivateKeyCreator creates SSH keys on the fly. """
+
+ #: Different configurations for different clients/groups can be
+ #: handled with Client and Group tags within privkey.xml
+ __specific__ = False
+
+ #: Handle XML specifications of private keys
+ __basenames__ = ['privkey.xml']
+
+ def __init__(self, fname):
+ CfgCreator.__init__(self, fname)
+ StructFile.__init__(self, fname)
+
+ 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)
+ __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")
+ 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")]
+ return None
+
+ def handle_event(self, event):
+ CfgCreator.handle_event(self, event)
+ StructFile.HandleEvent(self, event)
+ handle_event.__doc__ = CfgCreator.handle_event.__doc__
+
+ def _gen_keypair(self, metadata, spec=None):
+ """ Generate a keypair according to the given client medata
+ and key specification.
+
+ :param metadata: The client metadata to generate keys for
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :param spec: The key specification to follow when creating the
+ keys. This should be an XML document that only
+ contains key specification data that applies to
+ the given client metadata, and may be obtained by
+ doing ``self.XMLMatch(metadata)``
+ :type spec: lxml.etree._Element
+ :returns: None
+ """
+ if spec is None:
+ spec = self.XMLMatch(metadata)
+
+ # set key parameters
+ ktype = "rsa"
+ bits = None
+ params = spec.find("Params")
+ if params is not None:
+ bits = params.get("bits")
+ ktype = params.get("type", ktype)
+ try:
+ passphrase = spec.find("Passphrase").text
+ except AttributeError:
+ passphrase = ''
+ tempdir = tempfile.mkdtemp()
+ try:
+ filename = os.path.join(tempdir, "privkey")
+
+ # generate key pair
+ cmd = ["ssh-keygen", "-f", filename, "-t", ktype]
+ if bits:
+ cmd.extend(["-b", bits])
+ cmd.append("-N")
+ log_cmd = cmd[:]
+ cmd.append(passphrase)
+ if passphrase:
+ log_cmd.append("******")
+ else:
+ 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():
+ raise CfgCreationError("Cfg: Failed to generate SSH key pair "
+ "at %s for %s: %s" %
+ (filename, metadata.hostname, err))
+ elif err:
+ self.logger.warning("Cfg: Generated SSH key pair at %s for %s "
+ "with errors: %s" % (filename,
+ metadata.hostname,
+ err))
+ return filename
+ except:
+ shutil.rmtree(tempdir)
+ raise
+
+ def get_specificity(self, metadata, spec=None):
+ """ Get config settings for key generation specificity
+ (per-host or per-group).
+
+ :param metadata: The client metadata to create data for
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :param spec: The key specification to follow when creating the
+ keys. This should be an XML document that only
+ contains key specification data that applies to
+ the given client metadata, and may be obtained by
+ doing ``self.XMLMatch(metadata)``
+ :type spec: lxml.etree._Element
+ :returns: dict - A dict of specificity arguments suitable for
+ passing to
+ :func:`Bcfg2.Server.Plugins.Cfg.CfgCreator.write_data`
+ or
+ :func:`Bcfg2.Server.Plugins.Cfg.CfgCreator.get_filename`
+ """
+ if spec is None:
+ spec = self.XMLMatch(metadata)
+ category = spec.get("category", self.category)
+ print("category=%s" % category)
+ if category is None:
+ per_host_default = "true"
+ else:
+ per_host_default = "false"
+ per_host = spec.get("perhost", per_host_default).lower() == "true"
+
+ specificity = dict(host=metadata.hostname)
+ if category and not per_host:
+ group = metadata.group_in_category(category)
+ if group:
+ specificity = dict(group=group,
+ prio=int(spec.get("priority", 50)))
+ else:
+ self.logger.info("Cfg: %s has no group in category %s, "
+ "creating host-specific key" %
+ (metadata.hostname, category))
+ return specificity
+
+ # pylint: disable=W0221
+ def create_data(self, entry, metadata, return_pair=False):
+ """ Create data for the given entry on the given client
+
+ :param entry: The abstract entry to create data for. This
+ will not be modified
+ :type entry: lxml.etree._Element
+ :param metadata: The client metadata to create data for
+ :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
+ :param return_pair: Return a tuple of ``(public key, private
+ key)`` instead of just the private key.
+ This is used by
+ :class:`Bcfg2.Server.Plugins.Cfg.CfgPublicKeyCreator.CfgPublicKeyCreator`
+ to create public keys as requested.
+ :type return_pair: bool
+ :returns: string - The private key data
+ :returns: tuple - Tuple of ``(public key, private key)``, if
+ ``return_pair`` is set to True
+ """
+ spec = self.XMLMatch(metadata)
+ specificity = self.get_specificity(metadata, spec)
+ filename = self._gen_keypair(metadata, spec)
+
+ try:
+ # write the public key, stripping the comment and
+ # replacing it with a comment that specifies the filename.
+ kdata = open(filename + ".pub").read().split()[:2]
+ kdata.append(self.pubkey_creator.get_filename(**specificity))
+ pubkey = " ".join(kdata) + "\n"
+ self.pubkey_creator.write_data(pubkey, **specificity)
+
+ # encrypt the private key, write to the proper place, and
+ # return it
+ 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))
+ specificity['ext'] = '.crypt'
+
+ self.write_data(privkey, **specificity)
+
+ if return_pair:
+ return (pubkey, privkey)
+ else:
+ return privkey
+ finally:
+ shutil.rmtree(os.path.dirname(filename))
+ # pylint: enable=W0221
+
+ def Index(self):
+ StructFile.Index(self)
+ if HAS_CRYPTO:
+ strict = SETUP.cfp.get("sshkeys", "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.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
new file mode 100644
index 000000000..6be438462
--- /dev/null
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgPublicKeyCreator.py
@@ -0,0 +1,63 @@
+""" The CfgPublicKeyCreator invokes
+:class:`Bcfg2.Server.Plugins.Cfg.CfgPrivateKeyCreator.CfgPrivateKeyCreator`
+to create SSH keys on the fly. """
+
+import lxml.etree
+from Bcfg2.Server.Plugin import StructFile, PluginExecutionError
+from Bcfg2.Server.Plugins.Cfg import CfgCreator, CfgCreationError, CFG
+
+
+class CfgPublicKeyCreator(CfgCreator, StructFile):
+ """ .. currentmodule:: Bcfg2.Server.Plugins.Cfg
+
+ The CfgPublicKeyCreator creates SSH public keys on the fly. It is
+ invoked by :class:`CfgPrivateKeyCreator.CfgPrivateKeyCreator` to
+ handle the creation of the public key, and can also call
+ :class:`CfgPrivateKeyCreator.CfgPrivateKeyCreator` to trigger the
+ creation of a keypair when a public key is created. """
+
+ #: Different configurations for different clients/groups can be
+ #: handled with Client and Group tags within privkey.xml
+ __specific__ = False
+
+ #: Handle XML specifications of private keys
+ __basenames__ = ['pubkey.xml']
+
+ def __init__(self, fname):
+ CfgCreator.__init__(self, fname)
+ StructFile.__init__(self, fname)
+ self.cfg = CFG
+ __init__.__doc__ = CfgCreator.__init__.__doc__
+
+ def create_data(self, entry, metadata):
+ if entry.get("name").endswith(".pub"):
+ privkey = entry.get("name")[:-4]
+ else:
+ raise CfgCreationError("Cfg: Could not determine private key for "
+ "%s: Filename does not end in .pub" %
+ entry.get("name"))
+
+ if privkey not in self.cfg.entries:
+ raise CfgCreationError("Cfg: Could not find Cfg entry for %s "
+ "(private key for %s)" % (privkey,
+ self.name))
+ eset = self.cfg.entries[privkey]
+ try:
+ creator = eset.best_matching(metadata,
+ eset.get_handlers(metadata,
+ CfgCreator))
+ except PluginExecutionError:
+ raise CfgCreationError("Cfg: No privkey.xml defined for %s "
+ "(private key for %s)" % (privkey,
+ self.name))
+
+ privkey_entry = lxml.etree.Element("Path", name=privkey)
+ pubkey = creator.create_data(privkey_entry, metadata,
+ return_pair=True)[0]
+ return pubkey
+ create_data.__doc__ = CfgCreator.create_data.__doc__
+
+ def handle_event(self, event):
+ CfgCreator.handle_event(self, event)
+ StructFile.HandleEvent(self, event)
+ handle_event.__doc__ = CfgCreator.handle_event.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 2466d68a2..ea4a4263b 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -12,7 +12,7 @@ import Bcfg2.Server.Plugin
import Bcfg2.Server.Lint
from Bcfg2.Server.Plugin import PluginExecutionError
# pylint: disable=W0622
-from Bcfg2.Compat import u_str, unicode, b64encode, b64decode, walk_packages, \
+from Bcfg2.Compat import u_str, unicode, b64encode, walk_packages, \
any, oct_mode
# pylint: enable=W0622
@@ -27,6 +27,14 @@ from Bcfg2.Compat import u_str, unicode, b64encode, b64decode, walk_packages, \
#: 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
+#: :class:`Bcfg2.Server.Plugin.helpers.GroupSpool` and
+#: :class:`Bcfg2.Server.Plugin.helpers.EntrySet` classes have no
+#: facility for passing it otherwise.
+CFG = None
+
class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
Bcfg2.Server.Plugin.Debuggable):
@@ -62,8 +70,8 @@ class CfgBaseFileMatcher(Bcfg2.Server.Plugin.SpecificData,
#: if they handle a given event. If this explicit priority is not
#: set, then :class:`CfgPlaintextGenerator.CfgPlaintextGenerator`
#: would match against nearly every other sort of generator file
- #: if it comes first. It's not necessary to set ``__priority`` on
- #: handlers where :attr:`CfgBaseFileMatcher.__specific__` is
+ #: if it comes first. It's not necessary to set ``__priority__``
+ #: on handlers where :attr:`CfgBaseFileMatcher.__specific__` is
#: False, since they don't have a potentially open-ended regex
__priority__ = 0
@@ -304,6 +312,23 @@ class CfgCreator(CfgBaseFileMatcher):
client, writes its data to disk as a static file, and is not
called on subsequent runs by the same client. """
+ #: CfgCreators generally store their configuration in a single XML
+ #: 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
+ :type name: string
+
+ .. -----
+ .. autoattribute:: Bcfg2.Server.Plugins.Cfg.CfgCreator.__specific__
+ """
+ CfgBaseFileMatcher.__init__(self, fname, None, None)
+
def create_data(self, entry, metadata):
""" Create new data for the given entry and write it to disk
using :func:`Bcfg2.Server.Plugins.Cfg.CfgCreator.write_data`.
@@ -312,11 +337,43 @@ class CfgCreator(CfgBaseFileMatcher):
:type entry: lxml.etree._Element
:param metadata: The client metadata to create data for.
:type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
- :returns: string - the contents of the entry
+ :returns: string - The contents of the entry
+ :raises: :exc:`Bcfg2.Server.Plugins.Cfg.CfgCreationError`
"""
raise NotImplementedError
- def write_data(self, data, host=None, group=None, prio=0):
+ def get_filename(self, host=None, group=None, prio=0, ext=''):
+ """ Get the filename where the new data will be written. If
+ ``host`` is given, it will be host-specific. It will be
+ group-specific if ``group`` and ``prio`` are given. If
+ neither ``host`` nor ``group`` is given, the filename will be
+ non-specific.
+
+ :param host: The file applies to the given host
+ :type host: bool
+ :param group: The file applies to the given group
+ :type group: string
+ :param prio: The file has the given priority relative to other
+ objects that also apply to the same group.
+ ``group`` must also be specified.
+ :type prio: int
+ :param ext: An extension to add after the specificity (e.g.,
+ '.crypt', to signal that an encrypted file has
+ been created)
+ :type prio: string
+ :returns: string - the filename
+ """
+ basefilename = \
+ os.path.join(os.path.dirname(self.name),
+ os.path.basename(os.path.dirname(self.name)))
+ if group:
+ return "%s.G%02d_%s%s" % (basefilename, prio, group, ext)
+ elif host:
+ return "%s.H_%s%s" % (basefilename, host, ext)
+ else:
+ return "%s%s" % (basefilename, ext)
+
+ def write_data(self, data, host=None, group=None, prio=0, ext=''):
""" Write the new data to disk. If ``host`` is given, it is
written as a host-specific file, or as a group-specific file
if ``group`` and ``prio`` are given. If neither ``host`` nor
@@ -332,19 +389,14 @@ class CfgCreator(CfgBaseFileMatcher):
objects that also apply to the same group.
``group`` must also be specified.
:type prio: int
+ :param ext: An extension to add after the specificity (e.g.,
+ '.crypt', to signal that an encrypted file has
+ been created)
+ :type prio: string
:returns: None
:raises: :exc:`Bcfg2.Server.Plugins.Cfg.CfgCreationError`
"""
- basefilename = \
- os.path.join(os.path.dirname(self.name),
- os.path.basename(os.path.dirname(self.name)))
- if group:
- fileloc = "%s.G%02d_%s" % (basefilename, prio, group)
- elif host:
- fileloc = "%s.H_%s" % (basefilename, host)
- else:
- fileloc = basefilename
-
+ fileloc = self.get_filename(host=host, group=group, prio=prio, ext=ext)
self.debug_log("%s: Writing new file %s" % (self.name, fileloc))
try:
os.makedirs(os.path.dirname(fileloc))
@@ -369,8 +421,9 @@ class CfgVerificationError(Exception):
class CfgCreationError(Exception):
- """ Raised by :class:`Bcfg2.Server.Plugins.Cfg.CfgCreator` when
- various stages of data creation fail """
+ """ Raised by
+ :func:`Bcfg2.Server.Plugins.Cfg.CfgCreator.create_data` when data
+ creation fails """
pass
@@ -607,8 +660,8 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
:type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
:returns: string - the data for the entry
"""
- creator = self.best_matching(metadata, self.get_handlers(metadata,
- CfgCreator))
+ creator = self.best_matching(metadata,
+ self.get_handlers(metadata, CfgCreator))
try:
return creator.create_data(entry, metadata)
@@ -766,10 +819,12 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
es_child_cls = Bcfg2.Server.Plugin.SpecificData
def __init__(self, core, datastore):
- global SETUP # pylint: disable=W0603
+ global SETUP, 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)