2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
import re
|
|
|
|
from zope.interface import implements
|
2007-03-23 23:15:57 +00:00
|
|
|
from twisted.application import service
|
|
|
|
from twisted.python import log
|
2007-03-27 23:12:11 +00:00
|
|
|
from foolscap import Referenceable
|
|
|
|
from allmydata.interfaces import RIIntroducer, RIIntroducerClient
|
|
|
|
from allmydata.util import idlib, observer
|
2007-03-23 23:15:57 +00:00
|
|
|
|
2007-05-08 02:10:24 +00:00
|
|
|
def ignoreDeadRef(target, *args, **kwargs):
|
|
|
|
from twisted.internet import error
|
|
|
|
from foolscap import DeadReferenceError
|
|
|
|
d = target.callRemote(*args, **kwargs)
|
|
|
|
def _ignore(f):
|
|
|
|
f.trap(error.ConnectionDone, error.ConnectError,
|
|
|
|
error.ConnectionLost, DeadReferenceError)
|
|
|
|
d.addErrback(_ignore)
|
|
|
|
|
2007-03-23 23:15:57 +00:00
|
|
|
class Introducer(service.MultiService, Referenceable):
|
|
|
|
implements(RIIntroducer)
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
service.MultiService.__init__(self)
|
|
|
|
self.nodes = set()
|
|
|
|
self.pburls = set()
|
|
|
|
|
|
|
|
def remote_hello(self, node, pburl):
|
2007-03-27 23:12:11 +00:00
|
|
|
log.msg("introducer: new contact at %s, node is %s" % (pburl, node))
|
2007-03-23 23:15:57 +00:00
|
|
|
def _remove():
|
2007-03-27 23:12:11 +00:00
|
|
|
log.msg(" introducer: removing %s %s" % (node, pburl))
|
2007-03-23 23:15:57 +00:00
|
|
|
self.nodes.remove(node)
|
|
|
|
self.pburls.remove(pburl)
|
2007-05-08 02:10:24 +00:00
|
|
|
for othernode in self.nodes:
|
|
|
|
#othernode.callRemote("lost_peers", set([pburl]))
|
|
|
|
#othernode.callRemoteOnly("lost_peers", set([pburl]))
|
|
|
|
ignoreDeadRef(othernode, "lost_peers", set([pburl]))
|
|
|
|
|
2007-03-23 23:15:57 +00:00
|
|
|
node.notifyOnDisconnect(_remove)
|
|
|
|
self.pburls.add(pburl)
|
|
|
|
node.callRemote("new_peers", self.pburls)
|
|
|
|
for othernode in self.nodes:
|
|
|
|
othernode.callRemote("new_peers", set([pburl]))
|
|
|
|
self.nodes.add(node)
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
class IntroducerClient(service.Service, Referenceable):
|
|
|
|
implements(RIIntroducerClient)
|
|
|
|
|
|
|
|
def __init__(self, tub, introducer_pburl, my_pburl):
|
|
|
|
self.tub = tub
|
|
|
|
self.introducer_pburl = introducer_pburl
|
|
|
|
self.my_pburl = my_pburl
|
|
|
|
|
|
|
|
self.connections = {} # k: nodeid, v: ref
|
|
|
|
self.reconnectors = {} # k: PBURL, v: reconnector
|
|
|
|
|
2007-05-08 02:10:24 +00:00
|
|
|
self.change_observers = observer.ObserverList()
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
def startService(self):
|
|
|
|
self.introducer_reconnector = self.tub.connectTo(self.introducer_pburl,
|
|
|
|
self._got_introducer)
|
|
|
|
|
|
|
|
def log(self, msg):
|
|
|
|
self.parent.log(msg)
|
|
|
|
|
|
|
|
def remote_new_peers(self, pburls):
|
|
|
|
for pburl in pburls:
|
|
|
|
self._new_peer(pburl)
|
|
|
|
|
2007-05-08 02:10:24 +00:00
|
|
|
def remote_lost_peers(self, pburls):
|
|
|
|
for pburl in pburls:
|
|
|
|
self._lost_peer(pburl)
|
|
|
|
|
2007-03-27 23:12:11 +00:00
|
|
|
def stopService(self):
|
|
|
|
service.Service.stopService(self)
|
|
|
|
self.introducer_reconnector.stopConnecting()
|
|
|
|
for reconnector in self.reconnectors.itervalues():
|
|
|
|
reconnector.stopConnecting()
|
2007-05-08 02:10:24 +00:00
|
|
|
self.reconnectors = {}
|
2007-03-27 23:12:11 +00:00
|
|
|
|
|
|
|
def _new_peer(self, pburl):
|
|
|
|
if pburl in self.reconnectors:
|
|
|
|
return
|
2007-03-29 18:16:29 +00:00
|
|
|
# TODO: rather than using the TubID as a nodeid, we should use
|
|
|
|
# something else. The thing that requires the least additional
|
|
|
|
# mappings is to use the foolscap "identifier" (the last component of
|
|
|
|
# the pburl), since these are unguessable. Before we can do that,
|
|
|
|
# though, we need a way to conveniently make these identifiers
|
|
|
|
# persist from one run of the client program to the next. Also, using
|
|
|
|
# the foolscap identifier would mean that anyone who knows the name
|
|
|
|
# of the node also has all the secrets they need to contact and use
|
|
|
|
# them, which may or may not be what we want.
|
2007-03-27 23:12:11 +00:00
|
|
|
m = re.match(r'pb://(\w+)@', pburl)
|
|
|
|
assert m
|
|
|
|
nodeid = idlib.a2b(m.group(1))
|
|
|
|
def _got_peer(rref):
|
|
|
|
self.log(" connected to(%s)" % idlib.b2a(nodeid))
|
2007-05-08 02:10:24 +00:00
|
|
|
self.change_observers.notify("add", nodeid, rref)
|
2007-03-27 23:12:11 +00:00
|
|
|
self.connections[nodeid] = rref
|
|
|
|
def _lost():
|
|
|
|
# TODO: notifyOnDisconnect uses eventually(), but connects do
|
|
|
|
# not. Could this cause a problem?
|
|
|
|
del self.connections[nodeid]
|
|
|
|
rref.notifyOnDisconnect(_lost)
|
|
|
|
self.log(" connecting to(%s)" % pburl)
|
|
|
|
self.reconnectors[pburl] = self.tub.connectTo(pburl, _got_peer)
|
|
|
|
|
|
|
|
def _got_introducer(self, introducer):
|
|
|
|
self.log(" introducing ourselves: %s, %s" % (self, self.my_pburl))
|
|
|
|
d = introducer.callRemote("hello",
|
|
|
|
node=self,
|
|
|
|
pburl=self.my_pburl)
|
|
|
|
|
2007-05-08 02:10:24 +00:00
|
|
|
def notify_on_change(self, cb):
|
|
|
|
"""Register a callback that will be fired (with ('add',nodeid,rref)
|
|
|
|
or ('remove',pburl) ) when a new connection is established or a peer
|
|
|
|
is lost. This is used by the unit tests."""
|
|
|
|
self.change_observers.subscribe(cb)
|
2007-03-27 23:12:11 +00:00
|
|
|
|
2007-05-08 02:10:24 +00:00
|
|
|
def _lost_peer(self, pburl):
|
|
|
|
if pburl in self.reconnectors:
|
|
|
|
self.reconnectors[pburl].stopConnecting()
|
|
|
|
del self.reconnectors[pburl]
|
|
|
|
self.change_observers.notify("remove", pburl)
|
|
|
|
# TODO: we don't currently bother to terminate any connections we
|
|
|
|
# might have to this peer. The assumption is that, since the
|
|
|
|
# introducer lost their connection to this peer, we'll probably lose
|
|
|
|
# our connection too. Also, foolscap doesn't currently provide a
|
|
|
|
# clean way to terminate a given connection.
|