From bbfed756eaf68437a80609b2c4599004582e4232 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Wed, 30 Mar 2011 17:54:21 +0200 Subject: [PATCH] utils: Add function generating regex for DNS name globbing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intent of this function is to be able to provide a globbing operator or query filters. One should be able to say, for example, something to the effect of βgnt-instance shutdown '*.site'β. Also rename a variable in MatchNameComponent. Signed-off-by: Michael Hanselmann <hansmi@google.com> Reviewed-by: Iustin Pop <iustin@google.com> --- lib/utils/text.py | 42 ++++++++++++++++++++++-- test/ganeti.utils.text_unittest.py | 52 ++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/lib/utils/text.py b/lib/utils/text.py index e55baaa9b..d8e0984a3 100644 --- a/lib/utils/text.py +++ b/lib/utils/text.py @@ -74,11 +74,13 @@ def MatchNameComponent(key, name_list, case_sensitive=True): if not case_sensitive: re_flags |= re.IGNORECASE key = key.upper() - mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags) + + name_re = re.compile(r"^%s(\..*)?$" % re.escape(key), re_flags) + names_filtered = [] string_matches = [] for name in name_list: - if mo.match(name) is not None: + if name_re.match(name) is not None: names_filtered.append(name) if not case_sensitive and key == name.upper(): string_matches.append(name) @@ -87,9 +89,45 @@ def MatchNameComponent(key, name_list, case_sensitive=True): return string_matches[0] if len(names_filtered) == 1: return names_filtered[0] + return None +def _DnsNameGlobHelper(match): + """Helper function for L{DnsNameGlobPattern}. + + Returns regular expression pattern for parts of the pattern. + + """ + text = match.group(0) + + if text == "*": + return "[^.]*" + elif text == "?": + return "[^.]" + else: + return re.escape(text) + + +def DnsNameGlobPattern(pattern): + """Generates regular expression from DNS name globbing pattern. + + A DNS name globbing pattern (e.g. C{*.site}) is converted to a regular + expression. Escape sequences or ranges (e.g. [a-z]) are not supported. + + Matching always starts at the leftmost part. An asterisk (*) matches all + characters except the dot (.) separating DNS name parts. A question mark (?) + matches a single character except the dot (.). + + @type pattern: string + @param pattern: DNS name globbing pattern + @rtype: string + @return: Regular expression + + """ + return r"^%s(\..*)?$" % re.sub(r"\*|\?|[^*?]*", _DnsNameGlobHelper, pattern) + + def FormatUnit(value, units): """Formats an incoming number of MiB with the appropriate unit. diff --git a/test/ganeti.utils.text_unittest.py b/test/ganeti.utils.text_unittest.py index 417a00165..5181db4d3 100755 --- a/test/ganeti.utils.text_unittest.py +++ b/test/ganeti.utils.text_unittest.py @@ -106,6 +106,58 @@ class TestMatchNameComponent(unittest.TestCase): None) +class TestDnsNameGlobPattern(unittest.TestCase): + def setUp(self): + self.names = [ + "node1.example.com", + "node2-0.example.com", + "node2-1.example.com", + "node1.example.net", + "web1.example.com", + "web2.example.com", + "sub.site.example.com", + ] + + def _Test(self, pattern): + re_pat = utils.DnsNameGlobPattern(pattern) + + return filter(re.compile(re_pat).match, self.names) + + def test(self): + for pattern in ["xyz", "node", " ", "example.net", "x*.example.*", + "x*.example.com"]: + self.assertEqual(self._Test(pattern), []) + + for pattern in ["*", "???*"]: + self.assertEqual(self._Test(pattern), self.names) + + self.assertEqual(self._Test("node1.*.net"), ["node1.example.net"]) + self.assertEqual(self._Test("*.example.net"), ["node1.example.net"]) + self.assertEqual(self._Test("web1.example.com"), ["web1.example.com"]) + + for pattern in ["*.*.*.*", "???", "*.site"]: + self.assertEqual(self._Test(pattern), ["sub.site.example.com"]) + + self.assertEqual(self._Test("node1"), [ + "node1.example.com", + "node1.example.net", + ]) + self.assertEqual(self._Test("node?*.example.*"), [ + "node1.example.com", + "node2-0.example.com", + "node2-1.example.com", + "node1.example.net", + ]) + self.assertEqual(self._Test("*-?"), [ + "node2-0.example.com", + "node2-1.example.com", + ]) + self.assertEqual(self._Test("node2-?.example.com"), [ + "node2-0.example.com", + "node2-1.example.com", + ]) + + class TestFormatUnit(unittest.TestCase): """Test case for the FormatUnit function""" -- GitLab