Commit b43dcc5a authored by Manuel Franceschini's avatar Manuel Franceschini

Support IPv6 node add

Signed-off-by: default avatarManuel Franceschini <livewire@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent e7323b5e
......@@ -348,7 +348,7 @@ def InitCluster(cluster_name, mac_prefix,
sshkey = sshline.split(" ")[1]
if modify_etc_hosts:
utils.AddHostToEtcHosts(hostname.name)
utils.AddHostToEtcHosts(hostname)
if modify_ssh_setup:
_InitSSHSetup()
......@@ -480,7 +480,9 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
@param ssh_key_check: whether to do a strict key check
"""
sshrunner = ssh.SshRunner(cluster_name)
family = ssconf.SimpleStore().GetPrimaryIPFamily()
sshrunner = ssh.SshRunner(cluster_name,
ipv6=family==netutils.IP6Address.family)
noded_cert = utils.ReadFile(constants.NODED_CERT_FILE)
rapi_cert = utils.ReadFile(constants.RAPI_CERT_FILE)
......@@ -502,6 +504,10 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
if not confd_hmac_key.endswith("\n"):
confd_hmac_key += "\n"
bind_address = constants.IP4_ADDRESS_ANY
if family == netutils.IP6Address.family:
bind_address = constants.IP6_ADDRESS_ANY
# set up inter-node password and certificate and restarts the node daemon
# and then connect with ssh to set password and start ganeti-noded
# note that all the below variables are sanitized at this point,
......@@ -515,13 +521,13 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
"cat > '%s' << '!EOF.' && \n"
"%s!EOF.\n"
"chmod 0400 %s %s %s && "
"%s start %s" %
"%s start %s -b '%s'" %
(constants.NODED_CERT_FILE, noded_cert,
constants.RAPI_CERT_FILE, rapi_cert,
constants.CONFD_HMAC_KEY, confd_hmac_key,
constants.NODED_CERT_FILE, constants.RAPI_CERT_FILE,
constants.CONFD_HMAC_KEY,
constants.DAEMON_UTIL, constants.NODED))
constants.DAEMON_UTIL, constants.NODED, bind_address))
result = sshrunner.Run(node, 'root', mycommand, batch=False,
ask_key=ssh_key_check,
......
......@@ -3675,7 +3675,9 @@ class LUAddNode(LogicalUnit):
def CheckArguments(self):
# validate/normalize the node name
self.op.node_name = netutils.Hostname.GetNormalizedName(self.op.node_name)
self.hostname = netutils.GetHostname(name=self.op.node_name,
family=self.cfg.GetPrimaryIPFamily())
self.op.node_name = self.hostname.name
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -3704,17 +3706,17 @@ class LUAddNode(LogicalUnit):
Any errors are signaled by raising errors.OpPrereqError.
"""
hostname = netutils.GetHostname(name=self.op.node_name)
node = hostname.name
cfg = self.cfg
hostname = self.hostname
node = hostname.name
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):
raise errors.OpPrereqError("Invalid secondary IP given",
errors.ECODE_INVAL)
secondary_ip = self.op.secondary_ip
if not netutils.IP4Address.IsValid(secondary_ip):
raise errors.OpPrereqError("Secondary IP (%s) needs to be a valid IPv4"
" address" % secondary_ip, errors.ECODE_INVAL)
node_list = cfg.GetNodeList()
if not self.op.readd and node in node_list:
......@@ -3846,7 +3848,7 @@ class LUAddNode(LogicalUnit):
# Add node to our /etc/hosts, and add key to known_hosts
if self.cfg.GetClusterInfo().modify_etc_hosts:
# FIXME: this should be done via an rpc call to node daemon
utils.AddHostToEtcHosts(new_node.name)
utils.AddHostToEtcHosts(self.hostname)
if new_node.secondary_ip != new_node.primary_ip:
result = self.rpc.call_node_has_ip_address(new_node.name,
......
......@@ -132,7 +132,7 @@ class 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)
result = socket.getaddrinfo(hostname, None)
except (socket.gaierror, socket.herror, socket.error), err:
# hostname not found in DNS, or other socket exception in the
# (code, description format)
......
......@@ -272,13 +272,15 @@ def _AddressLookup(node_list,
@returns: List of corresponding addresses, if found
"""
iplist = ssc().GetNodePrimaryIPList()
ss = ssc()
iplist = ss.GetNodePrimaryIPList()
family = ss.GetPrimaryIPFamily()
addresses = []
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)
address = nslookup_fn(node, family=family)
addresses.append(address)
return addresses
......
#
#
# 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
......@@ -66,8 +66,17 @@ class SshRunner:
"""Wrapper for SSH commands.
"""
def __init__(self, cluster_name):
def __init__(self, cluster_name, ipv6=False):
"""Initializes this class.
@type cluster_name: str
@param cluster_name: name of the cluster
@type ipv6: bool
@param ipv6: If true, force ssh to use IPv6 addresses only
"""
self.cluster_name = cluster_name
self.ipv6 = ipv6
def _BuildSshOptions(self, batch, ask_key, use_cluster_key,
strict_host_check, private_key=None, quiet=True):
......@@ -128,6 +137,9 @@ class SshRunner:
else:
options.append("-oStrictHostKeyChecking=no")
if self.ipv6:
options.append("-6")
return options
def BuildCmd(self, hostname, user, command, batch=True, ask_key=False,
......
......@@ -61,7 +61,6 @@ except ImportError:
from ganeti import errors
from ganeti import constants
from ganeti import compat
from ganeti import netutils
_locksheld = []
......@@ -1461,8 +1460,8 @@ def AddHostToEtcHosts(hostname):
L{constants.ETC_HOSTS}
"""
SetEtcHostsEntry(constants.ETC_HOSTS, netutils.Hostname.GetIP(hostname),
hostname, [hostname.split(".")[0]])
SetEtcHostsEntry(constants.ETC_HOSTS, hostname.ip, hostname.name,
[hostname.name.split(".")[0]])
def RemoveEtcHostsEntry(file_name, hostname):
......
......@@ -59,6 +59,7 @@ class FakeHttpPool:
def GetFakeSimpleStoreClass(fn):
class FakeSimpleStore:
GetNodePrimaryIPList = fn
GetPrimaryIPFamily = lambda _: None
return FakeSimpleStore
......@@ -239,16 +240,16 @@ class TestClient(unittest.TestCase):
addr_list = ["192.0.2.%d" % n for n in range(0, 255, 13)]
node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
node_addr_list = [ " ".join(t) for t in zip(node_list, addr_list)]
ssc = GetFakeSimpleStoreClass(lambda s: node_addr_list)
ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
result = rpc._AddressLookup(node_list, ssc=ssc)
self.assertEqual(result, addr_list)
def testAddressLookupNSLookup(self):
addr_list = ["192.0.2.%d" % n for n in range(0, 255, 13)]
node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
ssc = GetFakeSimpleStoreClass(lambda s: [])
ssc = GetFakeSimpleStoreClass(lambda _: [])
node_addr_map = dict(zip(node_list, addr_list))
nslookup_fn = lambda name: node_addr_map.get(name)
nslookup_fn = lambda name, family=None: node_addr_map.get(name)
result = rpc._AddressLookup(node_list, ssc=ssc, nslookup_fn=nslookup_fn)
self.assertEqual(result, addr_list)
......@@ -257,12 +258,20 @@ class TestClient(unittest.TestCase):
node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
n = len(addr_list) / 2
node_addr_list = [ " ".join(t) for t in zip(node_list[n:], addr_list[n:])]
ssc = GetFakeSimpleStoreClass(lambda s: node_addr_list)
ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
node_addr_map = dict(zip(node_list[:n], addr_list[:n]))
nslookup_fn = lambda name: node_addr_map.get(name)
nslookup_fn = lambda name, family=None: node_addr_map.get(name)
result = rpc._AddressLookup(node_list, ssc=ssc, nslookup_fn=nslookup_fn)
self.assertEqual(result, addr_list)
def testAddressLookupIPv6(self):
addr_list = ["2001:db8::%d" % n for n in range(0, 255, 13)]
node_list = ["node%d.example.com" % n for n in range(0, 255, 13)]
node_addr_list = [ " ".join(t) for t in zip(node_list, addr_list)]
ssc = GetFakeSimpleStoreClass(lambda _: node_addr_list)
result = rpc._AddressLookup(node_list, ssc=ssc)
self.assertEqual(result, addr_list)
if __name__ == "__main__":
testutils.GanetiTestProgram()
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