Commit 16abfbc2 authored by Alexander Schreiber's avatar Alexander Schreiber
Browse files

Remove fping as a dependency for Ganeti.

This patch completely  gets rid of fping
 - replace all fping invocations with TcpPing calls
 - update documentation accordingly.
 - associated cleanups (use constant for localhost IP, use more sensible
   defaults for TcpPing and _use_ those)

Reviewed-by: iustinp
parent 23269c5b
......@@ -22,8 +22,6 @@ Before installing, please verify that you have the following programs:
http://www.openssh.com/portable.html
- bridge utilities
http://bridge.sourceforge.net/
- fping
http://fping.sourceforge.net/
- iproute2
http://developer.osdl.org/dev/iproute2
- arping (part of iputils package)
......
......@@ -106,10 +106,10 @@ def StartMaster(master_netdev, master_ip, debug):
"""Starts the master.
"""
result = utils.RunCmd(["fping", "-q", master_ip])
if not result.failed:
r2 = utils.RunCmd(["fping", "-q", "-S127.0.0.1", master_ip])
if not r2.failed:
if utils.TcpPing(utils.HostInfo().name, master_ip,
constants.DEFAULT_NODED_PORT):
if utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, master_ip,
constants.DEFAULT_NODED_PORT):
# we already have the ip:
if debug:
sys.stderr.write("Notice: already started.\n")
......
......@@ -38,6 +38,7 @@ from ganeti import constants
from ganeti import objects
from ganeti import errors
from ganeti import ssconf
from ganeti import utils
from twisted.spread import pb
from twisted.internet import reactor
......@@ -346,6 +347,14 @@ class ServerObject(pb.Avatar):
# node --------------------------
@staticmethod
def perspective_node_tcp_ping(params):
"""Do a TcpPing on the remote node.
"""
return utils.TcpPing(params[0], params[1], params[2],
timeout=params[3], live_port_needed=params[4])
@staticmethod
def perspective_node_info(params):
"""Query node information.
......
......@@ -376,10 +376,6 @@ skip resource "r1" {
<simpara><ulink url="http://bridge.sourceforge.net/">Bridge
utilities</ulink></simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://fping.sourceforge.net/">fping</ulink></simpara>
</listitem>
<listitem>
<simpara><ulink
url="http://developer.osdl.org/dev/iproute2">iproute2</ulink></simpara>
......@@ -429,7 +425,7 @@ skip resource "r1" {
</formalpara>
<screen>
# apt-get install lvm2 ssh bridge-utils iproute iputils-arping \
fping python2.4 python-twisted-core python-pyopenssl openssl \
python2.4 python-twisted-core python-pyopenssl openssl \
mdadm
</screen>
......
......@@ -567,8 +567,8 @@ class LUInitCluster(LogicalUnit):
self.clustername = clustername = utils.HostInfo(self.op.cluster_name)
result = utils.RunCmd(["fping", "-S127.0.0.1", "-q", hostname.ip])
if result.failed:
if not utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, hostname.ip,
constants.DEFAULT_NODED_PORT):
raise errors.OpPrereqError("Inconsistency: this host's name resolves"
" to %s,\nbut this ip address does not"
" belong to this host."
......@@ -577,12 +577,13 @@ class LUInitCluster(LogicalUnit):
secondary_ip = getattr(self.op, "secondary_ip", None)
if secondary_ip and not utils.IsValidIP(secondary_ip):
raise errors.OpPrereqError("Invalid secondary ip given")
if secondary_ip and secondary_ip != hostname.ip:
result = utils.RunCmd(["fping", "-S127.0.0.1", "-q", secondary_ip])
if result.failed:
raise errors.OpPrereqError("You gave %s as secondary IP,\n"
"but it does not belong to this host." %
secondary_ip)
if (secondary_ip and
secondary_ip != hostname.ip and
(not utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, secondary_ip,
constants.DEFAULT_NODED_PORT))):
raise errors.OpPrereqError("You gave %s as secondary IP,\n"
"but it does not belong to this host." %
secondary_ip)
self.secondary_ip = secondary_ip
# checks presence of the volume group given
......@@ -1428,17 +1429,18 @@ class LUAddNode(LogicalUnit):
" new node doesn't have one")
# checks reachablity
command = ["fping", "-q", primary_ip]
result = utils.RunCmd(command)
if result.failed:
if not utils.TcpPing(utils.HostInfo().name,
primary_ip,
constants.DEFAULT_NODED_PORT):
raise errors.OpPrereqError("Node not reachable by ping")
if not newbie_singlehomed:
# check reachability from my secondary ip to newbie's secondary ip
command = ["fping", "-S%s" % myself.secondary_ip, "-q", secondary_ip]
result = utils.RunCmd(command)
if result.failed:
raise errors.OpPrereqError("Node secondary ip not reachable by ping")
if not utils.TcpPing(myself.secondary_ip,
secondary_ip,
constants.DEFAULT_NODED_PORT):
raise errors.OpPrereqError(
"Node secondary ip not reachable by TCP based ping to noded port")
self.new_node = objects.Node(name=node,
primary_ip=primary_ip,
......@@ -1529,9 +1531,11 @@ class LUAddNode(LogicalUnit):
self.cfg.GetHostKey())
if new_node.secondary_ip != new_node.primary_ip:
result = ssh.SSHCall(node, "root",
"fping -S 127.0.0.1 -q %s" % new_node.secondary_ip)
if result.failed:
if not rpc.call_node_tcp_ping(new_node.name,
constants.LOCALHOST_IP_ADDRESS,
new_node.secondary_ip,
constants.DEFAULT_NODED_PORT,
10, False):
raise errors.OpExecError("Node claims it doesn't have the"
" secondary ip you gave (%s).\n"
"Please fix and re-run this command." %
......@@ -2844,11 +2848,10 @@ class LUCreateInstance(LogicalUnit):
" adding an instance in start mode")
if self.op.ip_check:
command = ["fping", "-q", hostname1.ip]
result = utils.RunCmd(command)
if not result.failed:
raise errors.OpPrereqError("IP address %s of instance %s already"
" in use" % (hostname1.ip, instance_name))
if utils.TcpPing(utils.HostInfo().name, hostname1.ip,
constants.DEFAULT_NODED_PORT):
raise errors.OpPrereqError("IP %s of instance %s already in use" %
(hostname1.ip, instance_name))
# bridge verification
bridge = getattr(self.op, "bridge", None)
......
......@@ -106,3 +106,5 @@ MAX_TAGS_PER_OBJ = 4096
# others
DEFAULT_BRIDGE = "xen-br0"
SYNC_SPEED = 30 * 1024
LOCALHOST_IP_ADDRESS="127.0.0.1"
TCP_PING_TIMEOUT = 10
......@@ -373,6 +373,18 @@ def call_instance_list(node_list):
return c.getresult()
def call_node_tcp_ping(node, source, target, port, timeout, live_port_needed):
"""Do a TcpPing on the remote node
This is a single-node call.
"""
c = Client("node_tcp_ping", [source, target, port, timeout,
live_port_needed])
c.connect(node)
c.run()
return c.getresult().get(node, False)
def call_node_info(node_list, vg_name):
"""Return node information.
......
......@@ -37,6 +37,7 @@ import errno
from ganeti import logger
from ganeti import errors
_locksheld = []
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
......@@ -782,42 +783,8 @@ def ShellQuoteArgs(args):
return ' '.join([ShellQuote(i) for i in args])
def _ParseIpOutput(output):
"""Parsing code for GetLocalIPAddresses().
This function is split out, so we can unit test it.
"""
re_ip = re.compile('^(\d+\.\d+\.\d+\.\d+)(?:/\d+)$')
ips = []
for line in output.splitlines(False):
fields = line.split()
if len(line) < 4:
continue
m = re_ip.match(fields[3])
if m:
ips.append(m.group(1))
return ips
def GetLocalIPAddresses():
"""Gets a list of all local IP addresses.
Should this break one day, a small Python module written in C could
use the API call getifaddrs().
"""
result = RunCmd(["ip", "-family", "inet", "-oneline", "addr", "show"])
if result.failed:
raise errors.OpExecError("Command '%s' failed, error: %s,"
" output: %s" % (result.cmd, result.fail_reason, result.output))
return _ParseIpOutput(result.output)
def TcpPing(source, target, port, timeout=10, live_port_needed=True):
def TcpPing(source, target, port, timeout=10, live_port_needed=False):
"""Simple ping implementation using TCP connect(2).
Try to do a TCP connect(2) from the specified source IP to the specified
......
......@@ -55,8 +55,9 @@ class TestConfigRunner(unittest.TestCase):
def _init_cluster(self, cfg):
"""Initializes the cfg object"""
cfg.InitConfig(utils.HostInfo().name, '127.0.0.1', None, '', 'aa:00:00',
'xenvg', constants.DEFAULT_BRIDGE)
cfg.InitConfig(utils.HostInfo().name, constants.LOCALHOST_IP_ADDRESS,
None, '', 'aa:00:00', 'xenvg',
constants.DEFAULT_BRIDGE)
def _create_instance(self):
"""Create and return an instance object"""
......
......@@ -31,11 +31,11 @@ import socket
import shutil
import ganeti
from ganeti import constants
from ganeti.utils import IsProcessAlive, Lock, Unlock, RunCmd, \
RemoveFile, CheckDict, MatchNameComponent, FormatUnit, \
ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \
ShellQuote, ShellQuoteArgs, _ParseIpOutput, TcpPing, \
ListVisibleFiles
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles
from ganeti.errors import LockError, UnitParseError
......@@ -445,38 +445,12 @@ class TestShellQuoting(unittest.TestCase):
self.assertEqual(ShellQuoteArgs(['a', 'b\'', 'c']), "a 'b'\\\''' c")
class TestIpAdressList(unittest.TestCase):
"""Test case for local IP addresses"""
def _test(self, output, required):
ips = _ParseIpOutput(output)
# Sort the output, so our check below works in all cases
ips.sort()
required.sort()
self.assertEqual(required, ips)
def testSingleIpAddress(self):
output = \
("3: lo inet 127.0.0.1/8 brd 127.255.255.255 scope host lo\n"
"5: eth0 inet 10.0.0.1/24 brd 172.30.15.127 scope global eth0\n")
self._test(output, ['127.0.0.1', '10.0.0.1'])
def testMultipleIpAddresses(self):
output = \
("3: lo inet 127.0.0.1/8 brd 127.255.255.255 scope host lo\n"
"5: eth0 inet 10.0.0.1/24 brd 172.30.15.127 scope global eth0\n"
"5: eth0 inet 1.2.3.4/8 brd 1.255.255.255 scope global eth0:test\n")
self._test(output, ['127.0.0.1', '10.0.0.1', '1.2.3.4'])
class TestTcpPing(unittest.TestCase):
"""Testcase for TCP version of ping - against listen(2)ing port"""
def setUp(self):
self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.listener.bind(("127.0.0.1", 0))
self.listener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
self.listenerport = self.listener.getsockname()[1]
self.listener.listen(1)
......@@ -486,8 +460,8 @@ class TestTcpPing(unittest.TestCase):
del self.listenerport
def testTcpPingToLocalHostAccept(self):
self.assert_(TcpPing("127.0.0.1",
"127.0.0.1",
self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
constants.LOCALHOST_IP_ADDRESS,
self.listenerport,
timeout=10,
live_port_needed=True),
......@@ -499,7 +473,7 @@ class TestTcpPingDeaf(unittest.TestCase):
def setUp(self):
self.deaflistener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.deaflistener.bind(("127.0.0.1", 0))
self.deaflistener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
self.deaflistenerport = self.deaflistener.getsockname()[1]
def tearDown(self):
......@@ -507,18 +481,18 @@ class TestTcpPingDeaf(unittest.TestCase):
del self.deaflistenerport
def testTcpPingToLocalHostAcceptDeaf(self):
self.failIf(TcpPing("127.0.0.1",
"127.0.0.1",
self.failIf(TcpPing(constants.LOCALHOST_IP_ADDRESS,
constants.LOCALHOST_IP_ADDRESS,
self.deaflistenerport,
timeout=10, # timeout for blocking operations
timeout=constants.TCP_PING_TIMEOUT,
live_port_needed=True), # need successful connect(2)
"successfully connected to deaf listener")
def testTcpPingToLocalHostNoAccept(self):
self.assert_(TcpPing("127.0.0.1",
"127.0.0.1",
self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
constants.LOCALHOST_IP_ADDRESS,
self.deaflistenerport,
timeout=10, # timeout for blocking operations
timeout=constants.TCP_PING_TIMEOUT,
live_port_needed=False), # ECONNREFUSED is OK
"failed to ping alive host on deaf port")
......
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