diff options
author | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2014-02-23 18:43:30 -0500 |
---|---|---|
committer | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2014-02-26 22:31:02 -0500 |
commit | d4566b5285bb87cbc3e1a69d091f5404b5acfc0d (patch) | |
tree | c7390b0a6c4ee204300ec06ffb0a30f9f632812a | |
parent | 9eb8eb8ebad15ddb0ea2b5735452dbd5d6c41a91 (diff) | |
download | bcfg2-d4566b5285bb87cbc3e1a69d091f5404b5acfc0d.tar.gz bcfg2-d4566b5285bb87cbc3e1a69d091f5404b5acfc0d.tar.bz2 bcfg2-d4566b5285bb87cbc3e1a69d091f5404b5acfc0d.zip |
Bundler: added path globbing
-rw-r--r-- | schemas/pathentry.xsd | 29 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Bundler.py | 44 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py | 3 |
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., ``<Path + name="/etc/foo.conf"/>``) or a glob that matches a set of + paths (e.g., ``<Path glob="/etc/foo/*"/>``). Path + globbing may not work for some dynamically handled Path + entries, for instance :ref:`Packages client configs + <generating-client-configs>`. + </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 |