From 9bec4d6bbab599bee72256c7e09fe214cb849a1b Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Tue, 5 Feb 2013 12:13:20 -0500 Subject: abstracted similar digit range classes in POSIXUsers/GroupPatterns into Bcfg2.Utils --- src/lib/Bcfg2/Client/Tools/POSIXUsers.py | 45 ++------------- src/lib/Bcfg2/Server/Plugins/GroupPatterns.py | 23 +------- src/lib/Bcfg2/Utils.py | 67 ++++++++++++++++++++++ .../Testlib/TestClient/TestTools/TestPOSIXUsers.py | 38 ++---------- .../TestServer/TestPlugins/TestGroupPatterns.py | 21 ------- testsuite/Testsrc/Testlib/TestUtils.py | 48 ++++++++++++++++ 6 files changed, 127 insertions(+), 115 deletions(-) create mode 100644 src/lib/Bcfg2/Utils.py create mode 100644 testsuite/Testsrc/Testlib/TestUtils.py diff --git a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py index 53ceb4e3c..3248cef9c 100644 --- a/src/lib/Bcfg2/Client/Tools/POSIXUsers.py +++ b/src/lib/Bcfg2/Client/Tools/POSIXUsers.py @@ -5,44 +5,9 @@ import sys import pwd import grp import subprocess +from Bcfg2.Utils import PackedDigitRange import Bcfg2.Client.XML import Bcfg2.Client.Tools -from Bcfg2.Compat import any # pylint: disable=W0622 - - -class IDRangeSet(object): - """ Representation of a set of integer ranges. Used to describe - which UID/GID ranges are managed or unmanaged. """ - - def __init__(self, *ranges): - self.ranges = [] - self.ints = [] - self.str = ",".join(str(r) for r in ranges) - for item in ranges: - item = str(item).strip() - if item.endswith("-"): - self.ranges.append((int(item[:-1]), None)) - elif '-' in str(item): - self.ranges.append(tuple(int(x) for x in item.split('-'))) - else: - self.ints.append(int(item)) - - def __contains__(self, other): - other = int(other) - if other in self.ints: - return True - return any((end is None and other >= start) or - (end is not None and other >= start and other <= end) - for start, end in self.ranges) - - def __repr__(self): - return "%s:%s" % (self.__class__.__name__, str(self)) - - def __str__(self): - return "[%s]" % self.str - - def __len__(self): - return len(self.ranges) + len(self.ints) class ExecutionError(Exception): @@ -123,16 +88,16 @@ class POSIXUsers(Bcfg2.Client.Tools.Tool): self._blacklist = dict(POSIXUser=None, POSIXGroup=None) if self.setup['posix_uid_whitelist']: self._whitelist['POSIXUser'] = \ - IDRangeSet(*self.setup['posix_uid_whitelist']) + PackedDigitRange(*self.setup['posix_uid_whitelist']) else: self._blacklist['POSIXUser'] = \ - IDRangeSet(*self.setup['posix_uid_blacklist']) + PackedDigitRange(*self.setup['posix_uid_blacklist']) if self.setup['posix_gid_whitelist']: self._whitelist['POSIXGroup'] = \ - IDRangeSet(*self.setup['posix_gid_whitelist']) + PackedDigitRange(*self.setup['posix_gid_whitelist']) else: self._blacklist['POSIXGroup'] = \ - IDRangeSet(*self.setup['posix_gid_blacklist']) + PackedDigitRange(*self.setup['posix_gid_blacklist']) @property def existing(self): diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py index 42d860b89..99f01201b 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py @@ -6,28 +6,7 @@ import sys import logging import Bcfg2.Server.Lint import Bcfg2.Server.Plugin -from Bcfg2.Compat import any # pylint: disable=W0622 - - -class PackedDigitRange(object): - """ Helper object for NameRange entries """ - - def __init__(self, digit_range): - self.sparse = list() - self.ranges = list() - for item in digit_range.split(','): - if '-' in item: - self.ranges.append(tuple([int(x) for x in item.split('-')])) - else: - self.sparse.append(int(item)) - - def includes(self, other): - """ return True if other is included in this range """ - iother = int(other) - if iother in self.sparse: - return True - return any(iother in range(start, end + 1) - for start, end in self.ranges) +from Bcfg2.Utils import PackedDigitRange class PatternMap(object): diff --git a/src/lib/Bcfg2/Utils.py b/src/lib/Bcfg2/Utils.py new file mode 100644 index 000000000..ba17e1a63 --- /dev/null +++ b/src/lib/Bcfg2/Utils.py @@ -0,0 +1,67 @@ +""" Miscellaneous useful utility functions, classes, etc., that are +used by both client and server. Stuff that doesn't fit anywhere +else. """ + +from Bcfg2.Compat import any # pylint: disable=W0622 + + +class PackedDigitRange(object): + """ Representation of a set of integer ranges. A range is + described by a comma-delimited string of integers and ranges, + e.g.:: + + 1,10-12,15-20 + + Ranges are inclusive on both bounds, and may include 0. Negative + numbers are not supported.""" + + def __init__(self, *ranges): + """ May be instantiated in one of two ways:: + + PackedDigitRange() + + Or:: + + PackedDigitRange([, [, ...]]) + + E.g., both of the following are valid:: + + PackedDigitRange("1-5,7, 10-12") + PackedDigitRange("1-5", 7, "10-12") + """ + self.ranges = [] + self.ints = [] + self.str = ",".join(str(r) for r in ranges) + if len(ranges) == 1 and "," in ranges[0]: + ranges = ranges[0].split(",") + for item in ranges: + item = str(item).strip() + if item.endswith("-"): + self.ranges.append((int(item[:-1]), None)) + elif '-' in str(item): + self.ranges.append(tuple(int(x) for x in item.split('-'))) + else: + self.ints.append(int(item)) + + def includes(self, other): + """ Return True if ``other`` is included in this range. + Functionally equivalent to ``other in range``, which should be + used instead. """ + return other in self + + def __contains__(self, other): + other = int(other) + if other in self.ints: + return True + return any((end is None and other >= start) or + (end is not None and other >= start and other <= end) + for start, end in self.ranges) + + def __repr__(self): + return "%s:%s" % (self.__class__.__name__, str(self)) + + def __str__(self): + return "[%s]" % self.str + + def __len__(self): + return sum(r[1] - r[0] + 1 for r in self.ranges) + len(self.ints) diff --git a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py index bcf6cf133..8ab279a50 100644 --- a/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py +++ b/testsuite/Testsrc/Testlib/TestClient/TestTools/TestPOSIXUsers.py @@ -6,6 +6,7 @@ import subprocess from mock import Mock, MagicMock, patch import Bcfg2.Client.Tools from Bcfg2.Client.Tools.POSIXUsers import * +from Bcfg2.Utils import PackedDigitRange # add all parent testsuite directories to sys.path to allow (most) # relative imports in python 2.4 @@ -19,33 +20,6 @@ while path != "/": from common import * -class TestIDRangeSet(Bcfg2TestCase): - def test_ranges(self): - # test cases. tuples of (ranges, included numbers, excluded - # numbers) - # tuples of (range description, numbers that are included, - # numebrs that are excluded) - tests = [(["0-3"], ["0", 1, "2", 3], [4]), - (["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]), - (["1-5", 7, "9-11"], [1, 3, 5, 7, 9, 11], [0, 6, 8, 12]), - (["852-855", "321-497", 763], [852, 855, 321, 400, 497, 763], - [851, 320, 766, 999]), - (["0-"], [0, 1, 100, 100000], []), - ([1, "5-10", "1000-"], [1, 5, 10, 1000, 10000000], - [4, 11, 999])] - for ranges, inc, exc in tests: - rng = IDRangeSet(*ranges) - for test in inc: - self.assertIn(test, rng) - for test in exc: - self.assertNotIn(test, rng) - - class TestExecutor(Bcfg2TestCase): test_obj = Executor @@ -177,19 +151,19 @@ class TestPOSIXUsers(Bcfg2TestCase): def test__in_managed_range(self): users = self.get_obj() - users._whitelist = dict(POSIXGroup=IDRangeSet("1-10")) - users._blacklist = dict(POSIXGroup=IDRangeSet("8-100")) + users._whitelist = dict(POSIXGroup=PackedDigitRange("1-10")) + users._blacklist = dict(POSIXGroup=PackedDigitRange("8-100")) self.assertTrue(users._in_managed_range("POSIXGroup", "9")) users._whitelist = dict(POSIXGroup=None) - users._blacklist = dict(POSIXGroup=IDRangeSet("8-100")) + users._blacklist = dict(POSIXGroup=PackedDigitRange("8-100")) self.assertFalse(users._in_managed_range("POSIXGroup", "9")) users._whitelist = dict(POSIXGroup=None) - users._blacklist = dict(POSIXGroup=IDRangeSet("100-")) + users._blacklist = dict(POSIXGroup=PackedDigitRange("100-")) self.assertTrue(users._in_managed_range("POSIXGroup", "9")) - users._whitelist = dict(POSIXGroup=IDRangeSet("1-10")) + users._whitelist = dict(POSIXGroup=PackedDigitRange("1-10")) users._blacklist = dict(POSIXGroup=None) self.assertFalse(users._in_managed_range("POSIXGroup", "25")) diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py index a7a6b3d6e..a9346156c 100644 --- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py +++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestGroupPatterns.py @@ -18,27 +18,6 @@ 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]), - ("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]), - ("1-5,7,9-11", [1, 3, 5, 7, 9, 11], [0, 6, 8, 12]), - ("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 """ diff --git a/testsuite/Testsrc/Testlib/TestUtils.py b/testsuite/Testsrc/Testlib/TestUtils.py new file mode 100644 index 000000000..349d6cd40 --- /dev/null +++ b/testsuite/Testsrc/Testlib/TestUtils.py @@ -0,0 +1,48 @@ +import os +import sys +import copy +import lxml.etree +import subprocess +from mock import Mock, MagicMock, patch +from Bcfg2.Utils 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 * + + +class TestPackedDigitRange(Bcfg2TestCase): + def test_ranges(self): + # test cases. tuples of (ranges, included numbers, excluded + # numbers) + # tuples of (range description, numbers that are included, + # numebrs that are excluded) + tests = [(["0-3"], ["0", 1, "2", 3], [4]), + (["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]), + (["1-5", 7, "9-11"], [1, 3, 5, 7, 9, 11], [0, 6, 8, 12]), + (["1-5, 7,9-11 "], [1, 3, 5, 7, 9, 11], [0, 6, 8, 12]), + (["852-855", "321-497", 763], [852, 855, 321, 400, 497, 763], + [851, 320, 766, 999]), + (["0-"], [0, 1, 100, 100000], []), + ([1, "5-10", "1000-"], [1, 5, 10, 1000, 10000000], + [4, 11, 999])] + for ranges, inc, exc in tests: + rng = PackedDigitRange(*ranges) + for test in inc: + self.assertIn(test, rng) + self.assertTrue(rng.includes(test)) + for test in exc: + self.assertNotIn(test, rng) + self.assertFalse(rng.includes(test)) -- cgit v1.2.3-1-g7c22