Revision 16abfbc2
b/INSTALL | ||
---|---|---|
22 | 22 |
http://www.openssh.com/portable.html |
23 | 23 |
- bridge utilities |
24 | 24 |
http://bridge.sourceforge.net/ |
25 |
- fping |
|
26 |
http://fping.sourceforge.net/ |
|
27 | 25 |
- iproute2 |
28 | 26 |
http://developer.osdl.org/dev/iproute2 |
29 | 27 |
- arping (part of iputils package) |
b/daemons/ganeti-master | ||
---|---|---|
106 | 106 |
"""Starts the master. |
107 | 107 |
|
108 | 108 |
""" |
109 |
result = utils.RunCmd(["fping", "-q", master_ip])
|
|
110 |
if not result.failed:
|
|
111 |
r2 = utils.RunCmd(["fping", "-q", "-S127.0.0.1", master_ip])
|
|
112 |
if not r2.failed:
|
|
109 |
if utils.TcpPing(utils.HostInfo().name, master_ip,
|
|
110 |
constants.DEFAULT_NODED_PORT):
|
|
111 |
if utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, master_ip,
|
|
112 |
constants.DEFAULT_NODED_PORT):
|
|
113 | 113 |
# we already have the ip: |
114 | 114 |
if debug: |
115 | 115 |
sys.stderr.write("Notice: already started.\n") |
b/daemons/ganeti-noded | ||
---|---|---|
38 | 38 |
from ganeti import objects |
39 | 39 |
from ganeti import errors |
40 | 40 |
from ganeti import ssconf |
41 |
from ganeti import utils |
|
41 | 42 |
|
42 | 43 |
from twisted.spread import pb |
43 | 44 |
from twisted.internet import reactor |
... | ... | |
347 | 348 |
# node -------------------------- |
348 | 349 |
|
349 | 350 |
@staticmethod |
351 |
def perspective_node_tcp_ping(params): |
|
352 |
"""Do a TcpPing on the remote node. |
|
353 |
|
|
354 |
""" |
|
355 |
return utils.TcpPing(params[0], params[1], params[2], |
|
356 |
timeout=params[3], live_port_needed=params[4]) |
|
357 |
|
|
358 |
@staticmethod |
|
350 | 359 |
def perspective_node_info(params): |
351 | 360 |
"""Query node information. |
352 | 361 |
|
b/doc/install.sgml | ||
---|---|---|
378 | 378 |
</listitem> |
379 | 379 |
<listitem> |
380 | 380 |
<simpara><ulink |
381 |
url="http://fping.sourceforge.net/">fping</ulink></simpara> |
|
382 |
</listitem> |
|
383 |
<listitem> |
|
384 |
<simpara><ulink |
|
385 | 381 |
url="http://developer.osdl.org/dev/iproute2">iproute2</ulink></simpara> |
386 | 382 |
</listitem> |
387 | 383 |
<listitem> |
... | ... | |
429 | 425 |
</formalpara> |
430 | 426 |
<screen> |
431 | 427 |
# apt-get install lvm2 ssh bridge-utils iproute iputils-arping \ |
432 |
fping python2.4 python-twisted-core python-pyopenssl openssl \
|
|
428 |
python2.4 python-twisted-core python-pyopenssl openssl \ |
|
433 | 429 |
mdadm |
434 | 430 |
</screen> |
435 | 431 |
|
b/lib/cmdlib.py | ||
---|---|---|
567 | 567 |
|
568 | 568 |
self.clustername = clustername = utils.HostInfo(self.op.cluster_name) |
569 | 569 |
|
570 |
result = utils.RunCmd(["fping", "-S127.0.0.1", "-q", hostname.ip])
|
|
571 |
if result.failed:
|
|
570 |
if not utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, hostname.ip,
|
|
571 |
constants.DEFAULT_NODED_PORT):
|
|
572 | 572 |
raise errors.OpPrereqError("Inconsistency: this host's name resolves" |
573 | 573 |
" to %s,\nbut this ip address does not" |
574 | 574 |
" belong to this host." |
... | ... | |
577 | 577 |
secondary_ip = getattr(self.op, "secondary_ip", None) |
578 | 578 |
if secondary_ip and not utils.IsValidIP(secondary_ip): |
579 | 579 |
raise errors.OpPrereqError("Invalid secondary ip given") |
580 |
if secondary_ip and secondary_ip != hostname.ip: |
|
581 |
result = utils.RunCmd(["fping", "-S127.0.0.1", "-q", secondary_ip]) |
|
582 |
if result.failed: |
|
583 |
raise errors.OpPrereqError("You gave %s as secondary IP,\n" |
|
584 |
"but it does not belong to this host." % |
|
585 |
secondary_ip) |
|
580 |
if (secondary_ip and |
|
581 |
secondary_ip != hostname.ip and |
|
582 |
(not utils.TcpPing(constants.LOCALHOST_IP_ADDRESS, secondary_ip, |
|
583 |
constants.DEFAULT_NODED_PORT))): |
|
584 |
raise errors.OpPrereqError("You gave %s as secondary IP,\n" |
|
585 |
"but it does not belong to this host." % |
|
586 |
secondary_ip) |
|
586 | 587 |
self.secondary_ip = secondary_ip |
587 | 588 |
|
588 | 589 |
# checks presence of the volume group given |
... | ... | |
1428 | 1429 |
" new node doesn't have one") |
1429 | 1430 |
|
1430 | 1431 |
# checks reachablity |
1431 |
command = ["fping", "-q", primary_ip]
|
|
1432 |
result = utils.RunCmd(command)
|
|
1433 |
if result.failed:
|
|
1432 |
if not utils.TcpPing(utils.HostInfo().name,
|
|
1433 |
primary_ip,
|
|
1434 |
constants.DEFAULT_NODED_PORT):
|
|
1434 | 1435 |
raise errors.OpPrereqError("Node not reachable by ping") |
1435 | 1436 |
|
1436 | 1437 |
if not newbie_singlehomed: |
1437 | 1438 |
# check reachability from my secondary ip to newbie's secondary ip |
1438 |
command = ["fping", "-S%s" % myself.secondary_ip, "-q", secondary_ip] |
|
1439 |
result = utils.RunCmd(command) |
|
1440 |
if result.failed: |
|
1441 |
raise errors.OpPrereqError("Node secondary ip not reachable by ping") |
|
1439 |
if not utils.TcpPing(myself.secondary_ip, |
|
1440 |
secondary_ip, |
|
1441 |
constants.DEFAULT_NODED_PORT): |
|
1442 |
raise errors.OpPrereqError( |
|
1443 |
"Node secondary ip not reachable by TCP based ping to noded port") |
|
1442 | 1444 |
|
1443 | 1445 |
self.new_node = objects.Node(name=node, |
1444 | 1446 |
primary_ip=primary_ip, |
... | ... | |
1529 | 1531 |
self.cfg.GetHostKey()) |
1530 | 1532 |
|
1531 | 1533 |
if new_node.secondary_ip != new_node.primary_ip: |
1532 |
result = ssh.SSHCall(node, "root", |
|
1533 |
"fping -S 127.0.0.1 -q %s" % new_node.secondary_ip) |
|
1534 |
if result.failed: |
|
1534 |
if not rpc.call_node_tcp_ping(new_node.name, |
|
1535 |
constants.LOCALHOST_IP_ADDRESS, |
|
1536 |
new_node.secondary_ip, |
|
1537 |
constants.DEFAULT_NODED_PORT, |
|
1538 |
10, False): |
|
1535 | 1539 |
raise errors.OpExecError("Node claims it doesn't have the" |
1536 | 1540 |
" secondary ip you gave (%s).\n" |
1537 | 1541 |
"Please fix and re-run this command." % |
... | ... | |
2844 | 2848 |
" adding an instance in start mode") |
2845 | 2849 |
|
2846 | 2850 |
if self.op.ip_check: |
2847 |
command = ["fping", "-q", hostname1.ip] |
|
2848 |
result = utils.RunCmd(command) |
|
2849 |
if not result.failed: |
|
2850 |
raise errors.OpPrereqError("IP address %s of instance %s already" |
|
2851 |
" in use" % (hostname1.ip, instance_name)) |
|
2851 |
if utils.TcpPing(utils.HostInfo().name, hostname1.ip, |
|
2852 |
constants.DEFAULT_NODED_PORT): |
|
2853 |
raise errors.OpPrereqError("IP %s of instance %s already in use" % |
|
2854 |
(hostname1.ip, instance_name)) |
|
2852 | 2855 |
|
2853 | 2856 |
# bridge verification |
2854 | 2857 |
bridge = getattr(self.op, "bridge", None) |
b/lib/constants.py | ||
---|---|---|
106 | 106 |
# others |
107 | 107 |
DEFAULT_BRIDGE = "xen-br0" |
108 | 108 |
SYNC_SPEED = 30 * 1024 |
109 |
LOCALHOST_IP_ADDRESS="127.0.0.1" |
|
110 |
TCP_PING_TIMEOUT = 10 |
b/lib/rpc.py | ||
---|---|---|
373 | 373 |
return c.getresult() |
374 | 374 |
|
375 | 375 |
|
376 |
def call_node_tcp_ping(node, source, target, port, timeout, live_port_needed): |
|
377 |
"""Do a TcpPing on the remote node |
|
378 |
|
|
379 |
This is a single-node call. |
|
380 |
""" |
|
381 |
c = Client("node_tcp_ping", [source, target, port, timeout, |
|
382 |
live_port_needed]) |
|
383 |
c.connect(node) |
|
384 |
c.run() |
|
385 |
return c.getresult().get(node, False) |
|
386 |
|
|
387 |
|
|
376 | 388 |
def call_node_info(node_list, vg_name): |
377 | 389 |
"""Return node information. |
378 | 390 |
|
b/lib/utils.py | ||
---|---|---|
37 | 37 |
from ganeti import logger |
38 | 38 |
from ganeti import errors |
39 | 39 |
|
40 |
|
|
40 | 41 |
_locksheld = [] |
41 | 42 |
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$') |
42 | 43 |
|
... | ... | |
782 | 783 |
return ' '.join([ShellQuote(i) for i in args]) |
783 | 784 |
|
784 | 785 |
|
785 |
def _ParseIpOutput(output): |
|
786 |
"""Parsing code for GetLocalIPAddresses(). |
|
787 |
|
|
788 |
This function is split out, so we can unit test it. |
|
789 |
|
|
790 |
""" |
|
791 |
re_ip = re.compile('^(\d+\.\d+\.\d+\.\d+)(?:/\d+)$') |
|
792 |
|
|
793 |
ips = [] |
|
794 |
for line in output.splitlines(False): |
|
795 |
fields = line.split() |
|
796 |
if len(line) < 4: |
|
797 |
continue |
|
798 |
m = re_ip.match(fields[3]) |
|
799 |
if m: |
|
800 |
ips.append(m.group(1)) |
|
801 |
|
|
802 |
return ips |
|
803 |
|
|
804 |
|
|
805 |
def GetLocalIPAddresses(): |
|
806 |
"""Gets a list of all local IP addresses. |
|
807 |
|
|
808 |
Should this break one day, a small Python module written in C could |
|
809 |
use the API call getifaddrs(). |
|
810 |
|
|
811 |
""" |
|
812 |
result = RunCmd(["ip", "-family", "inet", "-oneline", "addr", "show"]) |
|
813 |
if result.failed: |
|
814 |
raise errors.OpExecError("Command '%s' failed, error: %s," |
|
815 |
" output: %s" % (result.cmd, result.fail_reason, result.output)) |
|
816 |
|
|
817 |
return _ParseIpOutput(result.output) |
|
818 |
|
|
819 | 786 |
|
820 |
def TcpPing(source, target, port, timeout=10, live_port_needed=True):
|
|
787 |
def TcpPing(source, target, port, timeout=10, live_port_needed=False):
|
|
821 | 788 |
"""Simple ping implementation using TCP connect(2). |
822 | 789 |
|
823 | 790 |
Try to do a TCP connect(2) from the specified source IP to the specified |
b/test/ganeti.config_unittest.py | ||
---|---|---|
55 | 55 |
|
56 | 56 |
def _init_cluster(self, cfg): |
57 | 57 |
"""Initializes the cfg object""" |
58 |
cfg.InitConfig(utils.HostInfo().name, '127.0.0.1', None, '', 'aa:00:00', |
|
59 |
'xenvg', constants.DEFAULT_BRIDGE) |
|
58 |
cfg.InitConfig(utils.HostInfo().name, constants.LOCALHOST_IP_ADDRESS, |
|
59 |
None, '', 'aa:00:00', 'xenvg', |
|
60 |
constants.DEFAULT_BRIDGE) |
|
60 | 61 |
|
61 | 62 |
def _create_instance(self): |
62 | 63 |
"""Create and return an instance object""" |
b/test/ganeti.utils_unittest.py | ||
---|---|---|
31 | 31 |
import shutil |
32 | 32 |
|
33 | 33 |
import ganeti |
34 |
from ganeti import constants |
|
34 | 35 |
from ganeti.utils import IsProcessAlive, Lock, Unlock, RunCmd, \ |
35 | 36 |
RemoveFile, CheckDict, MatchNameComponent, FormatUnit, \ |
36 | 37 |
ParseUnit, AddAuthorizedKey, RemoveAuthorizedKey, \ |
37 |
ShellQuote, ShellQuoteArgs, _ParseIpOutput, TcpPing, \ |
|
38 |
ListVisibleFiles |
|
38 |
ShellQuote, ShellQuoteArgs, TcpPing, ListVisibleFiles |
|
39 | 39 |
from ganeti.errors import LockError, UnitParseError |
40 | 40 |
|
41 | 41 |
|
... | ... | |
445 | 445 |
self.assertEqual(ShellQuoteArgs(['a', 'b\'', 'c']), "a 'b'\\\''' c") |
446 | 446 |
|
447 | 447 |
|
448 |
class TestIpAdressList(unittest.TestCase): |
|
449 |
"""Test case for local IP addresses""" |
|
450 |
|
|
451 |
def _test(self, output, required): |
|
452 |
ips = _ParseIpOutput(output) |
|
453 |
|
|
454 |
# Sort the output, so our check below works in all cases |
|
455 |
ips.sort() |
|
456 |
required.sort() |
|
457 |
|
|
458 |
self.assertEqual(required, ips) |
|
459 |
|
|
460 |
def testSingleIpAddress(self): |
|
461 |
output = \ |
|
462 |
("3: lo inet 127.0.0.1/8 brd 127.255.255.255 scope host lo\n" |
|
463 |
"5: eth0 inet 10.0.0.1/24 brd 172.30.15.127 scope global eth0\n") |
|
464 |
self._test(output, ['127.0.0.1', '10.0.0.1']) |
|
465 |
|
|
466 |
def testMultipleIpAddresses(self): |
|
467 |
output = \ |
|
468 |
("3: lo inet 127.0.0.1/8 brd 127.255.255.255 scope host lo\n" |
|
469 |
"5: eth0 inet 10.0.0.1/24 brd 172.30.15.127 scope global eth0\n" |
|
470 |
"5: eth0 inet 1.2.3.4/8 brd 1.255.255.255 scope global eth0:test\n") |
|
471 |
self._test(output, ['127.0.0.1', '10.0.0.1', '1.2.3.4']) |
|
472 |
|
|
473 |
|
|
474 | 448 |
class TestTcpPing(unittest.TestCase): |
475 | 449 |
"""Testcase for TCP version of ping - against listen(2)ing port""" |
476 | 450 |
|
477 | 451 |
def setUp(self): |
478 | 452 |
self.listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
479 |
self.listener.bind(("127.0.0.1", 0))
|
|
453 |
self.listener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
|
|
480 | 454 |
self.listenerport = self.listener.getsockname()[1] |
481 | 455 |
self.listener.listen(1) |
482 | 456 |
|
... | ... | |
486 | 460 |
del self.listenerport |
487 | 461 |
|
488 | 462 |
def testTcpPingToLocalHostAccept(self): |
489 |
self.assert_(TcpPing("127.0.0.1",
|
|
490 |
"127.0.0.1",
|
|
463 |
self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
|
|
464 |
constants.LOCALHOST_IP_ADDRESS,
|
|
491 | 465 |
self.listenerport, |
492 | 466 |
timeout=10, |
493 | 467 |
live_port_needed=True), |
... | ... | |
499 | 473 |
|
500 | 474 |
def setUp(self): |
501 | 475 |
self.deaflistener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
502 |
self.deaflistener.bind(("127.0.0.1", 0))
|
|
476 |
self.deaflistener.bind((constants.LOCALHOST_IP_ADDRESS, 0))
|
|
503 | 477 |
self.deaflistenerport = self.deaflistener.getsockname()[1] |
504 | 478 |
|
505 | 479 |
def tearDown(self): |
... | ... | |
507 | 481 |
del self.deaflistenerport |
508 | 482 |
|
509 | 483 |
def testTcpPingToLocalHostAcceptDeaf(self): |
510 |
self.failIf(TcpPing("127.0.0.1",
|
|
511 |
"127.0.0.1",
|
|
484 |
self.failIf(TcpPing(constants.LOCALHOST_IP_ADDRESS,
|
|
485 |
constants.LOCALHOST_IP_ADDRESS,
|
|
512 | 486 |
self.deaflistenerport, |
513 |
timeout=10, # timeout for blocking operations
|
|
487 |
timeout=constants.TCP_PING_TIMEOUT,
|
|
514 | 488 |
live_port_needed=True), # need successful connect(2) |
515 | 489 |
"successfully connected to deaf listener") |
516 | 490 |
|
517 | 491 |
def testTcpPingToLocalHostNoAccept(self): |
518 |
self.assert_(TcpPing("127.0.0.1",
|
|
519 |
"127.0.0.1",
|
|
492 |
self.assert_(TcpPing(constants.LOCALHOST_IP_ADDRESS,
|
|
493 |
constants.LOCALHOST_IP_ADDRESS,
|
|
520 | 494 |
self.deaflistenerport, |
521 |
timeout=10, # timeout for blocking operations
|
|
495 |
timeout=constants.TCP_PING_TIMEOUT,
|
|
522 | 496 |
live_port_needed=False), # ECONNREFUSED is OK |
523 | 497 |
"failed to ping alive host on deaf port") |
524 | 498 |
|
Also available in: Unified diff