summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSol Jerome <sol.jerome@gmail.com>2015-09-01 10:23:10 -0500
committerSol Jerome <sol.jerome@gmail.com>2015-09-01 10:23:10 -0500
commit75466a831a92ab136a9cc2b6b875d20f0b00a889 (patch)
treee83728ee5a1f67e072e4d6fba0b7c71ecb352253
parent824879eb85a1065e842930485778a594b914c77f (diff)
parent3259a1611edf97241b3a2d1bd585185907fe4e9c (diff)
downloadbcfg2-75466a831a92ab136a9cc2b6b875d20f0b00a889.tar.gz
bcfg2-75466a831a92ab136a9cc2b6b875d20f0b00a889.tar.bz2
bcfg2-75466a831a92ab136a9cc2b6b875d20f0b00a889.zip
Merge branch 'rules-replace_name' of https://github.com/AlexanderS/bcfg2
-rw-r--r--doc/releases/1.4.0pre2.txt1
-rw-r--r--doc/server/plugins/generators/rules.txt20
-rw-r--r--doc/server/plugins/structures/defaults.txt7
-rw-r--r--src/lib/Bcfg2/Server/Plugin/helpers.py14
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Defaults.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Rules.py27
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py9
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestRules.py18
-rw-r--r--testsuite/common.py19
9 files changed, 116 insertions, 11 deletions
diff --git a/doc/releases/1.4.0pre2.txt b/doc/releases/1.4.0pre2.txt
index 1dcdf237b..1ed2ae70f 100644
--- a/doc/releases/1.4.0pre2.txt
+++ b/doc/releases/1.4.0pre2.txt
@@ -19,6 +19,7 @@ environments.
* NagiosGen: Add bundles to configuration
* HomeBrew: Initial add of plugin
+* Rules/Defaults: Add possibility to use name of entry in attributes
backwards-incompatible user-facing changes
------------------------------------------
diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt
index e4f47c2bf..7aeec6990 100644
--- a/doc/server/plugins/generators/rules.txt
+++ b/doc/server/plugins/generators/rules.txt
@@ -512,3 +512,23 @@ you'd have to explicitly specify ``<Service name="bcfg2.*".../>``.
Note that only one Rule can apply to any abstract entry, so you cannot
specify multiple regexes to match the same rule.
+
+Replacing the name of the Entry in Attributes
+=============================================
+
+If you are using regular expressions to match the abstract configuration
+entries, you may need the concrete name of the entry in some attributes.
+To use this feature, you have to enable it. It is only useful, if used
+together with regex matching. ::
+
+ [rules]
+ regex = yes
+ replace_name = yes
+
+You now can write something like that in your xml file:
+
+.. code-block:: xml
+
+ <POSIXUser name='.*' home='/somewhere/%{name}'/>
+
+``%{name}`` will be correctly replaced with the username for each POSIXUser.
diff --git a/doc/server/plugins/structures/defaults.txt b/doc/server/plugins/structures/defaults.txt
index 58b9feddb..9d37b8e64 100644
--- a/doc/server/plugins/structures/defaults.txt
+++ b/doc/server/plugins/structures/defaults.txt
@@ -29,3 +29,10 @@ on Fedora 15 and the ``chkconfig`` tool on Fedora 14, you could do::
If you were to specify a ``type`` attribute for a Service entry in
Rules (or a ``type`` attribute for a BoundService entry in Bundler),
that would take precendence over the default.
+
+Like :ref:`server-plugins-generators-rules`, Defaults can also replace
+``%{name}`` in attributes with the real name of the entry. To enable this,
+add the following setting to ``bcfg2.conf``::
+
+ [defaults]
+ replace_name = yes
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py
index 3b62a3217..6b521dfd6 100644
--- a/src/lib/Bcfg2/Server/Plugin/helpers.py
+++ b/src/lib/Bcfg2/Server/Plugin/helpers.py
@@ -1064,6 +1064,20 @@ class PrioDir(Plugin, Generator, XMLDirectoryBacked):
data = candidate
break
+ self._apply(entry, data)
+
+ def _apply(self, entry, data):
+ """ Apply all available values from data onto entry. This
+ sets the available attributes (for all attribues unset in
+ the entry), adds all children and copies the text from data
+ to entry.
+
+ :param entry: The entry to apply the changes
+ :type entry: lxml.etree._Element
+ :param data: The entry to get the data from
+ :type data: lxml.etree._Element
+ """
+
if data.text is not None and data.text.strip() != '':
entry.text = data.text
for item in data.getchildren():
diff --git a/src/lib/Bcfg2/Server/Plugins/Defaults.py b/src/lib/Bcfg2/Server/Plugins/Defaults.py
index 79e2ca0e2..2242e3825 100644
--- a/src/lib/Bcfg2/Server/Plugins/Defaults.py
+++ b/src/lib/Bcfg2/Server/Plugins/Defaults.py
@@ -1,5 +1,6 @@
"""This generator provides rule-based entry mappings."""
+import Bcfg2.Options
import Bcfg2.Server.Plugin
import Bcfg2.Server.Plugins.Rules
@@ -9,7 +10,10 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
"""Set default attributes on bound entries"""
__author__ = 'bcfg-dev@mcs.anl.gov'
- options = Bcfg2.Server.Plugin.PrioDir.options
+ options = Bcfg2.Server.Plugin.PrioDir.options + [
+ Bcfg2.Options.BooleanOption(
+ cf=("defaults", "replace_name"), dest="defaults_replace_name",
+ help="Replace %{name} in attributes with name of target entry")]
# Rules is a Generator that happens to implement all of the
# functionality we want, so we overload it, but Defaults should
@@ -41,3 +45,9 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
def _regex_enabled(self):
""" Defaults depends on regex matching, so force it enabled """
return True
+
+ @property
+ def _replace_name_enabled(self):
+ """ Return True if the replace_name feature is enabled,
+ False otherwise """
+ return Bcfg2.Options.setup.defaults_replace_name
diff --git a/src/lib/Bcfg2/Server/Plugins/Rules.py b/src/lib/Bcfg2/Server/Plugins/Rules.py
index a3f682ed6..cf659251c 100644
--- a/src/lib/Bcfg2/Server/Plugins/Rules.py
+++ b/src/lib/Bcfg2/Server/Plugins/Rules.py
@@ -1,10 +1,17 @@
"""This generator provides rule-based entry mappings."""
+import copy
import re
+import string
import Bcfg2.Options
import Bcfg2.Server.Plugin
+class NameTemplate(string.Template):
+ """Simple subclass of string.Template with a custom delimiter."""
+ delimiter = '%'
+
+
class Rules(Bcfg2.Server.Plugin.PrioDir):
"""This is a generator that handles service assignments."""
__author__ = 'bcfg-dev@mcs.anl.gov'
@@ -12,7 +19,10 @@ class Rules(Bcfg2.Server.Plugin.PrioDir):
options = Bcfg2.Server.Plugin.PrioDir.options + [
Bcfg2.Options.BooleanOption(
cf=("rules", "regex"), dest="rules_regex",
- help="Allow regular expressions in Rules")]
+ help="Allow regular expressions in Rules"),
+ Bcfg2.Options.BooleanOption(
+ cf=("rules", "replace_name"), dest="rules_replace_name",
+ help="Replace %{name} in attributes with name of target entry")]
def __init__(self, core):
Bcfg2.Server.Plugin.PrioDir.__init__(self, core)
@@ -46,7 +56,22 @@ class Rules(Bcfg2.Server.Plugin.PrioDir):
return True
return False
+ def _apply(self, entry, data):
+ if self._replace_name_enabled:
+ data = copy.deepcopy(data)
+ for key, val in list(data.attrib.items()):
+ data.attrib[key] = NameTemplate(val).safe_substitute(
+ name=entry.get('name'))
+
+ Bcfg2.Server.Plugin.PrioDir._apply(self, entry, data)
+
@property
def _regex_enabled(self):
""" Return True if rules regexes are enabled, False otherwise """
return Bcfg2.Options.setup.rules_regex
+
+ @property
+ def _replace_name_enabled(self):
+ """ Return True if the replace_name feature is enabled,
+ False otherwise """
+ return Bcfg2.Options.setup.rules_replace_name
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
index 9b4a6af88..3c660099e 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestDefaults.py
@@ -22,6 +22,10 @@ from Testinterfaces import TestGoalValidator
class TestDefaults(TestRules, TestGoalValidator):
test_obj = Defaults
+ def setUp(self):
+ TestRules.setUp(self)
+ set_setup_default("defaults_replace_name", True)
+
def get_obj(self, *args, **kwargs):
return TestRules.get_obj(self, *args, **kwargs)
@@ -91,3 +95,8 @@ class TestDefaults(TestRules, TestGoalValidator):
def test_regex(self):
self._do_test('regex')
+
+ def test_replace_name(self):
+ Bcfg2.Options.setup.defaults_replace_name = True
+ self._do_test('replace_name')
+ Bcfg2.Options.setup.defaults_replace_name = False
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestRules.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestRules.py
index 45f3671e8..88b334edf 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestRules.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestRules.py
@@ -32,6 +32,7 @@ class TestRules(TestPrioDir):
group=lxml.etree.Element("SEPort", name="6789/tcp"),
children=lxml.etree.Element("Path", name="/etc/child-entries"),
regex=lxml.etree.Element("Package", name="regex"),
+ replace_name=lxml.etree.Element("POSIXUser", name="regex"),
slash=lxml.etree.Element("Path", name="/etc/trailing/slash"),
no_slash=lxml.etree.Element("Path", name="/etc/no/trailing/slash/"))
@@ -53,6 +54,8 @@ class TestRules(TestPrioDir):
group="root", mode="0775"),
regex=lxml.etree.Element("Package", name="regex", type="yum",
version="any"),
+ replace_name=lxml.etree.Element("POSIXUser", name="regex",
+ home="/foobar%{bar}/regex"),
slash=lxml.etree.Element("Path", name="/etc/trailing/slash",
type="directory", owner="root", group="root",
mode="0600"),
@@ -70,6 +73,8 @@ class TestRules(TestPrioDir):
in_file = copy.deepcopy(concrete)
in_file['regex'].set("name", ".*")
+ in_file['replace_name'].set("home", "/foobar%{bar}/%{name}")
+ in_file['replace_name'].set("name", ".*")
in_file['slash'].set("name", "/etc/trailing/slash/")
in_file['no_slash'].set("name", "/etc/no/trailing/slash")
@@ -91,6 +96,7 @@ class TestRules(TestPrioDir):
rules3 = lxml.etree.Element("Rules", priority="10")
rules3.append(in_file['duplicate'])
rules3.append(in_file['regex'])
+ rules3.append(in_file['replace_name'])
rules3.append(in_file['slash'])
rules = {"rules1.xml": rules1, "rules2.xml": rules2, "rules3.xml": rules3}
@@ -99,6 +105,7 @@ class TestRules(TestPrioDir):
TestPrioDir.setUp(self)
set_setup_default("lax_decryption", True)
set_setup_default("rules_regex", False)
+ set_setup_default("rules_replace_name", False)
def get_child(self, name):
""" Turn one of the XML documents in `rules` into a child
@@ -169,6 +176,17 @@ class TestRules(TestPrioDir):
self._do_test('regex')
Bcfg2.Options.setup.rules_regex = False
+ def test_replace_name(self):
+ """ Test that Rules handles replaces name in attribues with regular expressions """
+ Bcfg2.Options.setup.rules_regex = False
+ Bcfg2.Options.setup.rules_replace_name = False
+ self._do_test_failure('replace_name', handles=False)
+ Bcfg2.Options.setup.rules_regex = True
+ Bcfg2.Options.setup.rules_replace_name = True
+ self._do_test('replace_name')
+ Bcfg2.Options.setup.rules_regex = False
+ Bcfg2.Options.setup.rules_replace_name = False
+
def test_slash(self):
""" Test that Rules handles trailing slashes on Path entries """
self._do_test('slash')
diff --git a/testsuite/common.py b/testsuite/common.py
index e53cfdddf..45cfcbf0b 100644
--- a/testsuite/common.py
+++ b/testsuite/common.py
@@ -172,21 +172,22 @@ class Bcfg2TestCase(TestCase):
msg = "XML trees are not equal: %s"
else:
msg += ": %s"
- fullmsg = msg + "\nFirst: %s" % lxml.etree.tostring(el1) + \
+ msg += "\n%s"
+ fullmsg = "First: %s" % lxml.etree.tostring(el1) + \
"\nSecond: %s" % lxml.etree.tostring(el2)
- self.assertEqual(el1.tag, el2.tag, msg=fullmsg % "Tags differ")
+ self.assertEqual(el1.tag, el2.tag, msg=msg % ("Tags differ", fullmsg))
if el1.text is not None and el2.text is not None:
self.assertEqual(el1.text.strip(), el2.text.strip(),
- msg=fullmsg % "Text content differs")
+ msg=msg % ("Text content differs", fullmsg))
else:
self.assertEqual(el1.text, el2.text,
- msg=fullmsg % "Text content differs")
+ msg=msg % ("Text content differs", fullmsg))
self.assertItemsEqual(el1.attrib.items(), el2.attrib.items(),
- msg=fullmsg % "Attributes differ")
+ msg=msg % ("Attributes differ", fullmsg))
self.assertEqual(len(el1.getchildren()),
len(el2.getchildren()),
- msg=fullmsg % "Different numbers of children")
+ msg=msg % ("Different numbers of children", fullmsg))
matched = []
for child1 in el1.getchildren():
for child2 in el2.xpath(child1.tag):
@@ -200,10 +201,10 @@ class Bcfg2TestCase(TestCase):
continue
else:
assert False, \
- fullmsg % ("Element %s is missing from second" %
- lxml.etree.tostring(child1))
+ msg % ("Element %s is missing from second" %
+ lxml.etree.tostring(child1), fullmsg)
self.assertItemsEqual(el2.getchildren(), matched,
- msg=fullmsg % "Second has extra element(s)")
+ msg=msg % ("Second has extra element(s)", fullmsg))
class DBModelTestCase(Bcfg2TestCase):