summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--schemas/pathentry.xsd29
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bundler.py44
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py3
3 files changed, 57 insertions, 19 deletions
diff --git a/schemas/pathentry.xsd b/schemas/pathentry.xsd
index e5d2ef6af..44c86f9bc 100644
--- a/schemas/pathentry.xsd
+++ b/schemas/pathentry.xsd
@@ -12,7 +12,34 @@
schemaLocation="genshi.xsd"/>
<xsd:complexType name='PathEntry'>
- <xsd:attribute type='xsd:string' name='name' use='required'/>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract description of a path to be installed. This can
+ either be a single explicit path (e.g., ``&lt;Path
+ name="/etc/foo.conf"/&gt;``) or a glob that matches a set of
+ paths (e.g., ``&lt;Path glob="/etc/foo/*"/&gt;``). Path
+ globbing may not work for some dynamically handled Path
+ entries, for instance :ref:`Packages client configs
+ &lt;generating-client-configs&gt;`.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute type='xsd:string' name='name'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Install the single named path. Either ``name`` or
+ :xml:attribute:`PathEntry:glob` must be specified.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
+ <xsd:attribute type="xsd:string" name="glob">
+ <xsd:annotation>
+ <xsd:documentation>
+ Install all Cfg entries matching the given glob. Either
+ ``glob`` or :xml:attribute:`PathEntry:name` must be
+ specified.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute type='xsd:string' name='altsrc' use='optional'/>
<xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py
index 8b9330c9b..41ee57b6d 100644
--- a/src/lib/Bcfg2/Server/Plugins/Bundler.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py
@@ -4,31 +4,30 @@ import os
import re
import sys
import copy
-import Bcfg2.Server
-import Bcfg2.Server.Plugin
+import fnmatch
+import lxml.etree
+from Bcfg2.Server.Plugin import StructFile, Plugin, Structure, \
+ StructureValidator, XMLDirectoryBacked, Generator
from genshi.template import TemplateError
-class BundleFile(Bcfg2.Server.Plugin.StructFile):
+class BundleFile(StructFile):
""" Representation of a bundle XML file """
bundle_name_re = re.compile(r'^(?P<name>.*)\.(xml|genshi)$')
def __init__(self, filename, should_monitor=False):
- Bcfg2.Server.Plugin.StructFile.__init__(self, filename,
- should_monitor=should_monitor)
+ StructFile.__init__(self, filename, should_monitor=should_monitor)
if self.name.endswith(".genshi"):
self.logger.warning("Bundler: %s: Bundle filenames ending with "
".genshi are deprecated; add the Genshi XML "
"namespace to a .xml bundle instead" %
self.name)
- __init__.__doc__ = Bcfg2.Server.Plugin.StructFile.__init__.__doc__
def Index(self):
- Bcfg2.Server.Plugin.StructFile.Index(self)
+ StructFile.Index(self)
if self.xdata.get("name"):
self.logger.warning("Bundler: %s: Explicitly specifying bundle "
"names is deprecated" % self.name)
- Index.__doc__ = Bcfg2.Server.Plugin.StructFile.Index.__doc__
@property
def bundle_name(self):
@@ -37,9 +36,10 @@ class BundleFile(Bcfg2.Server.Plugin.StructFile):
os.path.basename(self.name)).group("name")
-class Bundler(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Structure,
- Bcfg2.Server.Plugin.XMLDirectoryBacked):
+class Bundler(Plugin,
+ Structure,
+ StructureValidator,
+ XMLDirectoryBacked):
""" The bundler creates dependent clauses based on the
bundle/translation scheme from Bcfg1. """
__author__ = 'bcfg-dev@mcs.anl.gov'
@@ -47,18 +47,30 @@ class Bundler(Bcfg2.Server.Plugin.Plugin,
patterns = re.compile(r'^.*\.(?:xml|genshi)$')
def __init__(self, core):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core)
- Bcfg2.Server.Plugin.Structure.__init__(self)
- Bcfg2.Server.Plugin.XMLDirectoryBacked.__init__(self, self.data)
+ Plugin.__init__(self, core)
+ Structure.__init__(self)
+ StructureValidator.__init__(self)
+ XMLDirectoryBacked.__init__(self, self.data)
#: Bundles by bundle name, rather than filename
self.bundles = dict()
def HandleEvent(self, event):
- Bcfg2.Server.Plugin.XMLDirectoryBacked.HandleEvent(self, event)
-
+ XMLDirectoryBacked.HandleEvent(self, event)
self.bundles = dict([(b.bundle_name, b)
for b in self.entries.values()])
+ def validate_structures(self, metadata, structures):
+ """ Translate <Path glob='...'/> entries into <Path name='...'/>
+ entries """
+ for struct in structures:
+ for pathglob in struct.xpath("//Path[@glob]"):
+ for plugin in self.core.plugins_by_type(Generator):
+ for match in fnmatch.filter(plugin.Entries['Path'].keys(),
+ pathglob.get("glob")):
+ lxml.etree.SubElement(pathglob.getparent(),
+ "Path", name=match)
+ pathglob.getparent().remove(pathglob)
+
def BuildStructures(self, metadata):
bundleset = []
bundles = copy.copy(metadata.bundles)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index d2b982349..5dc3d98eb 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -872,8 +872,7 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool,
""" The Cfg plugin provides a repository to describe configuration
file contents for clients. In its simplest form, the Cfg repository is
just a directory tree modeled off of the directory tree on your client
- machines.
- """
+ machines. """
__author__ = 'bcfg-dev@mcs.anl.gov'
es_cls = CfgEntrySet
es_child_cls = Bcfg2.Server.Plugin.SpecificData