diff --git a/lib/utils/text.py b/lib/utils/text.py
index e55baaa9b126d29087b73222552fe65887099be1..d8e0984a3ae001b15f7b5007e1d52b031b77852d 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 417a0016591b3f086462fde5c2de63945e9ec69e..5181db4d36aa963d1f552d59650039d186d0a696 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"""