diff --git a/lib/utils.py b/lib/utils.py
index 52b799fe5387e974a1a5d4da6657d6ff286772f2..131f4c1a7645bbe99f855befeaf252caed62c2e1 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -615,6 +615,8 @@ class HostInfo:
   """Class implementing resolver and hostname functionality
 
   """
+  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
+
   def __init__(self, name=None):
     """Initialize the host name object.
 
@@ -665,6 +667,27 @@ class HostInfo:
 
     return result
 
+  @classmethod
+  def NormalizeName(cls, hostname):
+    """Validate and normalize the given hostname.
+
+    @attention: the validation is a bit more relaxed than the standards
+        require; most importantly, we allow underscores in names
+    @raise errors.OpPrereqError: when the name is not valid
+
+    """
+    hostname = hostname.lower()
+    if (not cls._VALID_NAME_RE.match(hostname) or
+        # double-dots, meaning empty label
+        ".." in hostname or
+        # empty initial label
+        hostname.startswith(".")):
+      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
+                                 errors.ECODE_INVAL)
+    if hostname.endswith("."):
+      hostname = hostname.rstrip(".")
+    return hostname
+
 
 def GetHostInfo(name=None):
   """Lookup host name and raise an OpPrereqError for failures"""
diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py
index aad16c101df4b556a8c0a9c19ee35dab862e09dd..8392ad056cc90b009509dd0ec518178a4aacbb29 100755
--- a/test/ganeti.utils_unittest.py
+++ b/test/ganeti.utils_unittest.py
@@ -47,10 +47,10 @@ from ganeti.utils import IsProcessAlive, RunCmd, \
      ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
      SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
      TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
-     UnescapeAndSplit, RunParts, PathJoin
+     UnescapeAndSplit, RunParts, PathJoin, HostInfo
 
 from ganeti.errors import LockError, UnitParseError, GenericError, \
-     ProgrammerError
+     ProgrammerError, OpPrereqError
 
 
 class TestIsProcessAlive(unittest.TestCase):
@@ -1284,5 +1284,42 @@ class TestPathJoin(unittest.TestCase):
     self.failUnlessRaises(ValueError, PathJoin, "/a", "/b")
 
 
+class TestHostInfo(unittest.TestCase):
+  """Testing case for HostInfo"""
+
+  def testUppercase(self):
+    data = "AbC.example.com"
+    self.failUnlessEqual(HostInfo.NormalizeName(data), data.lower())
+
+  def testTooLongName(self):
+    data = "a.b." + "c" * 255
+    self.failUnlessRaises(OpPrereqError, HostInfo.NormalizeName, data)
+
+  def testTrailingDot(self):
+    data = "a.b.c"
+    self.failUnlessEqual(HostInfo.NormalizeName(data + "."), data)
+
+  def testInvalidName(self):
+    data = [
+      "a b",
+      "a/b",
+      ".a.b",
+      "a..b",
+      ]
+    for value in data:
+      self.failUnlessRaises(OpPrereqError, HostInfo.NormalizeName, value)
+
+  def testValidName(self):
+    data = [
+      "a.b",
+      "a-b",
+      "a_b",
+      "a.b.c",
+      ]
+    for value in data:
+      HostInfo.NormalizeName(value)
+
+
+
 if __name__ == '__main__':
   testutils.GanetiTestProgram()