Changeset 4f717ba in trunk


Ignore:
Timestamp:
2016-09-13T00:15:52Z (9 years ago)
Author:
David Stainton <dstainton415@…>
Branches:
master
Children:
4ae574c
Parents:
a8899c8
git-author:
David Stainton <dstainton415@…> (2016-09-12 23:01:23)
git-committer:
David Stainton <dstainton415@…> (2016-09-13 00:15:52)
Message:

Add a multi-introducer implementation

this is based the previous work of warner, killyourtv and leif
fixes ticket #68

Location:
src/allmydata
Files:
1 added
7 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified src/allmydata/client.py

    ra8899c8 r4f717ba  
    186186        self.logSource="Client"
    187187        self.encoding_params = self.DEFAULT_ENCODING_PARAMETERS.copy()
    188         self.init_introducer_client()
     188        self.init_introducer_clients()
    189189        self.init_stats_provider()
    190190        self.init_secrets()
     
    235235        return seqnum, nonce
    236236
    237     def init_introducer_client(self):
    238         self.introducer_furl = self.get_config("client", "introducer.furl")
    239         introducer_cache_filepath = FilePath(os.path.join(self.basedir, "private", "introducer_cache.yaml"))
    240         ic = IntroducerClient(self.tub, self.introducer_furl,
    241                               self.nickname,
    242                               str(allmydata.__full_version__),
    243                               str(self.OLDEST_SUPPORTED_VERSION),
    244                               self.get_app_versions(),
    245                               self._sequencer, introducer_cache_filepath)
    246         self.introducer_client = ic
    247         ic.setServiceParent(self)
     237    def init_introducer_clients(self):
     238        self.introducer_clients = []
     239        self.introducer_furls = []
     240
     241        introducers_yaml_filename = os.path.join(self.basedir, "private", "introducers.yaml")
     242        introducers_filepath = FilePath(introducers_yaml_filename)
     243
     244        try:
     245            with introducers_filepath.open() as f:
     246                introducers_yaml = yamlutil.safe_load(f)
     247                introducers = introducers_yaml.get("introducers", {})
     248                log.msg("found %d introducers in private/introducers.yaml" %
     249                        len(introducers))
     250        except EnvironmentError:
     251            introducers = {}
     252
     253        if "default" in introducers.keys():
     254            raise ValueError("'default' introducer furl cannot be specified in introducers.yaml; please fix impossible configuration.")
     255
     256        # read furl from tahoe.cfg
     257        tahoe_cfg_introducer_furl = self.get_config("client", "introducer.furl", None)
     258        if tahoe_cfg_introducer_furl:
     259            introducers[u'default'] = {'furl':tahoe_cfg_introducer_furl}
     260
     261        for petname, introducer in introducers.items():
     262            introducer_cache_filepath = FilePath(os.path.join(self.basedir, "private", "introducer_{}_cache.yaml".format(petname)))
     263            ic = IntroducerClient(self.tub, introducer['furl'],
     264                                  self.nickname,
     265                                  str(allmydata.__full_version__),
     266                                  str(self.OLDEST_SUPPORTED_VERSION),
     267                                  self.get_app_versions(), self._sequencer, introducer_cache_filepath)
     268            self.introducer_clients.append(ic)
     269            self.introducer_furls.append(introducer['furl'])
     270            ic.setServiceParent(self)
    248271
    249272    def init_stats_provider(self):
     
    366389               "permutation-seed-base32": self._init_permutation_seed(ss),
    367390               }
    368         self.introducer_client.publish("storage", ann, self._node_key)
     391        for ic in self.introducer_clients:
     392            ic.publish("storage", ann, self._node_key)
    369393
    370394    def init_client(self):
     
    423447        self.storage_broker = sb
    424448        sb.setServiceParent(self)
    425 
    426         sb.use_introducer(self.introducer_client)
     449        for ic in self.introducer_clients:
     450            sb.use_introducer(ic)
    427451
    428452    def get_storage_broker(self):
     
    575599        return self.encoding_params
    576600
     601    def introducer_connection_statuses(self):
     602        return [ic.connected_to_introducer() for ic in self.introducer_clients]
     603
    577604    def connected_to_introducer(self):
    578         if self.introducer_client:
    579             return self.introducer_client.connected_to_introducer()
    580         return False
     605        return any([ic.connected_to_introducer() for ic in self.introducer_clients])
    581606
    582607    def get_renewal_secret(self): # this will go away
  • TabularUnified src/allmydata/introducer/client.py

    ra8899c8 r4f717ba  
    4848
    4949        self._publisher = None
     50        self._since = None
    5051
    5152        self._local_subscribers = [] # (servicename,cb,args,kwargs) tuples
     
    141142            raise InsufficientVersionError("V2", publisher.version)
    142143        self._publisher = publisher
     144        self._since = int(time.time())
    143145        publisher.notifyOnDisconnect(self._disconnected)
    144146        self._maybe_publish()
     
    148150        self.log("bummer, we've lost our connection to the introducer")
    149151        self._publisher = None
     152        self._since = int(time.time())
    150153        self._subscriptions.clear()
    151154
     
    326329    def connected_to_introducer(self):
    327330        return bool(self._publisher)
     331
     332    def get_since(self):
     333        return self._since
     334
     335    def get_last_received_data_time(self):
     336        if self._publisher is None:
     337            return None
     338        else:
     339            return self._publisher.getDataLastReceivedAt()
  • TabularUnified src/allmydata/test/test_helper.py

    ra8899c8 r4f717ba  
    8282
    8383class FakeClient(service.MultiService):
     84    introducer_clients = []
    8485    DEFAULT_ENCODING_PARAMETERS = {"k":25,
    8586                                   "happy": 75,
  • TabularUnified src/allmydata/test/test_introducer.py

    ra8899c8 r4f717ba  
    728728        fileutil.make_dirs(basedir)
    729729        cache_filepath = FilePath(os.path.join(basedir, "private",
    730                                                "introducer_cache.yaml"))
     730                                               "introducer_default_cache.yaml"))
    731731
    732732        # if storage is enabled, the Client will publish its storage server
     
    742742
    743743        c = TahoeClient(basedir)
    744         ic = c.introducer_client
     744        ic = c.introducer_clients[0]
    745745        sk_s, vk_s = keyutil.make_keypair()
    746746        sk, _ignored = keyutil.parse_privkey(sk_s)
     
    810810
    811811        c2 = TahoeClient(basedir)
    812         c2.introducer_client._load_announcements()
     812        c2.introducer_clients[0]._load_announcements()
    813813        yield flushEventualQueue()
    814814        self.assertEqual(c2.storage_broker.get_all_serverids(),
     
    831831
    832832        c = TahoeClient(basedir)
    833         ic = c.introducer_client
     833        ic = c.introducer_clients[0]
    834834        outbound = ic._outbound_announcements
    835835        published = ic._published_announcements
  • TabularUnified src/allmydata/test/web/test_web.py

    ra8899c8 r4f717ba  
    229229        self.nodeid = "fake_nodeid"
    230230        self.nickname = u"fake_nickname \u263A"
    231         self.introducer_furl = "None"
     231        self.introducer_furls = []
     232        self.introducer_clients = []
    232233        self.stats_provider = FakeStatsProvider()
    233234        self._secret_holder = SecretHolder("lease secret", "convergence secret")
     
    658659            def connected_to_introducer(self):
    659660                return self.connected
     661            def get_since(self):
     662                return 0
     663            def get_last_received_data_time(self):
     664                return 0
    660665
    661666        d = defer.succeed(None)
     
    663668        # introducer not connected, unguessable furl
    664669        def _set_introducer_not_connected_unguessable(ign):
    665             self.s.introducer_furl = "pb://someIntroducer/secret"
    666             self.s.introducer_client = MockIntroducerClient(False)
     670            self.s.introducer_furls = [ "pb://someIntroducer/secret" ]
     671            self.s.introducer_clients = [ MockIntroducerClient(False) ]
    667672            return self.GET("/")
    668673        d.addCallback(_set_introducer_not_connected_unguessable)
     
    671676            self.failUnlessIn('<div class="furl">pb://someIntroducer/[censored]</div>', html)
    672677            self.failIfIn('pb://someIntroducer/secret', html)
    673             self.failUnless(re.search('<img (alt="Disconnected" |src="img/connected-no.png" ){2}/>', html), res)
     678            self.failUnless(re.search('<img (alt="Disconnected" |src="img/connected-no.png" ){2}/></div>[ ]*<div>No introducers connected</div>', html), res)
     679
    674680        d.addCallback(_check_introducer_not_connected_unguessable)
    675681
    676682        # introducer connected, unguessable furl
    677683        def _set_introducer_connected_unguessable(ign):
    678             self.s.introducer_furl = "pb://someIntroducer/secret"
    679             self.s.introducer_client = MockIntroducerClient(True)
     684            self.s.introducer_furls = [ "pb://someIntroducer/secret" ]
     685            self.s.introducer_clients = [ MockIntroducerClient(True) ]
    680686            return self.GET("/")
    681687        d.addCallback(_set_introducer_connected_unguessable)
     
    684690            self.failUnlessIn('<div class="furl">pb://someIntroducer/[censored]</div>', html)
    685691            self.failIfIn('pb://someIntroducer/secret', html)
    686             self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/>', html), res)
     692            self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/></div>[ ]*<div>1 introducer connected</div>', html), res)
    687693        d.addCallback(_check_introducer_connected_unguessable)
    688694
    689695        # introducer connected, guessable furl
    690696        def _set_introducer_connected_guessable(ign):
    691             self.s.introducer_furl = "pb://someIntroducer/introducer"
    692             self.s.introducer_client = MockIntroducerClient(True)
     697            self.s.introducer_furls = [ "pb://someIntroducer/introducer" ]
     698            self.s.introducer_clients = [ MockIntroducerClient(True) ]
    693699            return self.GET("/")
    694700        d.addCallback(_set_introducer_connected_guessable)
     
    696702            html = res.replace('\n', ' ')
    697703            self.failUnlessIn('<div class="furl">pb://someIntroducer/introducer</div>', html)
    698             self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/>', html), res)
     704            self.failUnless(re.search('<img (src="img/connected-yes.png" |alt="Connected" ){2}/></div>[ ]*<div>1 introducer connected</div>', html), res)
    699705        d.addCallback(_check_introducer_connected_guessable)
    700706        return d
  • TabularUnified src/allmydata/web/root.py

    ra8899c8 r4f717ba  
    231231        return ctx.tag[ul]
    232232
    233     def data_introducer_furl_prefix(self, ctx, data):
    234         ifurl = self.client.introducer_furl
    235         # trim off the secret swissnum
    236         (prefix, _, swissnum) = ifurl.rpartition("/")
    237         if not ifurl:
    238             return None
    239         if swissnum == "introducer":
    240             return ifurl
     233    def data_introducer_description(self, ctx, data):
     234        connected_count = self.data_connected_introducers( ctx, data )
     235        if connected_count == 0:
     236            return "No introducers connected"
     237        elif connected_count == 1:
     238            return "1 introducer connected"
    241239        else:
    242             return "%s/[censored]" % (prefix,)
    243 
    244     def data_introducer_description(self, ctx, data):
    245         if self.data_connected_to_introducer(ctx, data) == "no":
    246             return "Introducer not connected"
    247         return "Introducer"
     240            return "%s introducers connected" % (connected_count,)
     241
     242    def data_total_introducers(self, ctx, data):
     243        return len(self.client.introducer_furls)
     244
     245    def data_connected_introducers(self, ctx, data):
     246        return self.client.introducer_connection_statuses().count(True)
    248247
    249248    def data_connected_to_introducer(self, ctx, data):
     
    252251        return "no"
    253252
    254     def data_connected_to_introducer_alt(self, ctx, data):
    255         return self._connectedalts[self.data_connected_to_introducer(ctx, data)]
     253    def data_connected_to_at_least_one_introducer(self, ctx, data):
     254        if True in self.client.introducer_connection_statuses():
     255            return "yes"
     256        return "no"
     257
     258    def data_connected_to_at_least_one_introducer_alt(self, ctx, data):
     259        return self._connectedalts[self.data_connected_to_at_least_one_introducer(ctx, data)]
     260
     261    # In case we configure multiple introducers
     262    def data_introducers(self, ctx, data):
     263        connection_statuses = self.client.introducer_connection_statuses()
     264        s = []
     265        furls = self.client.introducer_furls
     266        for furl in furls:
     267            if connection_statuses:
     268                display_furl = furl
     269                # trim off the secret swissnum
     270                (prefix, _, swissnum) = furl.rpartition("/")
     271                if swissnum != "introducer":
     272                    display_furl = "%s/[censored]" % (prefix,)
     273                i = furls.index(furl)
     274                ic = self.client.introducer_clients[i]
     275                s.append((display_furl, bool(connection_statuses[i]), ic))
     276        s.sort()
     277        return s
     278
     279    def render_introducers_row(self, ctx, s):
     280        (furl, connected, ic) = s
     281        service_connection_status = "yes" if connected else "no"
     282
     283        since = ic.get_since()
     284        service_connection_status_rel_time = render_time_delta(since, self.now_fn())
     285        service_connection_status_abs_time = render_time_attr(since)
     286
     287        last_received_data_time = ic.get_last_received_data_time()
     288        last_received_data_rel_time = render_time_delta(last_received_data_time, self.now_fn())
     289        last_received_data_abs_time = render_time_attr(last_received_data_time)
     290
     291        ctx.fillSlots("introducer_furl", "%s" % (furl))
     292        ctx.fillSlots("service_connection_status", "%s" % (service_connection_status,))
     293        ctx.fillSlots("service_connection_status_alt",
     294            self._connectedalts[service_connection_status])
     295        ctx.fillSlots("service_connection_status_abs_time", service_connection_status_abs_time)
     296        ctx.fillSlots("service_connection_status_rel_time", service_connection_status_rel_time)
     297        ctx.fillSlots("last_received_data_abs_time", last_received_data_abs_time)
     298        ctx.fillSlots("last_received_data_rel_time", last_received_data_rel_time)
     299        return ctx.tag
    256300
    257301    def data_helper_furl_prefix(self, ctx, data):
     
    338382        ctx.fillSlots("address", addr)
    339383        ctx.fillSlots("service_connection_status", service_connection_status)
    340         ctx.fillSlots("service_connection_status_alt", self._connectedalts[service_connection_status])
    341         ctx.fillSlots("connected-bool", bool(rhost))
     384        ctx.fillSlots("service_connection_status_alt",
     385                      self._connectedalts[service_connection_status])
    342386        ctx.fillSlots("service_connection_status_abs_time", service_connection_status_abs_time)
    343387        ctx.fillSlots("service_connection_status_rel_time", service_connection_status_rel_time)
  • TabularUnified src/allmydata/web/welcome.xhtml

    ra8899c8 r4f717ba  
    139139                <div>
    140140                  <h3>
    141                     <div class="status-indicator"><img><n:attr name="src">img/connected-<n:invisible n:render="string" n:data="connected_to_introducer" />.png</n:attr><n:attr name="alt"><n:invisible n:render="string" n:data="connected_to_introducer_alt" /></n:attr></img></div>
     141                    <div class="status-indicator"><img><n:attr name="src">img/connected-<n:invisible n:render="string" n:data="connected_to_at_least_one_introducer" />.png</n:attr><n:attr name="alt"><n:invisible n:render="string" n:data="connected_to_at_least_one_introducer_alt" /></n:attr></img></div>
    142142                    <div n:render="string" n:data="introducer_description" />
    143143                  </h3>
    144                   <div class="furl" n:render="string" n:data="introducer_furl_prefix" />
    145144                </div>
    146145                <div>
     
    198197            <tr n:pattern="empty"><td colspan="5">You are not presently connected to any peers</td></tr>
    199198          </table>
     199          <div class="row-fluid">
     200            <h2>Connected to <span n:render="string" n:data="connected_introducers" /> of <span n:render="string" n:data="total_introducers" /> introducers</h2>
     201          </div>
     202          <table class="table table-striped table-bordered peer-status" n:render="sequence" n:data="introducers">
     203            <thead>
     204              <tr n:pattern="header">
     205                <td><h3>Address</h3></td>
     206                <td><h3>Last&nbsp;RX</h3></td>
     207              </tr>
     208            </thead>
     209            <tr n:pattern="item" n:render="introducers_row">
     210              <td class="nickname-and-peerid">
     211                <div class="status-indicator"><img><n:attr name="src">img/connected-<n:slot name="service_connection_status" />.png</n:attr><n:attr name="alt"><n:slot name="service_connection_status_alt" /></n:attr></img></div>
     212                <a class="timestamp"><n:attr name="title"><n:slot name="service_connection_status_abs_time"/></n:attr><n:slot name="service_connection_status_rel_time"/></a>
     213                <div class="furl"><n:slot name="introducer_furl"/></div>
     214              </td>
     215              <td class="service-last-received-data"><a class="timestamp"><n:attr name="title"><n:slot name="last_received_data_abs_time"/></n:attr><n:slot name="last_received_data_rel_time"/></a></td>
     216            </tr>
     217            <tr n:pattern="empty"><td colspan="2">No introducers are configured.</td></tr>
     218          </table>
    200219        </div><!--/span-->
    201220      </div><!--/row-->
Note: See TracChangeset for help on using the changeset viewer.