diff options
3 files changed, 199 insertions, 21 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py index 6f0695bc3..2e8c56b4e 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py @@ -118,7 +118,8 @@ class PatternFile(Bcfg2.Server.Plugin.XMLFileBacked): self.patterns.append(PatternMap(None, rng, groups)) except: # pylint: disable=W0702 self.logger.error("GroupPatterns: Failed to initialize " - "pattern %s" % entry.get('pattern')) + "pattern %s: %s" % (entry.text, + sys.exc_info()[1])) def process_patterns(self, hostname): """ return a list of groups that should be added to the given @@ -126,9 +127,9 @@ class PatternFile(Bcfg2.Server.Plugin.XMLFileBacked): ret = [] for pattern in self.patterns: try: - grpname = pattern.process(hostname) - if grpname is not None: - ret.extend(grpname) + grps = pattern.process(hostname) + if grps is not None: + ret.extend(grps) except: # pylint: disable=W0702 self.logger.error("GroupPatterns: Failed to process pattern " "%s for %s" % (pattern.pattern, hostname), diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py index d3e97df8d..559742d00 100644 --- a/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py +++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugin/Testhelpers.py @@ -388,6 +388,11 @@ class TestDirectoryBacked(Bcfg2TestCase): class TestXMLFileBacked(TestFileBacked): test_obj = XMLFileBacked + + # can be set to True (on child test cases where should_monitor is + # always True) or False (on child test cases where should_monitor + # is always False) + should_monitor = None path = os.path.join(datastore, "test", "test1.xml") def get_obj(self, path=None, fam=None, should_monitor=False): @@ -398,14 +403,19 @@ class TestXMLFileBacked(TestFileBacked): def test__init(self): fam = Mock() xfb = self.get_obj() - self.assertIsNone(xfb.fam) + if self.should_monitor is True: + self.assertIsNotNone(xfb.fam) + else: + self.assertIsNone(xfb.fam) - xfb = self.get_obj(fam=fam) - self.assertFalse(fam.AddMonitor.called) + if self.should_monitor is not True: + xfb = self.get_obj(fam=fam) + self.assertFalse(fam.AddMonitor.called) - fam.reset_mock() - xfb = self.get_obj(fam=fam, should_monitor=True) - fam.AddMonitor.assert_called_with(self.path, xfb) + if self.should_monitor is not False: + fam.reset_mock() + xfb = self.get_obj(fam=fam, should_monitor=True) + fam.AddMonitor.assert_called_with(self.path, xfb) @patch("os.path.exists") @patch("lxml.etree.parse") @@ -577,17 +587,20 @@ class TestXMLFileBacked(TestFileBacked): self.assertIn("/test/test2.xml", xfb.extras) fam = Mock() - xfb = self.get_obj(fam=fam) - fam.reset_mock() - xfb.add_monitor("/test/test3.xml") - self.assertFalse(fam.AddMonitor.called) - self.assertIn("/test/test3.xml", xfb.extras) - - fam.reset_mock() - xfb = self.get_obj(fam=fam, should_monitor=True) - xfb.add_monitor("/test/test4.xml") - fam.AddMonitor.assert_called_with("/test/test4.xml", xfb) - self.assertIn("/test/test4.xml", xfb.extras) + if self.should_monitor is not True: + fam.reset_mock() + xfb = self.get_obj(fam=fam) + fam.reset_mock() + xfb.add_monitor("/test/test3.xml") + self.assertFalse(fam.AddMonitor.called) + self.assertIn("/test/test3.xml", xfb.extras) + + if self.should_monitor is not False: + fam.reset_mock() + xfb = self.get_obj(fam=fam, should_monitor=True) + xfb.add_monitor("/test/test4.xml") + fam.AddMonitor.assert_called_with("/test/test4.xml", xfb) + self.assertIn("/test/test4.xml", xfb.extras) class TestStructFile(TestXMLFileBacked): diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py new file mode 100644 index 000000000..84d35ccc5 --- /dev/null +++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py @@ -0,0 +1,164 @@ +import os +import sys +import lxml.etree +import Bcfg2.Server.Plugin +from mock import Mock, MagicMock, patch +from Bcfg2.Server.Plugins.GroupPatterns import * + +# add all parent testsuite directories to sys.path to allow (most) +# relative imports in python 2.4 +path = os.path.dirname(__file__) +while path != "/": + if os.path.basename(path).lower().startswith("test"): + sys.path.append(path) + if os.path.basename(path) == "testsuite": + break + path = os.path.dirname(path) +from common import * +from TestPlugin import TestXMLFileBacked, TestPlugin, TestConnector + + +class TestPackedDigitRange(Bcfg2TestCase): + def test_includes(self): + # tuples of (range description, numbers that are included, + # numebrs that are excluded) + tests = [("1-3", [1, "2", 3], [4, "0"]), + ("1", [1], [0, 2]), + ("10-11", [10, 11], [0, 1]), + ("9-9", [9], [8, 10]), + ("0-100", [0, 10, 99, 100], []), + ("1,3,5", [1, 3, 5], [0, 2, 4, 6]), + ("1-5,7", [1, 3, 5, 7], [0, 6, 8]), + ("852-855,321-497,763", [852, 855, 321, 400, 497, 763], [])] + for rng, inc, exc in tests: + r = PackedDigitRange(rng) + for test in inc: + self.assertTrue(r.includes(test)) + for test in exc: + self.assertFalse(r.includes(test)) + + +class TestPatternMap(Bcfg2TestCase): + def test_ranges(self): + """ test processing NameRange patterns """ + tests = [("foo[[1-5]]", + ["foo1", "foo2", "foo5"], + ["foo", "foo0", "foo10"]), + ("[[10-99]]foo", + ["10foo", "99foo", "25foo"], + ["foo", "1foo", "999foo", "110foo"]), + ("foo[[1,3,5-10]]bar", + ["foo1bar", "foo7bar", "foo10bar"], + ["foo2bar", "foobar", "foo3", "5bar"]), + ("[[9-15]]foo[[16-20]]", + ["9foo18", "13foo17"], + ["8foo21", "12foo21", "8foo18", "16foo16", + "15foo15", "29foo20", "9foo200", "29foo200"])] + + groups = MagicMock() + for rng, inc, exc in tests: + pmap = PatternMap(None, rng, groups) + for test in inc: + self.assertEqual(pmap.process(test), groups) + for test in exc: + self.assertIsNone(pmap.process(test)) + + def test_simple_patterns(self): + """ test processing NamePatterns without backreferences """ + tests = [("foo.*", + ["foo", "foobar", "barfoo", "barfoobar"], + ["bar", "fo0"]), + ("^[A-z]fooo?$", + ["Afoo", "bfooo"], + ["foo", "fooo", "AAfoo", "Afoooo"])] + + groups = ["a", "b", "c"] + for rng, inc, exc in tests: + pmap = PatternMap(rng, None, groups) + for test in inc: + self.assertItemsEqual(pmap.process(test), groups) + for test in exc: + self.assertIsNone(pmap.process(test)) + + def test_backref_patterns(self): + """ test NamePatterns with backreferences """ + tests = [("foo(.*)", ['a', 'a$1', '$1a', '$$', '$a', '$1'], + {"foo": ['a', 'a', 'a', '$$', '$a', ''], + "foooOOo": ['a', 'aoOOo', 'oOOoa', '$$', '$a', 'oOOo'], + "barfoo$1": ['a', 'a$1', '$1a', '$$', '$a', '$1']}), + ("^([a-z])foo(.+)", ['a', 'a$1', '$1a$2', '$1$$2', '$2'], + {"foo": None, + "afooa": ['a', 'aa', 'aaa', 'a$a', 'a'], + "bfoobar": ['a', 'ab', 'babar', 'b$bar', 'bar']})] + + for rng, groups, cases in tests: + pmap = PatternMap(rng, None, groups) + for name, ret in cases.items(): + if ret is None: + self.assertIsNone(pmap.process(name)) + else: + self.assertItemsEqual(pmap.process(name), ret) + + +class TestPatternFile(TestXMLFileBacked): + test_obj = PatternFile + should_monitor = True + + def get_obj(self, path=None, fam=None, core=None, should_monitor=True): + if path is None: + path = self.path + if fam and not core: + core = Mock() + core.fam = fam + elif not core: + core = Mock() + return self.test_obj(path, core=core) + + @patch("Bcfg2.Server.Plugins.GroupPatterns.PatternMap") + def test_Index(self, mock_PatternMap): + TestXMLFileBacked.test_Index(self) + core = Mock() + pf = self.get_obj(core=core) + + pf.data = """ +<GroupPatterns> + <GroupPattern> + <NamePattern>foo.*</NamePattern> + <Group>test1</Group> + <Group>test2</Group> + </GroupPattern> + <GroupPattern> + <NameRange>foo[[1-5]]</NameRange> + <Group>test3</Group> + </GroupPattern> +</GroupPatterns>""" + + core.metadata_cache_mode = 'aggressive' + pf.Index() + core.metadata_cache.expire.assert_called_with() + self.assertItemsEqual(mock_PatternMap.call_args_list, + [call("foo.*", None, ["test1", "test2"]), + call(None, "foo[[1-5]]", ["test3"])]) + + def test_process_patterns(self): + pf = self.get_obj() + pf.patterns = [Mock(), Mock(), Mock()] + pf.patterns[0].process.return_value = ["a", "b"] + pf.patterns[1].process.return_value = None + pf.patterns[2].process.return_value = ["b", "c"] + self.assertItemsEqual(pf.process_patterns("foo.example.com"), + ["a", "b", "b", "c"]) + for pat in pf.patterns: + pat.process.assert_called_with("foo.example.com") + + +class TestGroupPatterns(TestPlugin, TestConnector): + test_obj = GroupPatterns + + def test_get_additional_groups(self): + gp = self.get_obj() + gp.config = Mock() + metadata = Mock() + self.assertEqual(gp.get_additional_groups(metadata), + gp.config.process_patterns.return_value) + gp.config.process_patterns.assert_called_with(metadata.hostname) |