Ticket #1118: happy_share_distribution.dpatch.txt

File happy_share_distribution.dpatch.txt, 37.4 KB (added by zooko, at 2010-07-19T05:07:38Z)
Line 
1Sun Jul 18 16:15:37 MDT 2010  zooko@zooko.com
2  * immutable: test for #1118
3
4Sun Jul 18 22:50:47 MDT 2010  zooko@zooko.com
5  * immutable: extend the tests to check that the shares that got uploaded really do make a sufficiently Happy distribution
6  This patch also renames some instances of "find_shares()" to "find_all_shares()" and other instances to "find_uri_shares()" as appropriate -- the conflation between those names confused me at first when writing these tests.
7
8New patches:
9
10[immutable: test for #1118
11zooko@zooko.com**20100718221537
12 Ignore-this: 8882aabe2aaec6a0148c87e735d817ad
13] {
14hunk ./src/allmydata/immutable/upload.py 919
15             for shnum in peer.buckets:
16                 self._peer_trackers[shnum] = peer
17                 servermap.setdefault(shnum, set()).add(peer.peerid)
18-        assert len(buckets) == sum([len(peer.buckets) for peer in used_peers])
19+        assert len(buckets) == sum([len(peer.buckets) for peer in used_peers]), "%s (%s) != %s (%s)" % (len(buckets), buckets, sum([len(peer.buckets) for peer in used_peers]), [(p.buckets, p.peerid) for p in used_peers])
20         encoder.set_shareholders(buckets, servermap)
21 
22     def _encrypted_done(self, verifycap):
23hunk ./src/allmydata/test/test_upload.py 1789
24         return d
25     test_problem_layout_comment_187.todo = "this isn't fixed yet"
26 
27+    def test_problem_layout_ticket_1118(self):
28+        # #1118 includes a report from a user who hit an assertion in
29+        # the upload code with this layout.
30+        self.basedir = self.mktemp()
31+        d = self._setup_and_upload(k=2, n=4)
32+
33+        # server 0: no shares
34+        # server 1: shares 0, 3
35+        # server 3: share 1
36+        # server 2: share 2
37+        # The order that they get queries is 0, 1, 3, 2
38+        def _setup(ign):
39+            self._add_server(server_number=0)
40+            self._add_server_with_share(server_number=1, share_number=0)
41+            self._add_server_with_share(server_number=2, share_number=2)
42+            self._add_server_with_share(server_number=3, share_number=1)
43+            # Copy shares
44+            self._copy_share_to_server(3, 1)
45+            storedir = self.get_serverdir(0)
46+            # remove the storedir, wiping out any existing shares
47+            shutil.rmtree(storedir)
48+            # create an empty storedir to replace the one we just removed
49+            os.mkdir(storedir)
50+            client = self.g.clients[0]
51+            client.DEFAULT_ENCODING_PARAMETERS['happy'] = 4
52+            return client
53+
54+        d.addCallback(_setup)
55+        d.addCallback(lambda client:
56+                          client.upload(upload.Data("data" * 10000, convergence="")))
57+        return d
58 
59     def test_upload_succeeds_with_some_homeless_shares(self):
60         # If the upload is forced to stop trying to place shares before
61}
62[immutable: extend the tests to check that the shares that got uploaded really do make a sufficiently Happy distribution
63zooko@zooko.com**20100719045047
64 Ignore-this: 89c33a7b795e23018667351045a8d5d0
65 This patch also renames some instances of "find_shares()" to "find_all_shares()" and other instances to "find_uri_shares()" as appropriate -- the conflation between those names confused me at first when writing these tests.
66] {
67hunk ./src/allmydata/test/common.py 959
68         d.addCallback(_stash_it)
69         return d
70 
71-    def find_shares(self, unused=None):
72+    def find_all_shares(self, unused=None):
73         """Locate shares on disk. Returns a dict that maps
74         (clientnum,sharenum) to a string that contains the share container
75         (copied directly from the disk, containing leases etc). You can
76hunk ./src/allmydata/test/common.py 984
77 
78     def replace_shares(self, newshares, storage_index):
79         """Replace shares on disk. Takes a dictionary in the same form
80-        as find_shares() returns."""
81+        as find_all_shares() returns."""
82 
83         for i, c in enumerate(self.clients):
84             sharedir = c.getServiceNamed("storage").sharedir
85hunk ./src/allmydata/test/common.py 1009
86     def _delete_a_share(self, unused=None, sharenum=None):
87         """ Delete one share. """
88 
89-        shares = self.find_shares()
90+        shares = self.find_all_shares()
91         ks = shares.keys()
92         if sharenum is not None:
93             k = [ key for key in shares.keys() if key[1] == sharenum ][0]
94hunk ./src/allmydata/test/common.py 1021
95         return unused
96 
97     def _corrupt_a_share(self, unused, corruptor_func, sharenum):
98-        shares = self.find_shares()
99+        shares = self.find_all_shares()
100         ks = [ key for key in shares.keys() if key[1] == sharenum ]
101         assert ks, (shares.keys(), sharenum)
102         k = ks[0]
103hunk ./src/allmydata/test/common.py 1031
104 
105     def _corrupt_all_shares(self, unused, corruptor_func):
106         """ All shares on disk will be corrupted by corruptor_func. """
107-        shares = self.find_shares()
108+        shares = self.find_all_shares()
109         for k in shares.keys():
110             self._corrupt_a_share(unused, corruptor_func, k[1])
111         return corruptor_func
112hunk ./src/allmydata/test/common.py 1038
113 
114     def _corrupt_a_random_share(self, unused, corruptor_func):
115         """ Exactly one share on disk will be corrupted by corruptor_func. """
116-        shares = self.find_shares()
117+        shares = self.find_all_shares()
118         ks = shares.keys()
119         k = random.choice(ks)
120         self._corrupt_a_share(unused, corruptor_func, k[1])
121hunk ./src/allmydata/test/no_network.py 305
122             ss = self.g.servers_by_number[i]
123             yield (i, ss, ss.storedir)
124 
125-    def find_shares(self, uri):
126+    def find_uri_shares(self, uri):
127         si = tahoe_uri.from_string(uri).get_storage_index()
128         prefixdir = storage_index_to_dir(si)
129         shares = []
130hunk ./src/allmydata/test/no_network.py 326
131         os.unlink(sharefile)
132 
133     def delete_shares_numbered(self, uri, shnums):
134-        for (i_shnum, i_serverid, i_sharefile) in self.find_shares(uri):
135+        for (i_shnum, i_serverid, i_sharefile) in self.find_uri_shares(uri):
136             if i_shnum in shnums:
137                 os.unlink(i_sharefile)
138 
139hunk ./src/allmydata/test/no_network.py 336
140         open(sharefile, "wb").write(corruptdata)
141 
142     def corrupt_shares_numbered(self, uri, shnums, corruptor, debug=False):
143-        for (i_shnum, i_serverid, i_sharefile) in self.find_shares(uri):
144+        for (i_shnum, i_serverid, i_sharefile) in self.find_uri_shares(uri):
145             if i_shnum in shnums:
146                 sharedata = open(i_sharefile, "rb").read()
147                 corruptdata = corruptor(sharedata, debug=debug)
148hunk ./src/allmydata/test/test_cli.py 1998
149 
150         def _clobber_shares(ignored):
151             # delete one, corrupt a second
152-            shares = self.find_shares(self.uri)
153+            shares = self.find_uri_shares(self.uri)
154             self.failUnlessReallyEqual(len(shares), 10)
155             os.unlink(shares[0][2])
156             cso = debug.CorruptShareOptions()
157hunk ./src/allmydata/test/test_cli.py 2123
158         d.addCallback(_check_stats)
159 
160         def _clobber_shares(ignored):
161-            shares = self.find_shares(self.uris[u"gööd"])
162+            shares = self.find_uri_shares(self.uris[u"gööd"])
163             self.failUnlessReallyEqual(len(shares), 10)
164             os.unlink(shares[0][2])
165 
166hunk ./src/allmydata/test/test_cli.py 2127
167-            shares = self.find_shares(self.uris["mutable"])
168+            shares = self.find_uri_shares(self.uris["mutable"])
169             cso = debug.CorruptShareOptions()
170             cso.stdout = StringIO()
171             cso.parseOptions([shares[1][2]])
172hunk ./src/allmydata/test/test_deepcheck.py 994
173         self.delete_shares_numbered(node.get_uri(), [0,1])
174 
175     def _corrupt_some_shares(self, node):
176-        for (shnum, serverid, sharefile) in self.find_shares(node.get_uri()):
177+        for (shnum, serverid, sharefile) in self.find_uri_shares(node.get_uri()):
178             if shnum in (0,1):
179                 self._run_cli(["debug", "corrupt-share", sharefile])
180 
181hunk ./src/allmydata/test/test_hung_server.py 67
182             os.makedirs(si_dir)
183         new_sharefile = os.path.join(si_dir, str(sharenum))
184         shutil.copy(sharefile, new_sharefile)
185-        self.shares = self.find_shares(self.uri)
186+        self.shares = self.find_uri_shares(self.uri)
187         # Make sure that the storage server has the share.
188         self.failUnless((sharenum, ss.original.my_nodeid, new_sharefile)
189                         in self.shares)
190hunk ./src/allmydata/test/test_hung_server.py 98
191             d = nm.create_mutable_file(mutable_plaintext)
192             def _uploaded_mutable(node):
193                 self.uri = node.get_uri()
194-                self.shares = self.find_shares(self.uri)
195+                self.shares = self.find_uri_shares(self.uri)
196             d.addCallback(_uploaded_mutable)
197         else:
198             data = upload.Data(immutable_plaintext, convergence="")
199hunk ./src/allmydata/test/test_hung_server.py 105
200             d = self.c0.upload(data)
201             def _uploaded_immutable(upload_res):
202                 self.uri = upload_res.uri
203-                self.shares = self.find_shares(self.uri)
204+                self.shares = self.find_uri_shares(self.uri)
205             d.addCallback(_uploaded_immutable)
206         return d
207 
208hunk ./src/allmydata/test/test_immutable.py 14
209         # replace_shares, and asserting that the new set of shares equals the
210         # old is more to test this test code than to test the Tahoe code...
211         d = defer.succeed(None)
212-        d.addCallback(self.find_shares)
213+        d.addCallback(self.find_all_shares)
214         stash = [None]
215         def _stash_it(res):
216             stash[0] = res
217hunk ./src/allmydata/test/test_repairer.py 90
218         d.addCallback(_check)
219 
220         def _remove_all(ignored):
221-            for sh in self.find_shares(self.uri):
222+            for sh in self.find_uri_shares(self.uri):
223                 self.delete_share(sh)
224         d.addCallback(_remove_all)
225 
226hunk ./src/allmydata/test/test_repairer.py 325
227         def _grab_sh0(res):
228             self.sh0_file = [sharefile
229                              for (shnum, serverid, sharefile)
230-                             in self.find_shares(self.uri)
231+                             in self.find_uri_shares(self.uri)
232                              if shnum == 0][0]
233             self.sh0_orig = open(self.sh0_file, "rb").read()
234         d.addCallback(_grab_sh0)
235hunk ./src/allmydata/test/test_repairer.py 470
236         self.set_up_grid(num_clients=2)
237         d = self.upload_and_stash()
238 
239-        d.addCallback(lambda ignored: self.find_shares(self.uri))
240+        d.addCallback(lambda ignored: self.find_uri_shares(self.uri))
241         def _stash_shares(oldshares):
242             self.oldshares = oldshares
243         d.addCallback(_stash_shares)
244hunk ./src/allmydata/test/test_repairer.py 474
245-        d.addCallback(lambda ignored: self.find_shares(self.uri))
246+        d.addCallback(lambda ignored: self.find_uri_shares(self.uri))
247         def _compare(newshares):
248             self.failUnlessEqual(newshares, self.oldshares)
249         d.addCallback(_compare)
250hunk ./src/allmydata/test/test_repairer.py 485
251             for sh in self.oldshares[1:8]:
252                 self.delete_share(sh)
253         d.addCallback(_delete_8)
254-        d.addCallback(lambda ignored: self.find_shares(self.uri))
255+        d.addCallback(lambda ignored: self.find_uri_shares(self.uri))
256         d.addCallback(lambda shares: self.failUnlessEqual(len(shares), 2))
257 
258         d.addCallback(lambda ignored:
259hunk ./src/allmydata/test/test_repairer.py 502
260         # test share corruption
261         def _test_corrupt(ignored):
262             olddata = {}
263-            shares = self.find_shares(self.uri)
264+            shares = self.find_uri_shares(self.uri)
265             for (shnum, serverid, sharefile) in shares:
266                 olddata[ (shnum, serverid) ] = open(sharefile, "rb").read()
267             for sh in shares:
268hunk ./src/allmydata/test/test_repairer.py 513
269         d.addCallback(_test_corrupt)
270 
271         def _remove_all(ignored):
272-            for sh in self.find_shares(self.uri):
273+            for sh in self.find_uri_shares(self.uri):
274                 self.delete_share(sh)
275         d.addCallback(_remove_all)
276hunk ./src/allmydata/test/test_repairer.py 516
277-        d.addCallback(lambda ignored: self.find_shares(self.uri))
278+        d.addCallback(lambda ignored: self.find_uri_shares(self.uri))
279         d.addCallback(lambda shares: self.failUnlessEqual(shares, []))
280 
281         return d
282hunk ./src/allmydata/test/test_repairer.py 547
283 
284             # Now we inspect the filesystem to make sure that it has 10
285             # shares.
286-            shares = self.find_shares(self.uri)
287+            shares = self.find_uri_shares(self.uri)
288             self.failIf(len(shares) < 10)
289         d.addCallback(_check_results)
290 
291hunk ./src/allmydata/test/test_repairer.py 592
292             self.failUnless(post.is_healthy(), post.data)
293 
294             # Make sure we really have 10 shares.
295-            shares = self.find_shares(self.uri)
296+            shares = self.find_uri_shares(self.uri)
297             self.failIf(len(shares) < 10)
298         d.addCallback(_check_results)
299 
300hunk ./src/allmydata/test/test_repairer.py 653
301     def OFF_test_repair_from_corruption_of_1(self):
302         d = defer.succeed(None)
303 
304-        d.addCallback(self.find_shares)
305+        d.addCallback(self.find_all_shares)
306         stash = [None]
307         def _stash_it(res):
308             stash[0] = res
309hunk ./src/allmydata/test/test_repairer.py 688
310 
311                 # Now we inspect the filesystem to make sure that it has 10
312                 # shares.
313-                shares = self.find_shares()
314+                shares = self.find_all_shares()
315                 self.failIf(len(shares) < 10)
316 
317                 # Now we assert that the verifier reports the file as healthy.
318hunk ./src/allmydata/test/test_system.py 386
319 
320         return d
321 
322-    def _find_shares(self, basedir):
323+    def _find_all_shares(self, basedir):
324         shares = []
325         for (dirpath, dirnames, filenames) in os.walk(basedir):
326             if "storage" not in dirpath:
327hunk ./src/allmydata/test/test_system.py 478
328         def _test_debug(res):
329             # find a share. It is important to run this while there is only
330             # one slot in the grid.
331-            shares = self._find_shares(self.basedir)
332+            shares = self._find_all_shares(self.basedir)
333             (client_num, storage_index, filename, shnum) = shares[0]
334             log.msg("test_system.SystemTest.test_mutable._test_debug using %s"
335                     % filename)
336hunk ./src/allmydata/test/test_system.py 581
337         def _corrupt_shares(res):
338             # run around and flip bits in all but k of the shares, to test
339             # the hash checks
340-            shares = self._find_shares(self.basedir)
341+            shares = self._find_all_shares(self.basedir)
342             ## sort by share number
343             #shares.sort( lambda a,b: cmp(a[3], b[3]) )
344             where = dict([ (shnum, filename)
345hunk ./src/allmydata/test/test_upload.py 1
346+# -*- coding: utf-8 -*-
347+
348 import os, shutil
349 from cStringIO import StringIO
350 from twisted.trial import unittest
351hunk ./src/allmydata/test/test_upload.py 689
352         d.addCallback(_done)
353         return d
354 
355+# copied from python docs because itertools.combinations was added in
356+# python 2.6 and we support >= 2.4.
357+def combinations(iterable, r):
358+    # combinations('ABCD', 2) --> AB AC AD BC BD CD
359+    # combinations(range(4), 3) --> 012 013 023 123
360+    pool = tuple(iterable)
361+    n = len(pool)
362+    if r > n:
363+        return
364+    indices = range(r)
365+    yield tuple(pool[i] for i in indices)
366+    while True:
367+        for i in reversed(range(r)):
368+            if indices[i] != i + n - r:
369+                break
370+        else:
371+            return
372+        indices[i] += 1
373+        for j in range(i+1, r):
374+            indices[j] = indices[j-1] + 1
375+        yield tuple(pool[i] for i in indices)
376+
377+def is_happy_enough(servertoshnums, h, k):
378+    """ I calculate whether servertoshnums achieves happiness level h. I do this with a naïve "brute force search" approach. (See src/allmydata/util/happinessutil.py for a better algorithm.) """
379+    if len(servertoshnums) < h:
380+        return False
381+    # print "servertoshnums: ", servertoshnums, h, k
382+    for happysetcombo in combinations(servertoshnums.iterkeys(), h):
383+        # print "happysetcombo: ", happysetcombo
384+        for subsetcombo in combinations(happysetcombo, k):
385+            shnums = reduce(set.union, [ servertoshnums[s] for s in subsetcombo ])
386+            # print "subsetcombo: ", subsetcombo, ", shnums: ", shnums
387+            if len(shnums) < k:
388+                # print "NOT HAAPP{Y", shnums, k
389+                return False
390+    # print "HAAPP{Y"
391+    return True
392+
393 class EncodingParameters(GridTestMixin, unittest.TestCase, SetDEPMixin,
394     ShouldFailMixin):
395hunk ./src/allmydata/test/test_upload.py 729
396+    def find_all_shares(self, unused=None):
397+        """Locate shares on disk. Returns a dict that maps
398+        server to set of sharenums.
399+        """
400+        assert self.g, "I tried to find a grid at self.g, but failed"
401+        servertoshnums = {} # k: server, v: set(shnum)
402+
403+        for i, c in self.g.servers_by_number.iteritems():
404+            for (dirp, dirns, fns) in os.walk(c.sharedir):
405+                for fn in fns:
406+                    try:
407+                        sharenum = int(fn)
408+                    except TypeError:
409+                        # Whoops, I guess that's not a share file then.
410+                        pass
411+                    else:
412+                        servertoshnums.setdefault(i, set()).add(sharenum)
413+
414+        return servertoshnums
415+
416     def _do_upload_with_broken_servers(self, servers_to_break):
417         """
418         I act like a normal upload, but before I send the results of
419hunk ./src/allmydata/test/test_upload.py 792
420         d.addCallback(_have_shareholders)
421         return d
422 
423+    def _has_happy_share_distribution(self):
424+        servertoshnums = self.find_all_shares()
425+        k = self.g.clients[0].DEFAULT_ENCODING_PARAMETERS['k']
426+        h = self.g.clients[0].DEFAULT_ENCODING_PARAMETERS['happy']
427+        return is_happy_enough(servertoshnums, h, k)
428 
429     def _add_server(self, server_number, readonly=False):
430         assert self.g, "I tried to find a grid at self.g, but failed"
431hunk ./src/allmydata/test/test_upload.py 828
432                                           str(share_number))
433         if old_share_location != new_share_location:
434             shutil.copy(old_share_location, new_share_location)
435-        shares = self.find_shares(self.uri)
436+        shares = self.find_uri_shares(self.uri)
437         # Make sure that the storage server has the share.
438         self.failUnless((share_number, ss.my_nodeid, new_share_location)
439                         in shares)
440hunk ./src/allmydata/test/test_upload.py 858
441             self.uri = ur.uri
442         d.addCallback(_store_uri)
443         d.addCallback(lambda ign:
444-            self.find_shares(self.uri))
445+            self.find_uri_shares(self.uri))
446         def _store_shares(shares):
447             self.shares = shares
448         d.addCallback(_store_shares)
449hunk ./src/allmydata/test/test_upload.py 944
450         d.addCallback(lambda ign: self._add_server(4, False))
451         # and this time the upload ought to succeed
452         d.addCallback(lambda ign: c.upload(DATA))
453+        d.addCallback(lambda ign:
454+            self.failUnless(self._has_happy_share_distribution()))
455         return d
456 
457 
458hunk ./src/allmydata/test/test_upload.py 1082
459         d.addCallback(_reset_encoding_parameters)
460         d.addCallback(lambda client:
461             client.upload(upload.Data("data" * 10000, convergence="")))
462+        d.addCallback(lambda ign:
463+            self.failUnless(self._has_happy_share_distribution()))
464 
465 
466         # This scenario is basically comment:53, but changed so that the
467hunk ./src/allmydata/test/test_upload.py 1122
468         d.addCallback(_reset_encoding_parameters)
469         d.addCallback(lambda client:
470             client.upload(upload.Data("data" * 10000, convergence="")))
471+        d.addCallback(lambda ign:
472+            self.failUnless(self._has_happy_share_distribution()))
473 
474 
475         # Try the same thing, but with empty servers after the first one
476hunk ./src/allmydata/test/test_upload.py 1155
477         # servers of happiness were pushed.
478         d.addCallback(lambda results:
479             self.failUnlessEqual(results.pushed_shares, 3))
480+        d.addCallback(lambda ign:
481+            self.failUnless(self._has_happy_share_distribution()))
482         return d
483 
484     def test_problem_layout_ticket1124(self):
485hunk ./src/allmydata/test/test_upload.py 1182
486         d.addCallback(_setup)
487         d.addCallback(lambda client:
488             client.upload(upload.Data("data" * 10000, convergence="")))
489+        d.addCallback(lambda ign:
490+            self.failUnless(self._has_happy_share_distribution()))
491         return d
492     test_problem_layout_ticket1124.todo = "Fix this after 1.7.1 release."
493 
494hunk ./src/allmydata/test/test_upload.py 1221
495         d.addCallback(_reset_encoding_parameters)
496         d.addCallback(lambda client:
497             client.upload(upload.Data("data" * 10000, convergence="")))
498+        d.addCallback(lambda ign:
499+            self.failUnless(self._has_happy_share_distribution()))
500         return d
501 
502 
503hunk ./src/allmydata/test/test_upload.py 1260
504         d.addCallback(_reset_encoding_parameters)
505         d.addCallback(lambda client:
506             client.upload(upload.Data("data" * 10000, convergence="")))
507+        d.addCallback(lambda ign:
508+            self.failUnless(self._has_happy_share_distribution()))
509         return d
510 
511 
512hunk ./src/allmydata/test/test_upload.py 1571
513         d.addCallback(_prepare_client)
514         d.addCallback(lambda client:
515             client.upload(upload.Data("data" * 10000, convergence="")))
516+        d.addCallback(lambda ign:
517+            self.failUnless(self._has_happy_share_distribution()))
518         return d
519 
520 
521hunk ./src/allmydata/test/test_upload.py 1867
522         d.addCallback(_setup)
523         d.addCallback(lambda client:
524             client.upload(upload.Data("data" * 10000, convergence="")))
525+        d.addCallback(lambda ign:
526+            self.failUnless(self._has_happy_share_distribution()))
527         return d
528     test_problem_layout_comment_187.todo = "this isn't fixed yet"
529 
530hunk ./src/allmydata/test/test_upload.py 1902
531         d.addCallback(_setup)
532         d.addCallback(lambda client:
533                           client.upload(upload.Data("data" * 10000, convergence="")))
534+        d.addCallback(lambda ign:
535+            self.failUnless(self._has_happy_share_distribution()))
536         return d
537 
538     def test_upload_succeeds_with_some_homeless_shares(self):
539hunk ./src/allmydata/test/test_upload.py 1940
540         d.addCallback(_server_setup)
541         d.addCallback(lambda client:
542             client.upload(upload.Data("data" * 10000, convergence="")))
543+        d.addCallback(lambda ign:
544+            self.failUnless(self._has_happy_share_distribution()))
545         return d
546 
547 
548hunk ./src/allmydata/test/test_upload.py 1969
549         d.addCallback(_server_setup)
550         d.addCallback(lambda client:
551             client.upload(upload.Data("data" * 10000, convergence="")))
552+        d.addCallback(lambda ign:
553+            self.failUnless(self._has_happy_share_distribution()))
554         return d
555 
556 
557hunk ./src/allmydata/test/test_web.py 3198
558         d.addCallback(_compute_fileurls)
559 
560         def _clobber_shares(ignored):
561-            good_shares = self.find_shares(self.uris["good"])
562+            good_shares = self.find_uri_shares(self.uris["good"])
563             self.failUnlessReallyEqual(len(good_shares), 10)
564hunk ./src/allmydata/test/test_web.py 3200
565-            sick_shares = self.find_shares(self.uris["sick"])
566+            sick_shares = self.find_uri_shares(self.uris["sick"])
567             os.unlink(sick_shares[0][2])
568hunk ./src/allmydata/test/test_web.py 3202
569-            dead_shares = self.find_shares(self.uris["dead"])
570+            dead_shares = self.find_uri_shares(self.uris["dead"])
571             for i in range(1, 10):
572                 os.unlink(dead_shares[i][2])
573hunk ./src/allmydata/test/test_web.py 3205
574-            c_shares = self.find_shares(self.uris["corrupt"])
575+            c_shares = self.find_uri_shares(self.uris["corrupt"])
576             cso = CorruptShareOptions()
577             cso.stdout = StringIO()
578             cso.parseOptions([c_shares[0][2]])
579hunk ./src/allmydata/test/test_web.py 3339
580         d.addCallback(_compute_fileurls)
581 
582         def _clobber_shares(ignored):
583-            good_shares = self.find_shares(self.uris["good"])
584+            good_shares = self.find_uri_shares(self.uris["good"])
585             self.failUnlessReallyEqual(len(good_shares), 10)
586hunk ./src/allmydata/test/test_web.py 3341
587-            sick_shares = self.find_shares(self.uris["sick"])
588+            sick_shares = self.find_uri_shares(self.uris["sick"])
589             os.unlink(sick_shares[0][2])
590hunk ./src/allmydata/test/test_web.py 3343
591-            dead_shares = self.find_shares(self.uris["dead"])
592+            dead_shares = self.find_uri_shares(self.uris["dead"])
593             for i in range(1, 10):
594                 os.unlink(dead_shares[i][2])
595hunk ./src/allmydata/test/test_web.py 3346
596-            c_shares = self.find_shares(self.uris["corrupt"])
597+            c_shares = self.find_uri_shares(self.uris["corrupt"])
598             cso = CorruptShareOptions()
599             cso.stdout = StringIO()
600             cso.parseOptions([c_shares[0][2]])
601hunk ./src/allmydata/test/test_web.py 3407
602         d.addCallback(_compute_fileurls)
603 
604         def _clobber_shares(ignored):
605-            sick_shares = self.find_shares(self.uris["sick"])
606+            sick_shares = self.find_uri_shares(self.uris["sick"])
607             os.unlink(sick_shares[0][2])
608         d.addCallback(_clobber_shares)
609 
610hunk ./src/allmydata/test/test_web.py 3897
611         #d.addCallback(_stash_uri, "corrupt")
612 
613         def _clobber_shares(ignored):
614-            good_shares = self.find_shares(self.uris["good"])
615+            good_shares = self.find_uri_shares(self.uris["good"])
616             self.failUnlessReallyEqual(len(good_shares), 10)
617hunk ./src/allmydata/test/test_web.py 3899
618-            sick_shares = self.find_shares(self.uris["sick"])
619+            sick_shares = self.find_uri_shares(self.uris["sick"])
620             os.unlink(sick_shares[0][2])
621hunk ./src/allmydata/test/test_web.py 3901
622-            #dead_shares = self.find_shares(self.uris["dead"])
623+            #dead_shares = self.find_uri_shares(self.uris["dead"])
624             #for i in range(1, 10):
625             #    os.unlink(dead_shares[i][2])
626 
627hunk ./src/allmydata/test/test_web.py 3905
628-            #c_shares = self.find_shares(self.uris["corrupt"])
629+            #c_shares = self.find_uri_shares(self.uris["corrupt"])
630             #cso = CorruptShareOptions()
631             #cso.stdout = StringIO()
632             #cso.parseOptions([c_shares[0][2]])
633hunk ./src/allmydata/test/test_web.py 3961
634 
635     def _count_leases(self, ignored, which):
636         u = self.uris[which]
637-        shares = self.find_shares(u)
638+        shares = self.find_uri_shares(u)
639         lease_counts = []
640         for shnum, serverid, fn in shares:
641             sf = get_share_file(fn)
642}
643
644Context:
645
646[docs/logging.txt: document that _trial_temp/test.log does not receive messages below level=OPERATIONAL, due to <http://foolscap.lothar.com/trac/ticket/154>.
647david-sarah@jacaranda.org**20100718230420
648 Ignore-this: aef40f2e74ddeabee5e122e8d80893a1
649]
650[immutable: test for #1124
651zooko@zooko.com**20100718222907
652 Ignore-this: 1766e3cbab92ea2a9e246f40eb6e770b
653]
654[trivial: fix unused import (sorry about that, pyflakes)
655zooko@zooko.com**20100718215133
656 Ignore-this: c2414e443405072b51d552295f2c0e8c
657]
658[tests, NEWS, CREDITS re: #1117
659zooko@zooko.com**20100718203225
660 Ignore-this: 1f08be2c692fb72cc0dd023259f11354
661 Give Brian and Kevan promotions, move release date in NEWS to the 18th, commit Brian's test for #1117.
662 fixes #1117
663]
664[test/test_upload.py: test to see that aborted buckets are ignored by the storage server
665Kevan Carstensen <kevan@isnotajoke.com>**20100716001046
666 Ignore-this: cc075c24b1c86d737f3199af894cc780
667]
668[test/test_storage.py: test for the new remote_abort semantics.
669Kevan Carstensen <kevan@isnotajoke.com>**20100715232148
670 Ignore-this: d3d6491f17bf670e770ca4b385007515
671]
672[storage/immutable.py: make remote_abort btell the storage server about aborted buckets.
673Kevan Carstensen <kevan@isnotajoke.com>**20100715232105
674 Ignore-this: 16ab0090676355abdd5600ed44ff19c9
675]
676[test/test_upload.py: changes to test plumbing for #1117 tests
677Kevan Carstensen <kevan@isnotajoke.com>**20100715231820
678 Ignore-this: 78a6d359d7bf8529d283e2815bf1e2de
679 
680     - Add a callRemoteOnly method to FakeBucketWriter.
681     - Change the abort method in FakeBucketWriter to not return a
682       RuntimeError.
683]
684[immutable/upload.py: abort buckets if peer selection fails
685Kevan Carstensen <kevan@isnotajoke.com>**20100715231714
686 Ignore-this: 2a0b643a22284df292d8ed9d91b1fd37
687]
688[test_encodingutil: correct an error in the previous patch to StdlibUnicode.test_open_representable.
689david-sarah@jacaranda.org**20100718151420
690 Ignore-this: af050955f623fbc0e4d78e15a0a8a144
691]
692[NEWS: Forward-compatibility improvements for non-ASCII caps (#1051).
693david-sarah@jacaranda.org**20100718143622
694 Ignore-this: 1edfebc4bd38a3b5c35e75c99588153f
695]
696[test_dirnode and test_web: don't use failUnlessReallyEqual in cases where the return type from simplejson.loads can vary between unicode and str. Use to_str when comparing URIs parsed from JSON.
697david-sarah@jacaranda.org**20100718142915
698 Ignore-this: c4e78ef4b1478dd400da71cf077ffa4a
699]
700[test_encodingutil: StdlibUnicode.test_open_representable no longer uses a mock.
701david-sarah@jacaranda.org**20100718125412
702 Ignore-this: 4bf373a5e2dfe4209e5e364124af29a3
703]
704[docs: add comment clarifying #1051
705zooko@zooko.com**20100718053250
706 Ignore-this: 6cfc0930434cbdbbc262dabb58f1505d
707]
708[docs: update NEWS
709zooko@zooko.com**20100718053225
710 Ignore-this: 63d5c782ef84812e6d010f0590866831
711]
712[Add tests of caps from the future that have non-ASCII characters in them (encoded as UTF-8). The changes to test_uri.py, test_client.py, and test_dirnode.py add tests of non-ASCII future caps in addition to the current tests. The changes to test_web.py just replace the tests of all-ASCII future caps with tests of non-ASCII future caps. We also change uses of failUnlessEqual to failUnlessReallyEqual, in order to catch cases where the type of a string is not as expected.
713david-sarah@jacaranda.org**20100711200252
714 Ignore-this: c2f193352369d32e06865f8f3e951894
715]
716[Debian documentation update
717jacob@appelbaum.net**20100305003004]
718[debian-docs-patch-final
719jacob@appelbaum.net**20100304085955]
720[M-x whitespace-cleanup
721zooko@zooko.com**20100718032739
722 Ignore-this: babfd4af6ad2fc885c957fd5c8b10c3f
723]
724[docs: tidy up NEWS a little
725zooko@zooko.com**20100718032434
726 Ignore-this: 54f2820fd1a37c8967609f6bfc4e5e18
727]
728[benchmarking: update bench_dirnode.py to reflect the new directory interfaces
729zooko@zooko.com**20100718031710
730 Ignore-this: 368ba523dd3de80d9da29cd58afbe827
731]
732[test_encodingutil: fix test_open_representable, which is only valid when run on a platform for which we know an unrepresentable filename.
733david-sarah@jacaranda.org**20100718030333
734 Ignore-this: c114d92c17714a5d4ae005c15267d60c
735]
736[iputil.py: Add support for FreeBSD 7,8 and 9
737francois@ctrlaltdel.ch**20100718022832
738 Ignore-this: 1829b4cf4b91107f4cf87841e6167e99
739 committed by: zooko@zooko.com
740 date: 2010-07-17
741 and I also patched: NEWS and CREDITS
742]
743[NEWS: add snippet about #1083
744zooko@zooko.com**20100718020653
745 Ignore-this: d353a9d93cbc5a5e6ba4671f78d1e22b
746]
747[fileutil: docstrings for non-obvious usage restrictions on methods of EncryptedTemporaryFile.
748david-sarah@jacaranda.org**20100717054647
749 Ignore-this: 46d8fc10782fa8ec2b6c5b168c841943
750]
751[Move EncryptedTemporaryFile from SFTP frontend to allmydata.util.fileutil, and make the FTP frontend also use it (fixing #1083).
752david-sarah@jacaranda.org**20100711213721
753 Ignore-this: e452e8ca66391aa2a1a49afe0114f317
754]
755[NEWS: reorder NEWS snippets to be in descending order of interestingness
756zooko@zooko.com**20100718015929
757 Ignore-this: 146c42e88a9555a868a04a69dd0e5326
758]
759[Correct stringutils->encodingutil patch to be the newer version, rather than the old version that was committed in error.
760david-sarah@jacaranda.org**20100718013435
761 Ignore-this: c8940c4e1aa2e9acc80cd4fe54753cd8
762]
763[test_cli.py: fix error that crept in when rebasing the patch for #1072.
764david-sarah@jacaranda.org**20100718000123
765 Ignore-this: 3e8f6cc3a27b747c708221dd581934f4
766]
767[stringutils: add test for when sys.stdout has no encoding attribute (fixes #1099).
768david-sarah@jacaranda.org**20100717045816
769 Ignore-this: f28dce6940e909f12f354086d17db54f
770]
771[CLI: add 'tahoe unlink' as an alias to 'tahoe rm', for forward-compatibility.
772david-sarah@jacaranda.org**20100717220411
773 Ignore-this: 3ecdde7f2d0498514cef32e118e0b855
774]
775[minor code clean-up in dirnode.py
776zooko@zooko.com**20100714060255
777 Ignore-this: bb0ab2783203e605024b3e2f798256a1
778 Impose micro-POLA by passing only the writekey instead of the whole node object to {{{_encrypt_rw_uri()}}}. Remove DummyImmutableFileNode in nodemaker.py, which is obviated by this. Add micro-optimization by precomputing the netstring of the empty string and branching on whether the writekey is present or not outside of {{{_encrypt_rw_uri()}}}. Add doc about writekey to docstring.
779 fixes #967
780]
781[Rename stringutils to encodingutil, and drop listdir_unicode and open_unicode (since the Python stdlib functions work fine with Unicode paths). Also move some utility functions to fileutil.
782david-sarah@jacaranda.org**20100712003015
783 Ignore-this: 103b809d180df17a7283077c3104c7be
784]
785[Allow URIs passed in the initial JSON for t=mkdir-with-children, t=mkdir-immutable to be Unicode. Also pass the name of each child into nodemaker.create_from_cap for error reporting.
786david-sarah@jacaranda.org**20100711195525
787 Ignore-this: deac32d8b91ba26ede18905d3f7d2b93
788]
789[docs: CREDITS and NEWS
790zooko@zooko.com**20100714060150
791 Ignore-this: dc83e612f77d69e50ee975f07f6b16fe
792]
793[CREDITS: more creds for Kevan, plus utf-8 BOM
794zooko@zooko.com**20100619045503
795 Ignore-this: 72d02bdd7a0f324f1cee8cd399c7c6de
796]
797[cli.py: make command descriptions consistently end with a full stop.
798david-sarah@jacaranda.org**20100714014538
799 Ignore-this: 9ee7fa29ca2d1631db4049c2a389a97a
800]
801[SFTP: address some of the comments in zooko's review (#1106).
802david-sarah@jacaranda.org**20100712025537
803 Ignore-this: c3921638a2d4f1de2a776ae78e4dc37e
804]
805[docs/logging.txt: note that setting flogging vars might affect tests with race conditions.
806david-sarah@jacaranda.org**20100712050721
807 Ignore-this: fc1609d215fcd5561a57fd1226206f27
808]
809[test_storage.py: potential fix for failures when logging is enabled.
810david-sarah@jacaranda.org**19700713040546
811 Ignore-this: 5815693a0df3e64c52c3c6b7be2846c7
812]
813[upcase_since_on_welcome
814terrellrussell@gmail.com**20100708193903]
815[server_version_on_welcome_page.dpatch.txt
816freestorm77@gmail.com**20100605191721
817 Ignore-this: b450c76dc875f5ac8cca229a666cbd0a
818 
819 
820 - The storage server version is 0 for all storage nodes in the Welcome Page
821 
822 
823]
824[NEWS: add NEWS snippets about two recent patches
825zooko@zooko.com**20100708162058
826 Ignore-this: 6c9da6a0ad7351a960bdd60f81532899
827]
828[directory_html_top_banner.dpatch
829freestorm77@gmail.com**20100622205301
830 Ignore-this: 1d770d975e0c414c996564774f049bca
831 
832 The div tag with the link "Return to Welcome page" on the directory.xhtml page is not correct
833 
834]
835[tahoe_css_toolbar.dpatch
836freestorm77@gmail.com**20100622210046
837 Ignore-this: 5b3ebb2e0f52bbba718a932f80c246c0
838 
839 CSS modification to be correctly diplayed with Internet Explorer 8
840 
841 The links on the top of page directory.xhtml are not diplayed in the same line as display with Firefox.
842 
843]
844[runnin_test_tahoe_css.dpatch
845freestorm77@gmail.com**20100622214714
846 Ignore-this: e0db73d68740aad09a7b9ae60a08c05c
847 
848 Runnin test for changes in tahoe.css file
849 
850]
851[runnin_test_directory_xhtml.dpatch
852freestorm77@gmail.com**20100622201403
853 Ignore-this: f8962463fce50b9466405cb59fe11d43
854 
855 Runnin test for diretory.xhtml top banner
856 
857]
858[stringutils.py: tolerate sys.stdout having no 'encoding' attribute.
859david-sarah@jacaranda.org**20100626040817
860 Ignore-this: f42cad81cef645ee38ac1df4660cc850
861]
862[quickstart.html: python 2.5 -> 2.6 as recommended version
863david-sarah@jacaranda.org**20100705175858
864 Ignore-this: bc3a14645ea1d5435002966ae903199f
865]
866[SFTP: don't call .stopProducing on the producer registered with OverwriteableFileConsumer (which breaks with warner's new downloader).
867david-sarah@jacaranda.org**20100628231926
868 Ignore-this: 131b7a5787bc85a9a356b5740d9d996f
869]
870[docs/how_to_make_a_tahoe-lafs_release.txt: trivial correction, install.html should now be quickstart.html.
871david-sarah@jacaranda.org**20100625223929
872 Ignore-this: 99a5459cac51bd867cc11ad06927ff30
873]
874[setup: in the Makefile, refuse to upload tarballs unless someone has passed the environment variable "BB_BRANCH" with value "trunk"
875zooko@zooko.com**20100619034928
876 Ignore-this: 276ddf9b6ad7ec79e27474862e0f7d6
877]
878[trivial: tiny update to in-line comment
879zooko@zooko.com**20100614045715
880 Ignore-this: 10851b0ed2abfed542c97749e5d280bc
881 (I'm actually committing this patch as a test of the new eager-annotation-computation of trac-darcs.)
882]
883[docs: about.html link to home page early on, and be decentralized storage instead of cloud storage this time around
884zooko@zooko.com**20100619065318
885 Ignore-this: dc6db03f696e5b6d2848699e754d8053
886]
887[docs: update about.html, especially to have a non-broken link to quickstart.html, and also to comment out the broken links to "for Paranoids" and "for Corporates"
888zooko@zooko.com**20100619065124
889 Ignore-this: e292c7f51c337a84ebfeb366fbd24d6c
890]
891[TAG allmydata-tahoe-1.7.0
892zooko@zooko.com**20100619052631
893 Ignore-this: d21e27afe6d85e2e3ba6a3292ba2be1
894]
895Patch bundle hash:
8967895cf593c9c13c42fabb11ffce5c26217e8d6b9