summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/server/plugins/generators/cfg.txt47
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py25
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py16
3 files changed, 58 insertions, 30 deletions
diff --git a/doc/server/plugins/generators/cfg.txt b/doc/server/plugins/generators/cfg.txt
index f31923866..e3768a3ba 100644
--- a/doc/server/plugins/generators/cfg.txt
+++ b/doc/server/plugins/generators/cfg.txt
@@ -596,6 +596,11 @@ Deltas
cat file functionality. ``bcfg2-lint`` checks for deltas and
warns about them.
+.. warning::
+
+ In Bcfg2 1.3, deltas **do not** work with `SSH key or
+ authorized_keys generation <SSH Keys>`_.
+
Bcfg2 has finer grained control over how to deliver configuration
files to a host. Let's say we have a Group named file-server. Members
of this group need the exact same ``/etc/motd`` as all other hosts except
@@ -632,23 +637,35 @@ server and we have the following configuration files::
motd.G01_web-server
motd.G01_mail-server.cat
motd.G02_file-server.cat
+ motd.H_bar.example.com
motd.H_foo.example.com.cat
-If our machine **isn't** *foo.example.com* then here's what would happen:
-
-Bcfg2 would choose ``motd.G01_web-server`` as the base file. It is
-the most specific base file for this host. Bcfg2 would apply the
-``motd.G01_mail-server.cat`` delta to the ``motd.G01_web-server``
-base file. It is the least specific delta. Bcfg2 would then apply the
-``motd.G02_file-server.cat`` delta to the result of the delta before
-it. If our machine **is** *foo.example.com* then here's what would happen:
-
-Bcfg2 would choose ``motd.G01_web-server`` as the base file. It
-is the most specific base file for this host. Bcfg2 would apply the
-``motd.H_foo.example.com.cat`` delta to the ``motd.G01_web-server`` base
-file. The reason the other deltas aren't applied to *foo.example.com*
-is because a **.H_** delta is more specific than a **.G##_** delta. Bcfg2
-applies all the deltas at the most specific level.
+If our machine isn't *foo.example.com* or *bar.example.com*, but
+is a web server, then Bcfg2 would choose ``motd.G01_web-server`` as
+the base file. It is the most specific base file for this host. Bcfg2
+would apply the ``motd.G01_mail-server.cat`` delta to the
+``motd.G01_web-server`` base file. It is the least specific
+delta. Bcfg2 would then apply the ``motd.G02_file-server.cat`` delta
+to the result of the delta before it.
+
+If our machine is *foo.example.com* and a web server, then Bcfg2 would
+choose ``motd.G01_web-server`` as the base file. It is the most
+specific base file for this host. Bcfg2 would apply the
+``motd.H_foo.example.com.cat`` delta to the ``motd.G01_web-server``
+base file. The reason the other deltas aren't applied to
+*foo.example.com* is because a **.H_** delta is more specific than a
+**.G##_** delta. Bcfg2 applies all the deltas at the most specific
+level.
+
+If our machine is *bar.example.com*, then Bcfg2 would chose
+``motd.H_foo.example.com`` as the base file because it is the most
+specific base file for this host. Regardless of the groups
+*bar.example.com* is a member of, **no cat files** would be applied,
+because only cat files as specific or more specific than the base file
+are applied. (In other words, if a group-specific base file is
+selected, only group- or host-specific cat files can be applied; if a
+host-specific base file is selected, only host-specific cat files can
+be applied.)
.. _server-plugins-generators-cfg-validation:
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 842202a9c..154cd5e63 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -10,7 +10,6 @@ import lxml.etree
import Bcfg2.Options
import Bcfg2.Server.Plugin
import Bcfg2.Server.Lint
-from itertools import chain
from Bcfg2.Server.Plugin import PluginExecutionError
# pylint: disable=W0622
from Bcfg2.Compat import u_str, unicode, b64encode, walk_packages, \
@@ -582,10 +581,18 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
def bind_entry(self, entry, metadata):
self.bind_info_to_entry(entry, metadata)
- data = self._generate_data(entry, metadata)
-
- for fltr in self.get_handlers(metadata, CfgFilter):
- data = fltr.modify_data(entry, metadata, data)
+ data, generator = self._generate_data(entry, metadata)
+
+ if generator is not None:
+ # apply no filters if the data was created by a CfgCreator
+ for fltr in self.get_handlers(metadata, CfgFilter):
+ if fltr.specific <= generator.specific:
+ # only apply filters that are as specific or more
+ # specific than the generator used for this entry.
+ # Note that specificity comparison is backwards in
+ # this sense, since it's designed to sort from
+ # most specific to least specific.
+ data = fltr.modify_data(entry, metadata, data)
if SETUP['validate']:
try:
@@ -694,7 +701,9 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
:type entry: lxml.etree._Element
:param metadata: The client metadata to generate data for
:type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
- :returns: string - the data for the entry
+ :returns: tuple of (string, generator) - the data for the
+ entry and the generator used to generate it (or
+ None, if data was created)
"""
try:
generator = self.best_matching(metadata,
@@ -703,7 +712,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
except PluginExecutionError:
# if no creators or generators exist, _create_data()
# raises an appropriate exception
- return self._create_data(entry, metadata)
+ return (self._create_data(entry, metadata), None)
if entry.get('mode').lower() == 'inherit':
# use on-disk permissions
@@ -713,7 +722,7 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
entry.set('mode',
oct_mode(stat.S_IMODE(os.stat(fname).st_mode)))
try:
- return generator.get_data(entry, metadata)
+ return (generator.get_data(entry, metadata), generator)
except:
msg = "Cfg: Error rendering %s: %s" % (entry.get("name"),
sys.exc_info()[1])
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
index f838030e2..ea3549c1b 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/Test_init.py
@@ -6,7 +6,7 @@ import Bcfg2.Options
from Bcfg2.Compat import walk_packages
from mock import Mock, MagicMock, patch
from Bcfg2.Server.Plugins.Cfg import *
-from Bcfg2.Server.Plugin import PluginExecutionError
+from Bcfg2.Server.Plugin import PluginExecutionError, Specificity
# add all parent testsuite directories to sys.path to allow (most)
# relative imports in python 2.4
@@ -461,7 +461,7 @@ class TestCfgEntrySet(TestEntrySet):
metadata = Mock()
# test basic entry, no validation, no filters, etc.
- eset._generate_data.return_value = "data"
+ eset._generate_data.return_value = ("data", None)
eset.get_handlers.return_value = []
bound = eset.bind_entry(entry, metadata)
eset.bind_info_to_entry.assert_called_with(entry, metadata)
@@ -474,7 +474,7 @@ class TestCfgEntrySet(TestEntrySet):
# test empty entry
entry = reset()
- eset._generate_data.return_value = ""
+ eset._generate_data.return_value = ("", None)
bound = eset.bind_entry(entry, metadata)
eset.bind_info_to_entry.assert_called_with(entry, metadata)
eset._generate_data.assert_called_with(entry, metadata)
@@ -485,7 +485,9 @@ class TestCfgEntrySet(TestEntrySet):
# test filters
entry = reset()
- eset._generate_data.return_value = "initial data"
+ generator = Mock()
+ generator.specific = Specificity(all=True)
+ eset._generate_data.return_value = ("initial data", generator)
filters = [Mock(), Mock()]
filters[0].modify_data.return_value = "modified data"
filters[1].modify_data.return_value = "final data"
@@ -507,7 +509,7 @@ class TestCfgEntrySet(TestEntrySet):
entry.set("encoding", "base64")
mock_b64encode.return_value = "base64 data"
eset.get_handlers.return_value = []
- eset._generate_data.return_value = "data"
+ eset._generate_data.return_value = ("data", None)
bound = eset.bind_entry(entry, metadata)
eset.bind_info_to_entry.assert_called_with(entry, metadata)
eset._generate_data.assert_called_with(entry, metadata)
@@ -691,7 +693,7 @@ class TestCfgEntrySet(TestEntrySet):
eset._create_data.reset_mock()
# test success
- self.assertEqual(eset._generate_data(entry, metadata),
+ self.assertEqual(eset._generate_data(entry, metadata)[0],
"data")
eset.get_handlers.assert_called_with(metadata, CfgGenerator)
eset.best_matching.assert_called_with(metadata,
@@ -708,7 +710,7 @@ class TestCfgEntrySet(TestEntrySet):
reset()
eset.best_matching.side_effect = PluginExecutionError
self.assertEqual(eset._generate_data(entry, metadata),
- eset._create_data.return_value)
+ (eset._create_data.return_value, None))
eset.get_handlers.assert_called_with(metadata, CfgGenerator)
eset.best_matching.assert_called_with(metadata,
eset.get_handlers.return_value)