Commit b705c7a6 authored by Manuel Franceschini's avatar Manuel Franceschini
Browse files

Support for resolving hostnames to IPv6 addresses



This patch enables IPv6 name resolution by using socket.getaddrinfo
instead of socket.gethostbyname_ex.

It renames the HostInfo class to Hostname and unifies its use throughout
the code. This is achieved by using static calls where no object is
needed and removes some obsolete code.

For now, we just resolve to IPv4 addresses, but this will change once it
is needed.
Signed-off-by: default avatarManuel Franceschini <livewire@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent eb202c13
#!/usr/bin/python
#
# Copyright (C) 2006, 2007 Google Inc.
# Copyright (C) 2006, 2007, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -423,7 +423,7 @@ def CheckAgreement():
other node to be up too to confirm our status.
"""
myself = netutils.HostInfo().name
myself = netutils.Hostname.GetSysName()
#temp instantiation of a config writer, used only to get the node list
cfg = config.ConfigWriter()
node_list = cfg.GetNodeList()
......
#!/usr/bin/python
#
# Copyright (C) 2006, 2007, 2008 Google Inc.
# Copyright (C) 2006, 2007, 2008, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -215,7 +215,7 @@ class NodeMaintenance(object):
"""Check node status versus cluster desired state.
"""
my_name = netutils.HostInfo().name
my_name = netutils.Hostname.GetSysName()
req = confd_client.ConfdClientRequest(type=
constants.CONFD_REQ_NODE_ROLE_BYNAME,
query=my_name)
......@@ -466,7 +466,7 @@ class Watcher(object):
def __init__(self, opts, notepad):
self.notepad = notepad
master = client.QueryConfigValues(["master_node"])[0]
if master != netutils.HostInfo().name:
if master != netutils.Hostname.GetSysName():
raise NotMasterError("This is not the master node")
# first archive old jobs
self.ArchiveJobs(opts.job_age)
......
......@@ -408,7 +408,7 @@ def ParseOptions():
# Normalize and check parameters
if options.host is not None:
try:
options.host = netutils.HostInfo.NormalizeName(options.host)
options.host = netutils.Hostname.GetNormalizedName(options.host)
except errors.OpPrereqError, err:
parser.error("Invalid hostname '%s': %s" % (options.host, err))
......
......@@ -490,7 +490,7 @@ def VerifyNode(what, cluster_name):
"""
result = {}
my_name = netutils.HostInfo().name
my_name = netutils.Hostname.GetSysName()
port = netutils.GetDaemonPort(constants.NODED)
if constants.NV_HYPERVISOR in what:
......@@ -2595,7 +2595,7 @@ def CreateX509Certificate(validity, cryptodir=constants.CRYPTO_KEYS_DIR):
"""
(key_pem, cert_pem) = \
utils.GenerateSelfSignedX509Cert(netutils.HostInfo.SysName(),
utils.GenerateSelfSignedX509Cert(netutils.Hostname.GetSysName(),
min(validity, _MAX_SSL_CERT_VALIDITY))
cert_dir = tempfile.mkdtemp(dir=cryptodir,
......@@ -2938,7 +2938,7 @@ def _FindDisks(nodes_ip, disks):
"""
# set the correct physical ID
my_name = netutils.HostInfo().name
my_name = netutils.Hostname.GetSysName()
for cf in disks:
cf.SetPhysicalID(my_name, nodes_ip)
......
......@@ -244,7 +244,7 @@ def InitCluster(cluster_name, mac_prefix,
" entries: %s" % invalid_hvs,
errors.ECODE_INVAL)
hostname = netutils.GetHostInfo()
hostname = netutils.GetHostname()
if netutils.IP4Address.IsLoopback(hostname.ip):
raise errors.OpPrereqError("This host's IP (%s) resolves to a loopback"
......@@ -258,8 +258,7 @@ def InitCluster(cluster_name, mac_prefix,
" belong to this host. Aborting." %
hostname.ip, errors.ECODE_ENVIRON)
clustername = \
netutils.GetHostInfo(netutils.HostInfo.NormalizeName(cluster_name))
clustername = netutils.GetHostname(name=cluster_name)
if netutils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
timeout=5):
......@@ -649,7 +648,7 @@ def GatherMasterVotes(node_list):
@return: list of (node, votes)
"""
myself = netutils.HostInfo().name
myself = netutils.Hostname.GetSysName()
try:
node_list.remove(myself)
except ValueError:
......
......@@ -1648,7 +1648,7 @@ def FormatError(err):
elif isinstance(err, errors.HooksFailure):
obuf.write("Failure: hooks general failure: %s" % msg)
elif isinstance(err, errors.ResolverError):
this_host = netutils.HostInfo.SysName()
this_host = netutils.Hostname.GetSysName()
if err.args[0] == this_host:
msg = "Failure: can't resolve my own hostname ('%s')"
else:
......
......@@ -2527,7 +2527,7 @@ class LURenameCluster(LogicalUnit):
"""Verify that the passed name is a valid one.
"""
hostname = netutils.GetHostInfo(self.op.name)
hostname = netutils.GetHostname(name=self.op.name)
new_name = hostname.name
self.ip = new_ip = hostname.ip
......@@ -3674,7 +3674,7 @@ class LUAddNode(LogicalUnit):
def CheckArguments(self):
# validate/normalize the node name
self.op.node_name = netutils.HostInfo.NormalizeName(self.op.node_name)
self.op.node_name = netutils.Hostname.GetNormalizedName(self.op.node_name)
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -3703,13 +3703,11 @@ class LUAddNode(LogicalUnit):
Any errors are signaled by raising errors.OpPrereqError.
"""
node_name = self.op.node_name
hostname = netutils.GetHostname(name=self.op.node_name)
node = hostname.name
cfg = self.cfg
dns_data = netutils.GetHostInfo(node_name)
node = dns_data.name
primary_ip = self.op.primary_ip = dns_data.ip
primary_ip = self.op.primary_ip = hostname.ip
if self.op.secondary_ip is None:
self.op.secondary_ip = primary_ip
if not netutils.IP4Address.IsValid(self.op.secondary_ip):
......@@ -4915,12 +4913,12 @@ class LURenameInstance(LogicalUnit):
new_name = self.op.new_name
if self.op.name_check:
hostinfo = netutils.HostInfo(netutils.HostInfo.NormalizeName(new_name))
new_name = hostinfo.name
hostname = netutils.GetHostname(name=new_name)
new_name = hostname.name
if (self.op.ip_check and
netutils.TcpPing(hostinfo.ip, constants.DEFAULT_NODED_PORT)):
netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
raise errors.OpPrereqError("IP %s of instance %s already in use" %
(hostinfo.ip, new_name),
(hostname.ip, new_name),
errors.ECODE_NOTUNIQUE)
instance_list = self.cfg.GetInstanceList()
......@@ -4928,7 +4926,6 @@ class LURenameInstance(LogicalUnit):
raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
new_name, errors.ECODE_EXISTS)
def Exec(self, feedback_fn):
"""Reinstall the instance.
......@@ -6551,7 +6548,7 @@ class LUCreateInstance(LogicalUnit):
self.op.start = False
# validate/normalize the instance name
self.op.instance_name = \
netutils.HostInfo.NormalizeName(self.op.instance_name)
netutils.Hostname.GetNormalizedName(self.op.instance_name)
if self.op.ip_check and not self.op.name_check:
# TODO: make the ip check more flexible and not depend on the name check
......@@ -6590,7 +6587,7 @@ class LUCreateInstance(LogicalUnit):
# instance name verification
if self.op.name_check:
self.hostname1 = netutils.GetHostInfo(self.op.instance_name)
self.hostname1 = netutils.GetHostname(name=self.op.instance_name)
self.op.instance_name = self.hostname1.name
# used in CheckPrereq for ip ping check
self.check_ip = self.hostname1.ip
......@@ -6670,8 +6667,8 @@ class LUCreateInstance(LogicalUnit):
raise errors.OpPrereqError("Missing source instance name",
errors.ECODE_INVAL)
norm_name = netutils.HostInfo.NormalizeName(src_instance_name)
self.source_instance_name = netutils.GetHostInfo(norm_name).name
self.source_instance_name = \
netutils.GetHostname(name=src_instance_name).name
else:
raise errors.OpPrereqError("Invalid instance creation mode %r" %
......
......@@ -154,7 +154,7 @@ class ConfigWriter:
# _DistributeConfig, we compute it here once and reuse it; it's
# better to raise an error before starting to modify the config
# file than after it was modified
self._my_hostname = netutils.HostInfo().name
self._my_hostname = netutils.Hostname.GetSysName()
self._last_cluster_serial = -1
self._OpenConfig()
......
......@@ -849,7 +849,7 @@ class JobQueue(object):
"""
self.context = context
self._memcache = weakref.WeakValueDictionary()
self._my_hostname = netutils.HostInfo().name
self._my_hostname = netutils.Hostname.GetSysName()
# The Big JobQueue lock. If a code block or method acquires it in shared
# mode safe it must guarantee concurrency with all the code acquiring it in
......
......@@ -1554,7 +1554,7 @@ def CheckRemoteExportDiskInfo(cds, disk_index, disk_info):
if not utils.VerifySha1Hmac(cds, msg, hmac_digest, salt=hmac_salt):
raise errors.GenericError("HMAC is wrong")
return (netutils.HostInfo.NormalizeName(host),
return (netutils.Hostname.GetNormalizedName(host),
utils.ValidateServiceName(port),
magic)
......
......@@ -62,44 +62,48 @@ def GetSocketCredentials(sock):
return struct.unpack(_STRUCT_UCRED, peercred)
def GetHostInfo(name=None):
"""Lookup host name and raise an OpPrereqError for failures"""
def GetHostname(name=None, family=None):
"""Returns a Hostname object.
@type name: str
@param name: hostname or None
@type family: int
@param family: AF_INET | AF_INET6 | None
@rtype: L{Hostname}
@return: Hostname object
@raise: errors.OpPrereqError
"""
try:
return HostInfo(name)
return Hostname(name=name, family=family)
except errors.ResolverError, err:
raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
(err[0], err[2]), errors.ECODE_RESOLVER)
class HostInfo:
"""Class implementing resolver and hostname functionality
class Hostname:
"""Class implementing resolver and hostname functionality.
"""
_VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
def __init__(self, name=None):
def __init__(self, name=None, family=None):
"""Initialize the host name object.
If the name argument is not passed, it will use this system's
name.
If the name argument is None, it will use this system's name.
@type family: int
@param family: AF_INET | AF_INET6 | None
@type name: str
@param name: hostname or None
"""
if name is None:
name = self.SysName()
self.query = name
self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
self.ip = self.ipaddrs[0]
name = self.GetSysName()
def ShortName(self):
"""Returns the hostname without domain.
"""
return self.name.split('.')[0]
self.name = self.GetNormalizedName(name)
self.ip = self.GetIP(self.name, family=family)
@staticmethod
def SysName():
def GetSysName():
"""Return the current system's name.
This is simply a wrapper over C{socket.gethostname()}.
......@@ -108,29 +112,37 @@ class HostInfo:
return socket.gethostname()
@staticmethod
def LookupHostname(hostname):
"""Look up hostname
def GetIP(hostname, family=None):
"""Return IP address of given hostname.
Supports both IPv4 and IPv6.
@type hostname: str
@param hostname: hostname to look up
@rtype: tuple
@return: a tuple (name, aliases, ipaddrs) as returned by
C{socket.gethostbyname_ex}
@type family: int
@param family: AF_INET | AF_INET6 | None
@rtype: str
@return: IP address
@raise errors.ResolverError: in case of errors in resolving
"""
try:
result = socket.gethostbyname_ex(hostname)
if family in (socket.AF_INET, socket.AF_INET6):
result = socket.getaddrinfo(hostname, None, family)
else:
result = socket.getaddrinfo(hostname, None, socket.AF_INET)
except (socket.gaierror, socket.herror, socket.error), err:
# hostname not found in DNS, or other socket exception in the
# (code, description format)
raise errors.ResolverError(hostname, err.args[0], err.args[1])
return result
# getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
# canonname, sockaddr). We return the first tuple's first address in
# sockaddr
return result[0][4][0]
@classmethod
def NormalizeName(cls, hostname):
@staticmethod
def GetNormalizedName(hostname):
"""Validate and normalize the given hostname.
@attention: the validation is a bit more relaxed than the standards
......@@ -138,8 +150,9 @@ class HostInfo:
@raise errors.OpPrereqError: when the name is not valid
"""
valid_name_re = re.compile("^[a-z0-9._-]{1,255}$")
hostname = hostname.lower()
if (not cls._VALID_NAME_RE.match(hostname) or
if (not valid_name_re.match(hostname) or
# double-dots, meaning empty label
".." in hostname or
# empty initial label
......
......@@ -259,7 +259,7 @@ class RpcResult(object):
def _AddressLookup(node_list,
ssc=ssconf.SimpleStore,
nslookup_fn=netutils.HostInfo.LookupHostname):
nslookup_fn=netutils.Hostname.GetIP):
"""Return addresses for given node names.
@type node_list: list
......@@ -272,22 +272,14 @@ def _AddressLookup(node_list,
@returns: List of corresponding addresses, if found
"""
def _NSLookup(name):
_, _, addrs = nslookup_fn(name)
return addrs[0]
iplist = ssc().GetNodePrimaryIPList()
addresses = []
try:
iplist = ssc().GetNodePrimaryIPList()
ipmap = dict(entry.split() for entry in iplist)
for node in node_list:
address = ipmap.get(node)
if address is None:
address = _NSLookup(node)
addresses.append(address)
except errors.ConfigurationError:
# Address not found in so we do a NS lookup
addresses = [_NSLookup(node) for node in node_list]
ipmap = dict(entry.split() for entry in iplist)
for node in node_list:
address = ipmap.get(node)
if address is None:
address = nslookup_fn(node)
addresses.append(address)
return addresses
......
#
#
# Copyright (C) 2006, 2007, 2008 Google Inc.
# Copyright (C) 2006, 2007, 2008, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -478,7 +478,7 @@ def GetMasterAndMyself(ss=None):
"""
if ss is None:
ss = SimpleStore()
return ss.GetMasterNode(), netutils.HostInfo().name
return ss.GetMasterNode(), netutils.Hostname.GetSysName()
def CheckMaster(debug, ss=None):
......
......@@ -1461,8 +1461,8 @@ def AddHostToEtcHosts(hostname):
L{constants.ETC_HOSTS}
"""
hi = netutils.HostInfo(name=hostname)
SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
SetEtcHostsEntry(constants.ETC_HOSTS, netutils.Hostname.GetIP(hostname),
hostname, [hostname.split(".")[0]])
def RemoveEtcHostsEntry(file_name, hostname):
......@@ -1518,9 +1518,8 @@ def RemoveHostFromEtcHosts(hostname):
L{constants.ETC_HOSTS}
"""
hi = netutils.HostInfo(name=hostname)
RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname)
RemoveEtcHostsEntry(constants.ETC_HOSTS, hostname.split(".")[0])
def TimestampForFilename():
......
......@@ -135,8 +135,7 @@ def AddNode(opts, args):
"""
cl = GetClient()
dns_data = netutils.GetHostInfo(netutils.HostInfo.NormalizeName(args[0]))
node = dns_data.name
node = netutils.GetHostname(name=args[0]).name
readd = opts.readd
try:
......
......@@ -74,7 +74,8 @@ class TestX509Certificates(unittest.TestCase):
class TestNodeVerify(testutils.GanetiTestCase):
def testMasterIPLocalhost(self):
# this a real functional test, but requires localhost to be reachable
local_data = (netutils.HostInfo().name, constants.IP4_ADDRESS_LOCALHOST)
local_data = (netutils.Hostname.GetSysName(),
constants.IP4_ADDRESS_LOCALHOST)
result = backend.VerifyNode({constants.NV_MASTERIP: local_data}, None)
self.failUnless(constants.NV_MASTERIP in result,
"Master IP data not returned")
......
#!/usr/bin/python
#
# Copyright (C) 2006, 2007 Google Inc.
# Copyright (C) 2006, 2007, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -60,7 +60,7 @@ class TestConfigRunner(unittest.TestCase):
def _init_cluster(self, cfg):
"""Initializes the cfg object"""
me = netutils.HostInfo()
me = netutils.Hostname()
ip = constants.IP4_ADDRESS_LOCALHOST
cluster_config = objects.Cluster(
......
......@@ -102,21 +102,21 @@ class TestGetSocketCredentials(unittest.TestCase):
self.assertEqual(gid, os.getgid())
class TestHostInfo(unittest.TestCase):
"""Testing case for HostInfo"""
class TestHostname(unittest.TestCase):
"""Testing case for Hostname"""
def testUppercase(self):
data = "AbC.example.com"
self.failUnlessEqual(netutils.HostInfo.NormalizeName(data), data.lower())
self.assertEqual(netutils.Hostname.GetNormalizedName(data), data.lower())
def testTooLongName(self):
data = "a.b." + "c" * 255
self.failUnlessRaises(errors.OpPrereqError,
netutils.HostInfo.NormalizeName, data)
self.assertRaises(errors.OpPrereqError,
netutils.Hostname.GetNormalizedName, data)
def testTrailingDot(self):
data = "a.b.c"
self.failUnlessEqual(netutils.HostInfo.NormalizeName(data + "."), data)
self.assertEqual(netutils.Hostname.GetNormalizedName(data + "."), data)
def testInvalidName(self):
data = [
......@@ -126,8 +126,8 @@ class TestHostInfo(unittest.TestCase):
"a..b",
]
for value in data:
self.failUnlessRaises(errors.OpPrereqError,
netutils.HostInfo.NormalizeName, value)
self.assertRaises(errors.OpPrereqError,
netutils.Hostname.GetNormalizedName, value)
def testValidName(self):
data = [
......@@ -137,7 +137,7 @@ class TestHostInfo(unittest.TestCase):
"a.b.c",
]
for value in data:
netutils.HostInfo.NormalizeName(value)
self.assertEqual(netutils.Hostname.GetNormalizedName(value), value)
class TestIPAddress(unittest.TestCase):
......
......@@ -248,7 +248,7 @@ class TestClient(unittest.TestCase):
node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
ssc = GetFakeSimpleStoreClass(lambda s: [])
node_addr_map = dict(zip(node_list, addr_list))
nslookup_fn = lambda name: (None, None, [node_addr_map.get(name)])
nslookup_fn = lambda name: node_addr_map.get(name)
result = rpc._AddressLookup(node_list, ssc=ssc, nslookup_fn=nslookup_fn)
self.assertEqual(result, addr_list)
......@@ -259,7 +259,7 @@ class TestClient(unittest.TestCase):
node_addr_list = [ " ".join(t) for t in zip(node_list[n:], addr_list[n:])]
ssc = GetFakeSimpleStoreClass(lambda s: node_addr_list)
node_addr_map = dict(zip(node_list[:n], addr_list[:n]))
nslookup_fn = lambda name: (None, None, [node_addr_map.get(name)])
nslookup_fn = lambda name: node_addr_map.get(name)
result = rpc._AddressLookup(node_list, ssc=ssc, nslookup_fn=nslookup_fn)
self.assertEqual(result, addr_list)
......
#
#
# Copyright (C) 2006, 2007 Google Inc.
# Copyright (C) 2006, 2007, 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -50,7 +50,7 @@ class FakeConfig:
return "test.cluster"
def GetMasterNode(self):
return netutils.HostInfo().name
return netutils.Hostname.GetSysName()
def GetDefaultIAllocator(Self):
return "testallocator"
......
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