the owner of the client node can use this feature. This port is intended for
debugging and testing use.
+logport.furl : this file contains a FURL that provides access to a 'log port'
+on the client node, from which operational logs can be retrieved. Do not
+grant logport access to strangers, because occasionally secret information
+may be placed in the logs.
+
+log_gatherer.furl : if present, this file is used to contact a 'log
+gatherer', which will be granted access to the logport. This can be used by
+centralized storage meshes to gather operational logs in a single place.
+
+
== Introducer/vdrive-server configuration ==
Introducer/vdrive-server nodes use the same 'advertised_ip_addresses' file
--- /dev/null
+
+import os.path
+from zope.interface import implements
+from twisted.application import service
+from foolscap import Referenceable, RemoteInterface
+from foolscap.schema import DictOf
+
+class RILogPublisher(RemoteInterface):
+ def get_versions():
+ return DictOf(str, str)
+
+class RILogGatherer(RemoteInterface):
+ def logport(nodeid=str, logport=RILogPublisher):
+ return None
+
+class LogPublisher(Referenceable, service.MultiService):
+ implements(RILogPublisher)
+ name = "log_publisher"
+
+ def __init__(self):
+ service.MultiService.__init__(self)
+
+ def startService(self):
+ service.MultiService.startService(self)
+ furlfile = os.path.join(self.parent.basedir, "logport.furl")
+ self.parent.tub.registerReference(self, furlFile=furlfile)
+ os.chmod(furlfile, 0600)
+
+ def remote_get_versions(self):
+ versions = self.parent.get_versions()
+ # our __version__ attributes are actually instances of
+ # allmydata.util.version_class.Version, so convert them into strings
+ # first.
+ return dict([(k,str(v))
+ for k,v in versions.items()])
+
from foolscap import Tub, eventual
from allmydata.util import iputil, observer, humanreadable
from allmydata.util.assertutil import precondition
+from allmydata.logpublisher import LogPublisher
# Just to get their versions:
import allmydata
def tub_ready(self):
# called when the Tub is available for registerReference
- pass
+ self.add_service(LogPublisher())
+ log_gatherer_furl = self.get_config("log_gatherer.furl")
+ if log_gatherer_furl:
+ self.tub.connectTo(log_gatherer_furl, self._log_gatherer_connected)
+
+ def _log_gatherer_connected(self, rref):
+ rref.callRemote("logport",
+ self.nodeid, self.getServiceNamed("log_publisher"))
def when_tub_ready(self):
return self._tub_ready_observerlist.when_fired()
-import time
+import os, time
+from zope.interface import implements
from twisted.trial import unittest
from twisted.internet import defer
from twisted.python import log
+from foolscap import Tub, Referenceable
from foolscap.eventual import flushEventualQueue
from twisted.application import service
+import allmydata
from allmydata.node import Node, formatTimeTahoeStyle
-from allmydata.util import testutil
+from allmydata.util import testutil, fileutil
+from allmydata import logpublisher
class LoggingMultiService(service.MultiService):
def log(self, msg):
return d
def test_advertised_ip_addresses(self):
- open('advertised_ip_addresses','w').write('1.2.3.4:5')
+ basedir = "test_node/test_advertised_ip_addresses"
+ fileutil.make_dirs(basedir)
+ f = open(os.path.join(basedir, 'advertised_ip_addresses'),'w')
+ f.write('1.2.3.4:5')
+ f.close()
- n = TestNode()
+ n = TestNode(basedir)
n.setServiceParent(self.parent)
d = n.when_tub_ready()
def _check_addresses(ignored_result):
- self.failUnless("1.2.3.4:5" in n.tub.registerReference(n), n.tub.registerReference(n))
+ furl = n.tub.registerReference(n)
+ self.failUnless("1.2.3.4:5" in furl, furl)
d.addCallback(_check_addresses)
return d
def test_log(self):
- n = TestNode()
+ basedir = "test_node/test_log"
+ fileutil.make_dirs(basedir)
+ n = TestNode(basedir)
n.log("this is a message")
n.log("with %d %s %s", args=(2, "interpolated", "parameters"))
n.log("with bogus %d expansion", args=("not an integer",))
+ def test_logpublisher(self):
+ basedir = "test_node/test_logpublisher"
+ fileutil.make_dirs(basedir)
+ n = TestNode(basedir)
+ n.setServiceParent(self.parent)
+ d = n.when_tub_ready()
+ def _ready(res):
+ n.log("starting up")
+ flogport = open(os.path.join(n.basedir,"logport.furl"), "r").read()
+ return n.tub.getReference(flogport.strip())
+ d.addCallback(_ready)
+ def _got_logport(logport):
+ d = logport.callRemote("get_versions")
+ def _check(versions):
+ self.failUnlessEqual(versions["allmydata"],
+ allmydata.__version__)
+ d.addCallback(_check)
+ return d
+ d.addCallback(_got_logport)
+ return d
+
+ def test_log_gatherer(self):
+ t = Tub()
+ t.setServiceParent(self.parent)
+ t.listenOn("tcp:0:interface=127.0.0.1")
+ l = t.getListeners()[0]
+ portnum = l.getPortnum()
+ t.setLocation("127.0.0.1:%d" % portnum)
+ gatherer = Gatherer()
+ gatherer.d = defer.Deferred()
+ gatherer_furl = t.registerReference(gatherer)
+
+ basedir = "test_node/test_log_gatherer"
+ fileutil.make_dirs(basedir)
+ f = open(os.path.join(basedir, "log_gatherer.furl"), "w")
+ f.write(gatherer_furl + "\n")
+ f.close()
+
+ n = TestNode(basedir)
+ n.setServiceParent(self.parent)
+ d = n.when_tub_ready()
+ def _ready(res):
+ n.log("starting up")
+ # about now, the node will be contacting the Gatherer and
+ # offering its logport.
+ return gatherer.d
+ d.addCallback(_ready)
+ return d
+
def test_timestamp(self):
# this modified logger doesn't seem to get used during the tests,
# probably because we don't modify the LogObserver that trial
t2 = formatTimeTahoeStyle("ignored", int(time.time()))
self.failUnless("Z" in t2)
+class Gatherer(Referenceable):
+ implements(logpublisher.RILogGatherer)
+ def remote_logport(self, nodeid, logport):
+ d = logport.callRemote("get_versions")
+ d.addCallback(self.d.callback)