Add create node args: listen, port, hostname, location

fixes ticket: 2773
This commit is contained in:
David Stainton 2016-09-06 15:03:31 +00:00 committed by Brian Warner
parent 2a44a8e8cc
commit 83db7e8b43
3 changed files with 234 additions and 17 deletions

View File

@ -1,9 +1,10 @@
import os
from twisted.python import usage
from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
from allmydata.scripts.default_nodedir import _default_nodedir
from allmydata.util.assertutil import precondition
from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_local_unicode_path
from allmydata.util import fileutil
from allmydata.util import fileutil, iputil
dummy_tac = """
@ -17,10 +18,38 @@ def write_tac(basedir, nodetype):
fileutil.write(os.path.join(basedir, "tahoe-%s.tac" % (nodetype,)), dummy_tac)
WHERE_OPTS = [
("location", None, None, "Specify the location to advertise for this node."),
("port", None, None, "Specify the server endpoint to listen on for this node."),
]
def validate_where_options(options):
if options['hostname'] and options['port']:
raise usage.UsageError("The --hostname option cannot be used with the --port option.")
if options['hostname'] and options['location']:
raise usage.UsageError("The --hostname option cannot be used with the --location option.")
if not options['hostname'] and (options['location'] and not options['port']):
raise usage.UsageError("The --location option must be used with the --port option.")
if not options['hostname'] and (options['port'] and not options['location']):
raise usage.UsageError("The --port option must be used with the --location option.")
if (options['listen'] != "tcp") and options['hostname']:
raise usage.UsageError("The listener type must be TCP to use --hostname option.")
class _CreateBaseOptions(BasedirOptions):
optFlags = [
("hide-ip", None, "prohibit any configuration that would reveal the node's IP address"),
]
# This is overridden in order to ensure we get a "Wrong number of
# arguments." error when more than one argument is given.
def parseArgs(self, basedir=None):
BasedirOptions.parseArgs(self, basedir)
class CreateClientOptions(_CreateBaseOptions):
synopsis = "[options] [NODEDIR]"
description = "Create a client-only Tahoe-LAFS node (no storage server)."
optParameters = [
# we provide 'create-node'-time options for the most common
# configuration knobs. The rest can be controlled by editing
@ -31,32 +60,48 @@ class _CreateBaseOptions(BasedirOptions):
"Specify which TCP port to run the HTTP interface on. Use 'none' to disable."),
("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
% quote_local_unicode_path(_default_nodedir)),
]
# This is overridden in order to ensure we get a "Wrong number of
# arguments." error when more than one argument is given.
def parseArgs(self, basedir=None):
BasedirOptions.parseArgs(self, basedir)
class CreateClientOptions(_CreateBaseOptions):
synopsis = "[options] [NODEDIR]"
description = "Create a client-only Tahoe-LAFS node (no storage server)."
class CreateNodeOptions(CreateClientOptions):
optFlags = [
("no-storage", None, "Do not offer storage service to other nodes."),
]
synopsis = "[options] [NODEDIR]"
description = "Create a full Tahoe-LAFS node (client+server)."
optParameters = WHERE_OPTS + [
# we provide 'create-node'-time options for the most common
# configuration knobs. The rest can be controlled by editing
# tahoe.cfg before node startup.
("hostname", None, None, "Specify the hostname for listening and advertising for this node."),
("listen", None, "tcp", "Specify the listener type for this node."),
("nickname", "n", None, "Specify the nickname for this node."),
("introducer", "i", None, "Specify the introducer FURL to use."),
("webport", "p", "tcp:3456:interface=127.0.0.1",
"Specify which TCP port to run the HTTP interface on. Use 'none' to disable."),
("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
% quote_local_unicode_path(_default_nodedir)),
]
def parseArgs(self, basedir=None):
CreateClientOptions.parseArgs(self, basedir)
validate_where_options(self)
class CreateIntroducerOptions(NoDefaultBasedirOptions):
subcommand_name = "create-introducer"
description = "Create a Tahoe-LAFS introducer."
optFlags = [
("hide-ip", None, "prohibit any configuration that would reveal the node's IP address"),
]
]
optParameters = WHERE_OPTS + [("listen", None, "tcp", "Specify the listener type for this node."),
("hostname", None, None, "Specify the hostname for listening and advertising for this node."),
]
def parseArgs(self, basedir=None):
NoDefaultBasedirOptions.parseArgs(self, basedir)
validate_where_options(self)
def write_node_config(c, config):
# this is shared between clients and introducers
@ -83,11 +128,30 @@ def write_node_config(c, config):
webport = ""
c.write("web.port = %s\n" % (webport.encode('utf-8'),))
c.write("web.static = public_html\n")
c.write("# to prevent the Tub from listening at all, use this:\n")
c.write("# tub.port = disabled\n")
c.write("# tub.location = disabled\n")
c.write("#tub.port =\n")
c.write("#tub.location = \n")
if 'hostname' in config and config['hostname'] is not None:
print "HOSTNAME"
new_port = iputil.allocate_tcp_port()
c.write("tub.port = tcp:%s\n" % new_port)
c.write("tub.location = tcp:%s:%s\n" % (config.get('hostname').encode('utf-8'), new_port))
elif 'listen' in config and config['listen'] == "tor":
raise NotImplementedError("This feature addition is being tracked by this ticket:" +
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
elif 'listen' in config and config['listen'] == "i2p":
raise NotImplementedError("This feature addition is being tracked by this ticket:" +
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
elif config.get('port') is not None:
c.write("tub.port = %s\n" % config.get('port').encode('utf-8'))
c.write("tub.location = %s\n" % config.get('location').encode('utf-8'))
else:
c.write("tub.port = disabled\n")
c.write("tub.location = disabled\n")
if ('hostname' in config and config['hostname']) or ('listen' in config and config['listen']):
c.write("# to prevent the Tub from listening at all, use this:\n")
c.write("# tub.port = disabled\n")
c.write("# tub.location = disabled\n")
c.write("#log_gatherer.furl =\n")
c.write("#timeout.keepalive =\n")
c.write("#timeout.disconnect =\n")

View File

@ -1,6 +1,7 @@
import os
from twisted.trial import unittest
from twisted.internet import defer
from twisted.python import usage
from allmydata.util import configutil
from ..common_util import run_cli
@ -16,6 +17,81 @@ class Config(unittest.TestCase):
rc, out, err = yield run_cli("create-client", basedir)
cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True)
self.assertEqual(cfg.get("node", "tub.port"), "disabled")
self.assertEqual(cfg.get("node", "tub.location"), "disabled")
@defer.inlineCallbacks
def test_client_hostname(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--hostname=computer", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --hostname not recognized")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_client_port_location(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client",
"--port=unix:/var/tahoe/socket",
"--location=tor:myservice.onion:12345",
basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --port not recognized")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_client_port_only(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--port=unix:/var/tahoe/socket", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --port not recognized")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_client_location_only(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--location=tor:myservice.onion:12345", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --location not recognized")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_client_listen_tcp(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--listen=tcp", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --listen not recognized")
else:
self.fail("UsageError expected to be raised)")
@defer.inlineCallbacks
def test_client_listen_tor(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--listen=tor", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --listen not recognized")
else:
self.fail("UsageError expected to be raised)")
@defer.inlineCallbacks
def test_client_listen_i2p(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-client", "--listen=i2p", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "option --listen not recognized")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_client_hide_ip(self):
@ -30,6 +106,7 @@ class Config(unittest.TestCase):
rc, out, err = yield run_cli("create-node", basedir)
cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True)
self.assertEqual(cfg.get("node", "listen"), "tcp")
@defer.inlineCallbacks
def test_node_hide_ip(self):
@ -38,6 +115,73 @@ class Config(unittest.TestCase):
cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False)
@defer.inlineCallbacks
def test_node_hostname(self):
basedir = self.mktemp()
rc, out, err = yield run_cli("create-node", "--hostname=computer", basedir)
cfg = self.read_config(basedir)
self.assertTrue("computer" in cfg.get("node", "tub.location"))
@defer.inlineCallbacks
def test_node_port_location(self):
basedir = self.mktemp()
rc, out, err = yield run_cli("create-node",
"--port=unix:/var/tahoe/socket",
"--location=tor:myservice.onion:12345",
basedir)
cfg = self.read_config(basedir)
self.assertEqual(cfg.get("node", "tub.location"), "tor:myservice.onion:12345")
self.assertEqual(cfg.get("node", "tub.port"), "unix:/var/tahoe/socket")
@defer.inlineCallbacks
def test_node_listen_tcp(self):
basedir = self.mktemp()
rc, out, err = yield run_cli("create-node", "--listen=tcp", basedir)
cfg = self.read_config(basedir)
self.assertEqual(cfg.get("node", "listen"), "tcp")
@defer.inlineCallbacks
def test_node_listen_tor(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-node", "--listen=tor", basedir)
except NotImplementedError, e:
self.failUnlessEqual(str(e), "This feature addition is being tracked by this ticket:" +
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
else:
self.fail("NotImplementedError expected to be raised")
@defer.inlineCallbacks
def test_node_listen_i2p(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-node", "--listen=i2p", basedir)
except NotImplementedError, e:
self.failUnlessEqual(str(e), "This feature addition is being tracked by this ticket:" +
"https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2490")
else:
self.fail("NotImplementedError expected to be raised")
@defer.inlineCallbacks
def test_node_port_only(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-node", "--port=unix:/var/tahoe/socket", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "The --port option must be used with the --location option.")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_node_location_only(self):
basedir = self.mktemp()
try:
rc, out, err = yield run_cli("create-node", "--location=tor:myservice.onion:12345", basedir)
except usage.UsageError, e:
self.failUnlessEqual(str(e), "The --location option must be used with the --port option.")
else:
self.fail("UsageError expected to be raised")
@defer.inlineCallbacks
def test_introducer(self):
basedir = self.mktemp()
@ -51,3 +195,10 @@ class Config(unittest.TestCase):
rc, out, err = yield run_cli("create-introducer", "--hide-ip", basedir)
cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False)
@defer.inlineCallbacks
def test_introducer_hostname(self):
basedir = self.mktemp()
rc, out, err = yield run_cli("create-introducer", "--hostname=computer", basedir)
cfg = self.read_config(basedir)
self.assertTrue("computer" in cfg.get("node", "tub.location"))

View File

@ -344,7 +344,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
node_url_file = os.path.join(c1, "node.url")
config_file = os.path.join(c1, "tahoe.cfg")
d = self.run_bintahoe(["--quiet", "create-introducer", "--basedir", c1])
d = self.run_bintahoe(["--quiet", "create-introducer", "--basedir", c1, "--hostname", "localhost"])
def _cb(res):
out, err, rc_or_sig = res
self.failUnlessEqual(rc_or_sig, 0)
@ -373,6 +373,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err)
self.failUnlessEqual(rc_or_sig, 0, errstr)
self.failUnlessEqual(out, "", errstr)
print errstr
# self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise.
# the parent (twistd) has exited. However, twistd writes the pid
@ -393,6 +394,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
# read the introducer.furl and introducer.port files so we can
# check that their contents don't change on restart
self.furl = fileutil.read(introducer_furl_file)
print "portnum_file " + portnum_file
self.failUnless(os.path.exists(portnum_file))
self.portnum = fileutil.read(portnum_file)
@ -531,7 +533,7 @@ class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
node_url_file = os.path.join(c1, "node.url")
config_file = os.path.join(c1, "tahoe.cfg")
d = self.run_bintahoe(["--quiet", "create-node", "--basedir", c1, "--webport", "0"])
d = self.run_bintahoe(["--quiet", "create-node", "--basedir", c1, "--webport", "0", "--hostname", "localhost"])
def _cb(res):
out, err, rc_or_sig = res
self.failUnlessEqual(rc_or_sig, 0)