2008-02-05 20:05:13 +00:00
|
|
|
from base64 import b32decode
|
2007-03-27 23:12:11 +00:00
|
|
|
|
2007-12-03 21:52:42 +00:00
|
|
|
import os
|
|
|
|
|
2007-03-27 23:12:11 +00:00
|
|
|
from twisted.trial import unittest
|
2008-02-05 20:05:13 +00:00
|
|
|
from twisted.internet import defer
|
2007-03-27 23:12:11 +00:00
|
|
|
from twisted.python import log
|
|
|
|
|
2009-05-22 00:38:23 +00:00
|
|
|
from foolscap.api import Tub, Referenceable, fireEventually, flushEventualQueue
|
2007-03-27 23:12:11 +00:00
|
|
|
from twisted.application import service
|
2008-11-22 03:07:27 +00:00
|
|
|
from allmydata.interfaces import InsufficientVersionError
|
2008-06-18 19:24:16 +00:00
|
|
|
from allmydata.introducer.client import IntroducerClient
|
|
|
|
from allmydata.introducer.server import IntroducerService
|
2009-05-19 03:41:01 +00:00
|
|
|
from allmydata.introducer.common import make_index
|
2008-06-18 19:24:16 +00:00
|
|
|
# test compatibility with old introducer .tac files
|
|
|
|
from allmydata.introducer import IntroducerNode
|
2008-06-18 23:58:34 +00:00
|
|
|
from allmydata.introducer import old
|
2008-10-29 04:28:31 +00:00
|
|
|
from allmydata.util import idlib, pollmixin
|
|
|
|
import common_util as testutil
|
2007-03-27 23:12:11 +00:00
|
|
|
|
2007-12-12 01:10:29 +00:00
|
|
|
class FakeNode(Referenceable):
|
2007-03-27 23:12:11 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
class LoggingMultiService(service.MultiService):
|
2007-11-20 01:23:18 +00:00
|
|
|
def log(self, msg, **kw):
|
|
|
|
log.msg(msg, **kw)
|
2007-03-27 23:12:11 +00:00
|
|
|
|
2008-06-18 23:58:34 +00:00
|
|
|
class Node(testutil.SignalMixin, unittest.TestCase):
|
2007-12-03 21:52:42 +00:00
|
|
|
def test_loadable(self):
|
|
|
|
basedir = "introducer.IntroducerNode.test_loadable"
|
|
|
|
os.mkdir(basedir)
|
|
|
|
q = IntroducerNode(basedir)
|
|
|
|
d = fireEventually(None)
|
|
|
|
d.addCallback(lambda res: q.startService())
|
|
|
|
d.addCallback(lambda res: q.when_tub_ready())
|
|
|
|
d.addCallback(lambda res: q.stopService())
|
|
|
|
d.addCallback(flushEventualQueue)
|
|
|
|
return d
|
|
|
|
|
2008-06-18 23:58:34 +00:00
|
|
|
class ServiceMixin:
|
2007-03-27 23:12:11 +00:00
|
|
|
def setUp(self):
|
|
|
|
self.parent = LoggingMultiService()
|
|
|
|
self.parent.startService()
|
|
|
|
def tearDown(self):
|
|
|
|
log.msg("TestIntroducer.tearDown")
|
2007-04-04 23:09:13 +00:00
|
|
|
d = defer.succeed(None)
|
2007-03-27 23:12:11 +00:00
|
|
|
d.addCallback(lambda res: self.parent.stopService())
|
2007-03-28 00:16:13 +00:00
|
|
|
d.addCallback(flushEventualQueue)
|
2007-03-27 23:12:11 +00:00
|
|
|
return d
|
|
|
|
|
2008-10-29 04:15:48 +00:00
|
|
|
class Introducer(ServiceMixin, unittest.TestCase, pollmixin.PollMixin):
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
def test_create(self):
|
2008-02-05 20:05:13 +00:00
|
|
|
ic = IntroducerClient(None, "introducer.furl", "my_nickname",
|
|
|
|
"my_version", "oldest_version")
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
def test_listen(self):
|
2007-12-03 21:52:42 +00:00
|
|
|
i = IntroducerService()
|
2007-03-27 23:12:11 +00:00
|
|
|
i.setServiceParent(self.parent)
|
|
|
|
|
2008-04-23 22:05:39 +00:00
|
|
|
def test_duplicate(self):
|
|
|
|
i = IntroducerService()
|
|
|
|
self.failUnlessEqual(len(i.get_announcements()), 0)
|
|
|
|
self.failUnlessEqual(len(i.get_subscribers()), 0)
|
|
|
|
furl1 = "pb://62ubehyunnyhzs7r6vdonnm2hpi52w6y@192.168.69.247:36106,127.0.0.1:36106/gydnpigj2ja2qr2srq4ikjwnl7xfgbra"
|
|
|
|
furl2 = "pb://ttwwooyunnyhzs7r6vdonnm2hpi52w6y@192.168.69.247:36111,127.0.0.1:36106/ttwwoogj2ja2qr2srq4ikjwnl7xfgbra"
|
|
|
|
ann1 = (furl1, "storage", "RIStorage", "nick1", "ver23", "ver0")
|
|
|
|
ann1b = (furl1, "storage", "RIStorage", "nick1", "ver24", "ver0")
|
|
|
|
ann2 = (furl2, "storage", "RIStorage", "nick2", "ver30", "ver0")
|
|
|
|
i.remote_publish(ann1)
|
|
|
|
self.failUnlessEqual(len(i.get_announcements()), 1)
|
|
|
|
self.failUnlessEqual(len(i.get_subscribers()), 0)
|
|
|
|
i.remote_publish(ann2)
|
|
|
|
self.failUnlessEqual(len(i.get_announcements()), 2)
|
|
|
|
self.failUnlessEqual(len(i.get_subscribers()), 0)
|
|
|
|
i.remote_publish(ann1b)
|
|
|
|
self.failUnlessEqual(len(i.get_announcements()), 2)
|
|
|
|
self.failUnlessEqual(len(i.get_subscribers()), 0)
|
|
|
|
|
2008-10-29 04:15:48 +00:00
|
|
|
class SystemTestMixin(ServiceMixin, pollmixin.PollMixin):
|
2008-04-23 22:05:39 +00:00
|
|
|
|
2008-06-18 23:58:34 +00:00
|
|
|
def setUp(self):
|
|
|
|
ServiceMixin.setUp(self)
|
2007-03-27 23:12:11 +00:00
|
|
|
self.central_tub = tub = Tub()
|
|
|
|
#tub.setOption("logLocalFailures", True)
|
|
|
|
#tub.setOption("logRemoteFailures", True)
|
2009-05-22 00:46:32 +00:00
|
|
|
tub.setOption("expose-remote-exception-types", False)
|
2007-03-27 23:12:11 +00:00
|
|
|
tub.setServiceParent(self.parent)
|
|
|
|
l = tub.listenOn("tcp:0")
|
|
|
|
portnum = l.getPortnum()
|
|
|
|
tub.setLocation("localhost:%d" % portnum)
|
|
|
|
|
2008-06-18 23:58:34 +00:00
|
|
|
class SystemTest(SystemTestMixin, unittest.TestCase):
|
|
|
|
|
|
|
|
def test_system(self):
|
2007-12-03 21:52:42 +00:00
|
|
|
i = IntroducerService()
|
2007-03-27 23:12:11 +00:00
|
|
|
i.setServiceParent(self.parent)
|
2008-06-18 23:58:34 +00:00
|
|
|
self.introducer_furl = self.central_tub.registerReference(i)
|
|
|
|
return self.do_system_test()
|
|
|
|
|
|
|
|
def test_system_oldserver(self):
|
|
|
|
i = old.IntroducerService_V1()
|
|
|
|
i.setServiceParent(self.parent)
|
|
|
|
self.introducer_furl = self.central_tub.registerReference(i)
|
|
|
|
return self.do_system_test()
|
|
|
|
|
|
|
|
def do_system_test(self):
|
|
|
|
|
2007-03-27 23:12:11 +00:00
|
|
|
NUMCLIENTS = 5
|
2008-06-18 23:58:34 +00:00
|
|
|
# we have 5 clients who publish themselves, and an extra one does
|
|
|
|
# which not. When the connections are fully established, all six nodes
|
2008-02-02 02:48:38 +00:00
|
|
|
# should have 5 connections each.
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
clients = []
|
|
|
|
tubs = {}
|
2008-02-02 02:48:38 +00:00
|
|
|
for i in range(NUMCLIENTS+1):
|
2007-03-27 23:12:11 +00:00
|
|
|
tub = Tub()
|
|
|
|
#tub.setOption("logLocalFailures", True)
|
|
|
|
#tub.setOption("logRemoteFailures", True)
|
2009-05-22 00:46:32 +00:00
|
|
|
tub.setOption("expose-remote-exception-types", False)
|
2007-03-27 23:12:11 +00:00
|
|
|
tub.setServiceParent(self.parent)
|
|
|
|
l = tub.listenOn("tcp:0")
|
|
|
|
portnum = l.getPortnum()
|
|
|
|
tub.setLocation("localhost:%d" % portnum)
|
|
|
|
|
2007-12-12 01:10:29 +00:00
|
|
|
n = FakeNode()
|
2008-02-02 02:48:38 +00:00
|
|
|
log.msg("creating client %d: %s" % (i, tub.getShortTubID()))
|
2008-06-18 23:58:34 +00:00
|
|
|
client_class = IntroducerClient
|
|
|
|
if i == 0:
|
|
|
|
client_class = old.IntroducerClient_V1
|
|
|
|
c = client_class(tub, self.introducer_furl,
|
|
|
|
"nickname-%d" % i, "version", "oldest")
|
2008-02-02 02:48:38 +00:00
|
|
|
if i < NUMCLIENTS:
|
|
|
|
node_furl = tub.registerReference(n)
|
2008-02-05 20:05:13 +00:00
|
|
|
c.publish(node_furl, "storage", "ri_name")
|
|
|
|
# the last one does not publish anything
|
|
|
|
|
|
|
|
c.subscribe_to("storage")
|
2007-12-11 03:22:59 +00:00
|
|
|
|
2007-03-27 23:12:11 +00:00
|
|
|
c.setServiceParent(self.parent)
|
|
|
|
clients.append(c)
|
|
|
|
tubs[c] = tub
|
|
|
|
|
2008-02-05 20:05:13 +00:00
|
|
|
def _wait_for_all_connections():
|
2007-12-11 03:22:59 +00:00
|
|
|
for c in clients:
|
2008-02-05 20:05:13 +00:00
|
|
|
if len(c.get_all_connections()) < NUMCLIENTS:
|
|
|
|
return False
|
|
|
|
return True
|
2008-02-05 23:37:58 +00:00
|
|
|
d = self.poll(_wait_for_all_connections)
|
2007-03-27 23:12:11 +00:00
|
|
|
|
2007-09-19 18:50:13 +00:00
|
|
|
def _check1(res):
|
|
|
|
log.msg("doing _check1")
|
2007-03-27 23:12:11 +00:00
|
|
|
for c in clients:
|
2008-02-05 20:05:13 +00:00
|
|
|
self.failUnless(c.connected_to_introducer())
|
|
|
|
self.failUnlessEqual(len(c.get_all_connections()), NUMCLIENTS)
|
|
|
|
self.failUnlessEqual(len(c.get_all_peerids()), NUMCLIENTS)
|
|
|
|
self.failUnlessEqual(len(c.get_all_connections_for("storage")),
|
|
|
|
NUMCLIENTS)
|
2008-09-06 05:07:00 +00:00
|
|
|
nodeid0 = b32decode(tubs[clients[0]].tubID.upper())
|
|
|
|
self.failUnlessEqual(c.get_nickname_for_peerid(nodeid0),
|
|
|
|
"nickname-0")
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_check1)
|
2008-02-05 20:05:13 +00:00
|
|
|
|
2007-12-11 03:22:59 +00:00
|
|
|
origin_c = clients[0]
|
2007-09-19 18:50:13 +00:00
|
|
|
def _disconnect_somebody_else(res):
|
2007-05-08 02:10:24 +00:00
|
|
|
# now disconnect somebody's connection to someone else
|
2008-02-05 20:05:13 +00:00
|
|
|
current_counter = origin_c.counter
|
|
|
|
victim_nodeid = b32decode(tubs[clients[1]].tubID.upper())
|
2008-04-22 19:54:16 +00:00
|
|
|
log.msg(" disconnecting %s->%s" %
|
|
|
|
(tubs[origin_c].tubID,
|
|
|
|
idlib.shortnodeid_b2a(victim_nodeid)))
|
2008-02-05 20:05:13 +00:00
|
|
|
origin_c.debug_disconnect_from_peerid(victim_nodeid)
|
2007-03-27 23:12:11 +00:00
|
|
|
log.msg(" did disconnect")
|
2008-02-05 20:05:13 +00:00
|
|
|
|
|
|
|
# then wait until something changes, which ought to be them
|
|
|
|
# noticing the loss
|
|
|
|
def _compare():
|
|
|
|
return current_counter != origin_c.counter
|
2008-02-05 23:37:58 +00:00
|
|
|
return self.poll(_compare)
|
2008-02-05 20:05:13 +00:00
|
|
|
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_disconnect_somebody_else)
|
2008-02-05 20:05:13 +00:00
|
|
|
|
|
|
|
# and wait for them to reconnect
|
2008-02-05 23:37:58 +00:00
|
|
|
d.addCallback(lambda res: self.poll(_wait_for_all_connections))
|
2007-09-19 18:50:13 +00:00
|
|
|
def _check2(res):
|
|
|
|
log.msg("doing _check2")
|
2007-03-27 23:12:11 +00:00
|
|
|
for c in clients:
|
2008-02-05 20:05:13 +00:00
|
|
|
self.failUnlessEqual(len(c.get_all_connections()), NUMCLIENTS)
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_check2)
|
2008-02-05 20:05:13 +00:00
|
|
|
|
2007-09-19 18:50:13 +00:00
|
|
|
def _disconnect_yourself(res):
|
2007-12-11 03:22:59 +00:00
|
|
|
# now disconnect somebody's connection to themselves.
|
2008-02-05 20:05:13 +00:00
|
|
|
current_counter = origin_c.counter
|
|
|
|
victim_nodeid = b32decode(tubs[clients[0]].tubID.upper())
|
2008-04-22 19:54:16 +00:00
|
|
|
log.msg(" disconnecting %s->%s" %
|
|
|
|
(tubs[origin_c].tubID,
|
|
|
|
idlib.shortnodeid_b2a(victim_nodeid)))
|
2008-02-05 20:05:13 +00:00
|
|
|
origin_c.debug_disconnect_from_peerid(victim_nodeid)
|
2007-12-11 03:22:59 +00:00
|
|
|
log.msg(" did disconnect from self")
|
2008-02-05 20:05:13 +00:00
|
|
|
|
|
|
|
def _compare():
|
|
|
|
return current_counter != origin_c.counter
|
2008-02-05 23:37:58 +00:00
|
|
|
return self.poll(_compare)
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_disconnect_yourself)
|
2008-02-05 20:05:13 +00:00
|
|
|
|
2008-02-05 23:37:58 +00:00
|
|
|
d.addCallback(lambda res: self.poll(_wait_for_all_connections))
|
2007-09-19 18:50:13 +00:00
|
|
|
def _check3(res):
|
|
|
|
log.msg("doing _check3")
|
2007-03-27 23:12:11 +00:00
|
|
|
for c in clients:
|
2008-02-05 20:05:13 +00:00
|
|
|
self.failUnlessEqual(len(c.get_all_connections_for("storage")),
|
|
|
|
NUMCLIENTS)
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_check3)
|
|
|
|
def _shutdown_introducer(res):
|
|
|
|
# now shut down the introducer. We do this by shutting down the
|
|
|
|
# tub it's using. Nobody's connections (to each other) should go
|
|
|
|
# down. All clients should notice the loss, and no other errors
|
|
|
|
# should occur.
|
|
|
|
log.msg("shutting down the introducer")
|
|
|
|
return self.central_tub.disownServiceParent()
|
|
|
|
d.addCallback(_shutdown_introducer)
|
2008-02-05 20:05:13 +00:00
|
|
|
def _wait_for_introducer_loss():
|
|
|
|
for c in clients:
|
|
|
|
if c.connected_to_introducer():
|
|
|
|
return False
|
|
|
|
return True
|
2008-02-05 23:37:58 +00:00
|
|
|
d.addCallback(lambda res: self.poll(_wait_for_introducer_loss))
|
2008-02-05 20:05:13 +00:00
|
|
|
|
2007-09-19 18:50:13 +00:00
|
|
|
def _check4(res):
|
|
|
|
log.msg("doing _check4")
|
|
|
|
for c in clients:
|
2008-02-05 20:05:13 +00:00
|
|
|
self.failUnlessEqual(len(c.get_all_connections_for("storage")),
|
|
|
|
NUMCLIENTS)
|
|
|
|
self.failIf(c.connected_to_introducer())
|
2007-09-19 18:50:13 +00:00
|
|
|
d.addCallback(_check4)
|
2007-03-27 23:12:11 +00:00
|
|
|
return d
|
2008-11-22 03:07:27 +00:00
|
|
|
|
|
|
|
class TooNewServer(IntroducerService):
|
|
|
|
VERSION = { "http://allmydata.org/tahoe/protocols/introducer/v999":
|
|
|
|
{ },
|
|
|
|
"application-version": "greetings from the crazy future",
|
|
|
|
}
|
|
|
|
|
|
|
|
class NonV1Server(SystemTestMixin, unittest.TestCase):
|
|
|
|
# if the 1.3.0 client connects to a server that doesn't provide the 'v1'
|
|
|
|
# protocol, it is supposed to provide a useful error instead of a weird
|
|
|
|
# exception.
|
|
|
|
|
|
|
|
def test_failure(self):
|
|
|
|
i = TooNewServer()
|
|
|
|
i.setServiceParent(self.parent)
|
|
|
|
self.introducer_furl = self.central_tub.registerReference(i)
|
|
|
|
|
|
|
|
tub = Tub()
|
2009-05-22 00:46:32 +00:00
|
|
|
tub.setOption("expose-remote-exception-types", False)
|
2008-11-22 03:07:27 +00:00
|
|
|
tub.setServiceParent(self.parent)
|
|
|
|
l = tub.listenOn("tcp:0")
|
|
|
|
portnum = l.getPortnum()
|
|
|
|
tub.setLocation("localhost:%d" % portnum)
|
|
|
|
|
|
|
|
n = FakeNode()
|
|
|
|
c = IntroducerClient(tub, self.introducer_furl,
|
|
|
|
"nickname-client", "version", "oldest")
|
|
|
|
c.subscribe_to("storage")
|
|
|
|
|
|
|
|
c.setServiceParent(self.parent)
|
|
|
|
|
|
|
|
# now we wait for it to connect and notice the bad version
|
|
|
|
|
|
|
|
def _got_bad():
|
|
|
|
return bool(c._introducer_error) or bool(c._publisher)
|
|
|
|
d = self.poll(_got_bad)
|
|
|
|
def _done(res):
|
|
|
|
self.failUnless(c._introducer_error)
|
|
|
|
self.failUnless(c._introducer_error.check(InsufficientVersionError))
|
|
|
|
d.addCallback(_done)
|
|
|
|
return d
|
|
|
|
|
2009-05-19 03:41:01 +00:00
|
|
|
class Index(unittest.TestCase):
|
|
|
|
def test_make_index(self):
|
|
|
|
# make sure we have a working base64.b32decode. The one in
|
|
|
|
# python2.4.[01] was broken.
|
|
|
|
ann = ('pb://t5g7egomnnktbpydbuijt6zgtmw4oqi5@127.0.0.1:51857/hfzv36i',
|
|
|
|
'storage', 'RIStorageServer.tahoe.allmydata.com',
|
|
|
|
'plancha', 'allmydata-tahoe/1.4.1', '1.0.0')
|
|
|
|
(nodeid, service_name) = make_index(ann)
|
|
|
|
self.failUnlessEqual(nodeid, "\x9fM\xf2\x19\xcckU0\xbf\x03\r\x10\x99\xfb&\x9b-\xc7A\x1d")
|
|
|
|
self.failUnlessEqual(service_name, "storage")
|
|
|
|
|