From 79c45f5f4d7d311e005054bec582158e93f00722 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Sat, 2 Dec 2006 19:37:31 -0700
Subject: [PATCH] rearrange node startup again, allowing Tub.registerReference
 to happen in startService rather than in some later turn. Also add a
 'local_ip' file with which you can override the published IP address of the
 node

---
 allmydata/client.py           | 16 +++++---------
 allmydata/node.py             | 21 ++++++++++++-------
 allmydata/queen.py            |  7 ++++++-
 allmydata/test/test_system.py | 22 +++++++++++++++++---
 allmydata/util/iputil.py      | 39 ++++++-----------------------------
 5 files changed, 50 insertions(+), 55 deletions(-)

diff --git a/allmydata/client.py b/allmydata/client.py
index df17ac72..641b2e43 100644
--- a/allmydata/client.py
+++ b/allmydata/client.py
@@ -7,12 +7,7 @@ from zope.interface import implements
 from allmydata.interfaces import RIClient
 from allmydata import node
 
-from twisted.internet import defer, reactor
-# this BlockingResolver is because otherwise unit tests must sometimes deal
-# with a leftover DNS lookup thread. I'd prefer to not do this, and use the
-# default ThreadedResolver
-from twisted.internet.base import BlockingResolver
-reactor.installResolver(BlockingResolver())
+from twisted.internet import defer
 
 from allmydata.storageserver import StorageServer
 from allmydata.upload import Uploader
@@ -34,7 +29,10 @@ class Client(node.Node, Referenceable):
         self.add_service(Uploader())
         self.queen_pburl = None
         self.queen_connector = None
-        self.my_pburl = None
+
+    def tub_ready(self):
+        self.my_pburl = self.tub.registerReference(self)
+        self.maybe_connect_to_queen()
 
     def set_queen_pburl(self, queen_pburl):
         self.queen_pburl = queen_pburl
@@ -53,10 +51,6 @@ class Client(node.Node, Referenceable):
         self.queen_connector = self.tub.connectTo(self.queen_pburl,
                                                   self._got_queen)
 
-    def tub_ready(self, tub):
-        self.my_pburl = self.tub.registerReference(self)
-        self.maybe_connect_to_queen()
-
     def stopService(self):
         if self.queen_connector:
             self.queen_connector.stopConnecting()
diff --git a/allmydata/node.py b/allmydata/node.py
index 201b7540..7a0d28ad 100644
--- a/allmydata/node.py
+++ b/allmydata/node.py
@@ -12,6 +12,7 @@ class Node(service.MultiService):
     NODETYPE = "unknown NODETYPE"
     PORTNUMFILE = None
     CERTFILE = None
+    LOCAL_IP_FILE = "local_ip"
 
     def __init__(self, basedir="."):
         service.MultiService.__init__(self)
@@ -50,8 +51,15 @@ class Node(service.MultiService):
                 log.msg("AuthorizedKeysManhole listening on %d" % portnum)
 
     def _setup_tub(self, local_ip):
+        # we can't get a dynamically-assigned portnum until our Tub is
+        # running, which means after startService.
         l = self.tub.getListeners()[0]
         portnum = l.getPortnum()
+        local_ip_filename = os.path.join(self.basedir, self.LOCAL_IP_FILE)
+        if os.path.exists(local_ip_filename):
+            f = open(local_ip_filename, "r")
+            local_ip = f.read()
+            f.close()
         self.tub.setLocation("%s:%d" % (local_ip, portnum))
         if not os.path.exists(self._portnumfile):
             # record which port we're listening on, so we can grab the same
@@ -62,8 +70,8 @@ class Node(service.MultiService):
         self.tub.setLocation("%s:%d" % (local_ip, l.getPortnum()))
         return self.tub
 
-    def tub_ready(self, tub):
-        # this is called when the Tub has a location
+    def tub_ready(self):
+        # called when the Tub is available for registerReference
         pass
 
     def add_service(self, s):
@@ -73,8 +81,7 @@ class Node(service.MultiService):
     def startService(self):
         # note: this class can only be started and stopped once.
         service.MultiService.startService(self)
-        d = get_local_ip_for()
-        d.addCallback(self._setup_tub)
-        d.addCallback(self.tub_ready)
-        d.addCallback(lambda res: log.msg("%s running" % self.NODETYPE))
-
+        local_ip = get_local_ip_for()
+        self._setup_tub(local_ip)
+        self.tub_ready()
+        log.msg("%s running" % self.NODETYPE)
diff --git a/allmydata/queen.py b/allmydata/queen.py
index 5db05788..dfb299d0 100644
--- a/allmydata/queen.py
+++ b/allmydata/queen.py
@@ -1,4 +1,5 @@
 
+import os.path
 from foolscap import Referenceable
 from foolscap.eventual import eventually
 from twisted.application import service
@@ -53,8 +54,12 @@ class Queen(node.Node):
         node.Node.__init__(self, basedir)
         self.urls = {}
 
-    def tub_ready(self, tub):
+    def tub_ready(self):
         r = self.add_service(Roster())
         self.urls["roster"] = self.tub.registerReference(r, "roster")
         log.msg(" roster is at %s" % self.urls["roster"])
+        f = open(os.path.join(self.basedir, "roster_pburl"), "w")
+        f.write(self.urls["roster"] + "\n")
+        f.close()
+
 
diff --git a/allmydata/test/test_system.py b/allmydata/test/test_system.py
index 41a236a2..060ed5a7 100644
--- a/allmydata/test/test_system.py
+++ b/allmydata/test/test_system.py
@@ -1,21 +1,25 @@
 
 from twisted.trial import unittest
+from twisted.internet import defer, reactor
 from twisted.application import service
 from allmydata import upload, client, queen
 import os
+from foolscap.eventual import flushEventualQueue
 
 class SystemTest(unittest.TestCase):
     def setUp(self):
         self.sparent = service.MultiService()
         self.sparent.startService()
     def tearDown(self):
-        return self.sparent.stopService()
+        d = self.sparent.stopService()
+        d.addCallback(lambda res: flushEventualQueue())
+        return d
 
     def addService(self, s):
         s.setServiceParent(self.sparent)
         return s
 
-    def test_it(self):
+    def setUpNodes(self):
         os.mkdir("queen")
         q = self.addService(queen.Queen(basedir="queen"))
         clients = []
@@ -23,5 +27,17 @@ class SystemTest(unittest.TestCase):
         for i in range(NUMCLIENTS):
             basedir = "client%d" % i
             os.mkdir(basedir)
-            clients.append(self.addService(client.Client(basedir=basedir)))
+            c = self.addService(client.Client(basedir=basedir))
+            clients.append(c)
+
 
+    def waitForConnections(self):
+        # the cheap way: time
+        d = defer.Deferred()
+        reactor.callLater(1, d.callback, None)
+        return d
+
+    def test_it(self):
+        self.setUpNodes()
+        d = self.waitForConnections()
+        return d
diff --git a/allmydata/util/iputil.py b/allmydata/util/iputil.py
index 602d5f1a..9b87b1c0 100644
--- a/allmydata/util/iputil.py
+++ b/allmydata/util/iputil.py
@@ -1,48 +1,21 @@
 
 # adapted from nattraverso.ipdiscover
 
+import socket
 from twisted.internet import reactor
 from twisted.internet.protocol import DatagramProtocol
-#from twisted.internet.error import CannotListenError
-#from twisted.internet.interfaces import IReactorMulticast
-#from amdlib.util.nattraverso.utils import is_rfc1918_ip, is_bogus_ip
 
 def get_local_ip_for(target='A.ROOT-SERVERS.NET'):
     """Find out what our IP address is for use by a given target.
 
-    Returns a Deferred which will be fired with a string that holds the IP
-    address which could be used by 'target' to connect to us. It might work
-    for them, it might not.
-
-    The reactor must be running before you can call this, because we must
-    perform a DNS lookup on the target.
-
-    """
-    d = reactor.resolve(target)
-    def _resolved(target_ipaddr):
-        udpprot = DatagramProtocol()
-        port = reactor.listenUDP(0, udpprot)
-        udpprot.transport.connect(target_ipaddr, 7)
-        localip = udpprot.transport.getHost().host
-        port.stopListening()
-        return localip
-    d.addCallback(_resolved)
-    return d
-
-
-
-def BROKEN_get_local_ip_for(target_ipaddr):
-    """Find out what our IP address is for use by a given target.
-
-    Returns a Deferred which will be fired with a string that holds the IP
-    address which could be used by 'target' to connect to us. It might work
-    for them, it might not. 'target' must be an IP address.
-
+    Returns a string that holds the IP address which could be used by
+    'target' to connect to us. It might work for them, it might not.
     """
+    target_ipaddr = socket.gethostbyname(target)
     udpprot = DatagramProtocol()
     port = reactor.listenUDP(0, udpprot)
     udpprot.transport.connect(target_ipaddr, 7)
     localip = udpprot.transport.getHost().host
-    port.stopListening()
-
+    port.stopListening() # note, this returns a Deferred
     return localip
+
-- 
2.45.2