import json from os.path import join from twisted.python import usage from twisted.internet import defer, reactor from wormhole import wormhole from allmydata.util import configutil from allmydata.util.encodingutil import argv_to_abspath from allmydata.scripts.common import get_default_nodedir, get_introducer_furl class InviteOptions(usage.Options): synopsis = "[options] " description = "Create a client-only Tahoe-LAFS node (no storage server)." optParameters = [ ("shares-needed", None, None, "How many shares are needed to reconstruct files from this node"), ("shares-happy", None, None, "Distinct storage servers new node will upload shares to"), ("shares-total", None, None, "Total number of shares new node will upload"), ] def parseArgs(self, *args): if len(args) != 1: raise usage.UsageError( "Provide a single argument: the new node's nickname" ) self['nick'] = args[0].strip() @defer.inlineCallbacks def _send_config_via_wormhole(options, config): out = options.stdout err = options.stderr relay_url = options.parent['wormhole-server'] print >>out, "Connecting to '{}'...".format(relay_url) wh = wormhole.create( appid=options.parent['wormhole-invite-appid'], relay_url=relay_url, reactor=reactor, ) yield wh.get_welcome() print >>out, "Connected to wormhole server" # must call allocate_code before get_code will ever succeed wh.allocate_code() code = yield wh.get_code() print >>out, "Invite Code for client: {}".format(code) wh.send_message(json.dumps({ u"abilities": { u"server-v1": {}, } })) client_intro = yield wh.get_message() print >>out, " received client introduction" client_intro = json.loads(client_intro) if not u'abilities' in client_intro: print >>err, "No 'abilities' from client" defer.returnValue(1) if not u'client-v1' in client_intro[u'abilities']: print >>err, "No 'client-v1' in abilities from client" defer.returnValue(1) print >>out, " transmitting configuration" wh.send_message(json.dumps(config)) yield wh.close() @defer.inlineCallbacks def invite(options): if options.parent['node-directory']: basedir = argv_to_abspath(options.parent['node-directory']) else: basedir = get_default_nodedir() config = configutil.get_config(join(basedir, 'tahoe.cfg')) out = options.stdout err = options.stderr try: introducer_furl = get_introducer_furl(basedir, config) except Exception as e: print >>err, "Can't find introducer FURL for node '{}': {}".format(basedir, str(e)) raise SystemExit(1) nick = options['nick'] remote_config = { "shares-needed": options["shares-needed"] or config.get('client', 'shares.needed'), "shares-total": options["shares-total"] or config.get('client', 'shares.total'), "shares-happy": options["shares-happy"] or config.get('client', 'shares.happy'), "nickname": nick, "introducer": introducer_furl, } yield _send_config_via_wormhole(options, remote_config) print >>out, "Completed successfully" subCommands = [ ("invite", None, InviteOptions, "Invite a new node to this grid"), ] dispatch = { "invite": invite, }