diff --git a/lib/utils.py b/lib/utils.py index c58c2cd27b1bd4559a0a042c1404e17aae1a15e2..186f14ee367ffb8fe62aea753c38523a2a98f26f 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -42,6 +42,7 @@ import fcntl import resource import logging import signal +import string from cStringIO import StringIO @@ -489,7 +490,7 @@ def ReadPidFile(pidfile): return pid -def MatchNameComponent(key, name_list): +def MatchNameComponent(key, name_list, case_sensitive=True): """Try to match a name against a list. This function will try to match a name like test1 against a list @@ -504,6 +505,8 @@ def MatchNameComponent(key, name_list): @param key: the name to be searched @type name_list: list @param name_list: the list of strings against which to search the key + @type case_sensitive: boolean + @param case_sensitive: whether to provide a case-sensitive match @rtype: None or str @return: None if there is no match I{or} if there are multiple matches, @@ -512,11 +515,25 @@ def MatchNameComponent(key, name_list): """ if key in name_list: return key - mo = re.compile("^%s(\..*)?$" % re.escape(key)) - names_filtered = [name for name in name_list if mo.match(name) is not None] - if len(names_filtered) != 1: - return None - return names_filtered[0] + + re_flags = 0 + if not case_sensitive: + re_flags |= re.IGNORECASE + key = string.upper(key) + mo = re.compile("^%s(\..*)?$" % re.escape(key), re_flags) + names_filtered = [] + string_matches = [] + for name in name_list: + if mo.match(name) is not None: + names_filtered.append(name) + if not case_sensitive and key == string.upper(name): + string_matches.append(name) + + if len(string_matches) == 1: + return string_matches[0] + if len(names_filtered) == 1: + return names_filtered[0] + return None class HostInfo: diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 1e8761d1f0091dbb51cae32b6a1e3f46c2bb0785..255f9d6526593495441059d404280f1c821a8bcb 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -336,6 +336,36 @@ class TestMatchNameComponent(unittest.TestCase): self.failUnlessEqual(MatchNameComponent(key1, mlist), None) self.failUnlessEqual(MatchNameComponent(key2, mlist), key2) + def testCaseInsensitivePartialMatch(self): + """Test for the case_insensitive keyword""" + mlist = ["test1.example.com", "test2.example.net"] + self.assertEqual(MatchNameComponent("test2", mlist, case_sensitive=False), + "test2.example.net") + self.assertEqual(MatchNameComponent("Test2", mlist, case_sensitive=False), + "test2.example.net") + self.assertEqual(MatchNameComponent("teSt2", mlist, case_sensitive=False), + "test2.example.net") + self.assertEqual(MatchNameComponent("TeSt2", mlist, case_sensitive=False), + "test2.example.net") + + + def testCaseInsensitiveFullMatch(self): + mlist = ["ts1.ex", "ts1.ex.org", "ts2.ex", "Ts2.ex"] + # Between the two ts1 a full string match non-case insensitive should work + self.assertEqual(MatchNameComponent("Ts1", mlist, case_sensitive=False), + None) + self.assertEqual(MatchNameComponent("Ts1.ex", mlist, case_sensitive=False), + "ts1.ex") + self.assertEqual(MatchNameComponent("ts1.ex", mlist, case_sensitive=False), + "ts1.ex") + # Between the two ts2 only case differs, so only case-match works + self.assertEqual(MatchNameComponent("ts2.ex", mlist, case_sensitive=False), + "ts2.ex") + self.assertEqual(MatchNameComponent("Ts2.ex", mlist, case_sensitive=False), + "Ts2.ex") + self.assertEqual(MatchNameComponent("TS2.ex", mlist, case_sensitive=False), + None) + class TestFormatUnit(unittest.TestCase): """Test case for the FormatUnit function"""