Commit 26288e68 authored by Iustin Pop's avatar Iustin Pop
Browse files

Add a function to validate and normalize hostnames

This differs slightly from the specification, by allowing names to start
with digits, not checking the length of individual components, and
allowing underscores.
Signed-off-by: default avatarIustin Pop <>
Reviewed-by: default avatarMichael Hanselmann <>
parent 04a69a18
......@@ -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
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
raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
if hostname.endswith("."):
hostname = hostname.rstrip(".")
return hostname
def GetHostInfo(name=None):
"""Lookup host name and raise an OpPrereqError for failures"""
......@@ -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, 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 = ""
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",
for value in data:
self.failUnlessRaises(OpPrereqError, HostInfo.NormalizeName, value)
def testValidName(self):
data = [
for value in data:
if __name__ == '__main__':
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment