Revision 26288e68

b/lib/utils.py
615 615
  """Class implementing resolver and hostname functionality
616 616

  
617 617
  """
618
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
619

  
618 620
  def __init__(self, name=None):
619 621
    """Initialize the host name object.
620 622

  
......
665 667

  
666 668
    return result
667 669

  
670
  @classmethod
671
  def NormalizeName(cls, hostname):
672
    """Validate and normalize the given hostname.
673

  
674
    @attention: the validation is a bit more relaxed than the standards
675
        require; most importantly, we allow underscores in names
676
    @raise errors.OpPrereqError: when the name is not valid
677

  
678
    """
679
    hostname = hostname.lower()
680
    if (not cls._VALID_NAME_RE.match(hostname) or
681
        # double-dots, meaning empty label
682
        ".." in hostname or
683
        # empty initial label
684
        hostname.startswith(".")):
685
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
686
                                 errors.ECODE_INVAL)
687
    if hostname.endswith("."):
688
      hostname = hostname.rstrip(".")
689
    return hostname
690

  
668 691

  
669 692
def GetHostInfo(name=None):
670 693
  """Lookup host name and raise an OpPrereqError for failures"""
b/test/ganeti.utils_unittest.py
47 47
     ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles, \
48 48
     SetEtcHostsEntry, RemoveEtcHostsEntry, FirstFree, OwnIpAddress, \
49 49
     TailFile, ForceDictType, SafeEncode, IsNormAbsPath, FormatTime, \
50
     UnescapeAndSplit, RunParts, PathJoin
50
     UnescapeAndSplit, RunParts, PathJoin, HostInfo
51 51

  
52 52
from ganeti.errors import LockError, UnitParseError, GenericError, \
53
     ProgrammerError
53
     ProgrammerError, OpPrereqError
54 54

  
55 55

  
56 56
class TestIsProcessAlive(unittest.TestCase):
......
1284 1284
    self.failUnlessRaises(ValueError, PathJoin, "/a", "/b")
1285 1285

  
1286 1286

  
1287
class TestHostInfo(unittest.TestCase):
1288
  """Testing case for HostInfo"""
1289

  
1290
  def testUppercase(self):
1291
    data = "AbC.example.com"
1292
    self.failUnlessEqual(HostInfo.NormalizeName(data), data.lower())
1293

  
1294
  def testTooLongName(self):
1295
    data = "a.b." + "c" * 255
1296
    self.failUnlessRaises(OpPrereqError, HostInfo.NormalizeName, data)
1297

  
1298
  def testTrailingDot(self):
1299
    data = "a.b.c"
1300
    self.failUnlessEqual(HostInfo.NormalizeName(data + "."), data)
1301

  
1302
  def testInvalidName(self):
1303
    data = [
1304
      "a b",
1305
      "a/b",
1306
      ".a.b",
1307
      "a..b",
1308
      ]
1309
    for value in data:
1310
      self.failUnlessRaises(OpPrereqError, HostInfo.NormalizeName, value)
1311

  
1312
  def testValidName(self):
1313
    data = [
1314
      "a.b",
1315
      "a-b",
1316
      "a_b",
1317
      "a.b.c",
1318
      ]
1319
    for value in data:
1320
      HostInfo.NormalizeName(value)
1321

  
1322

  
1323

  
1287 1324
if __name__ == '__main__':
1288 1325
  testutils.GanetiTestProgram()

Also available in: Unified diff