1 # -*- coding: utf-8 -*-
4 from twisted.trial import unittest
5 from twisted.internet import defer
6 from allmydata import uri
7 from allmydata.util.consumer import download_to_data
8 from allmydata.immutable import upload
9 from allmydata.mutable.common import UnrecoverableFileError
10 from allmydata.storage.common import storage_index_to_dir
11 from allmydata.test.no_network import GridTestMixin
12 from allmydata.test.common import ShouldFailMixin, _corrupt_share_data
13 from allmydata.interfaces import NotEnoughSharesError
15 immutable_plaintext = "data" * 10000
16 mutable_plaintext = "muta" * 10000
18 class HungServerDownloadTest(GridTestMixin, ShouldFailMixin, unittest.TestCase):
19 # Many of these tests take around 60 seconds on François's ARM buildslave:
20 # http://tahoe-lafs.org/buildbot/builders/FranXois%20lenny-armv5tel
21 # allmydata.test.test_hung_server.HungServerDownloadTest.test_2_good_8_broken_duplicate_share_fail once ERRORed after 197 seconds on Midnight Magic's NetBSD buildslave:
22 # http://tahoe-lafs.org/buildbot/builders/MM%20netbsd4%20i386%20warp
23 # MM's buildslave varies a lot in how long it takes to run tests.
28 def _break(self, servers):
29 for (id, ss) in servers:
30 self.g.break_server(id)
32 def _hang(self, servers, **kwargs):
33 for (id, ss) in servers:
34 self.g.hang_server(id, **kwargs)
36 def _unhang(self, servers, **kwargs):
37 for (id, ss) in servers:
38 self.g.unhang_server(id, **kwargs)
40 def _delete_all_shares_from(self, servers):
41 serverids = [id for (id, ss) in servers]
42 for (i_shnum, i_serverid, i_sharefile) in self.shares:
43 if i_serverid in serverids:
44 os.unlink(i_sharefile)
46 def _corrupt_all_shares_in(self, servers, corruptor_func):
47 serverids = [id for (id, ss) in servers]
48 for (i_shnum, i_serverid, i_sharefile) in self.shares:
49 if i_serverid in serverids:
50 self._corrupt_share((i_shnum, i_sharefile), corruptor_func)
52 def _copy_all_shares_from(self, from_servers, to_server):
53 serverids = [id for (id, ss) in from_servers]
54 for (i_shnum, i_serverid, i_sharefile) in self.shares:
55 if i_serverid in serverids:
56 self._copy_share((i_shnum, i_sharefile), to_server)
58 def _copy_share(self, share, to_server):
59 (sharenum, sharefile) = share
61 shares_dir = os.path.join(ss.original.storedir, "shares")
62 si = uri.from_string(self.uri).get_storage_index()
63 si_dir = os.path.join(shares_dir, storage_index_to_dir(si))
64 if not os.path.exists(si_dir):
66 new_sharefile = os.path.join(si_dir, str(sharenum))
67 shutil.copy(sharefile, new_sharefile)
68 self.shares = self.find_uri_shares(self.uri)
69 # Make sure that the storage server has the share.
70 self.failUnless((sharenum, ss.original.my_nodeid, new_sharefile)
73 def _corrupt_share(self, share, corruptor_func):
74 (sharenum, sharefile) = share
75 data = open(sharefile, "rb").read()
76 newdata = corruptor_func(data)
78 wf = open(sharefile, "wb")
82 def _set_up(self, mutable, testdir, num_clients=1, num_servers=10):
83 self.mutable = mutable
85 self.basedir = "hung_server/mutable_" + testdir
87 self.basedir = "hung_server/immutable_" + testdir
89 self.set_up_grid(num_clients=num_clients, num_servers=num_servers)
91 self.c0 = self.g.clients[0]
92 nm = self.c0.nodemaker
93 self.servers = [(id, ss) for (id, ss) in nm.storage_broker.get_all_servers()]
96 d = nm.create_mutable_file(mutable_plaintext)
97 def _uploaded_mutable(node):
98 self.uri = node.get_uri()
99 self.shares = self.find_uri_shares(self.uri)
100 d.addCallback(_uploaded_mutable)
102 data = upload.Data(immutable_plaintext, convergence="")
103 d = self.c0.upload(data)
104 def _uploaded_immutable(upload_res):
105 self.uri = upload_res.uri
106 self.shares = self.find_uri_shares(self.uri)
107 d.addCallback(_uploaded_immutable)
110 def _start_download(self):
111 n = self.c0.create_node_from_uri(self.uri)
113 d = n.download_best_version()
114 stage_4_d = None # currently we aren't doing any tests which require this for mutable files
116 d = download_to_data(n)
117 #stage_4_d = n._downloader._all_downloads.keys()[0]._stage_4_d # too ugly! FIXME
119 return (d, stage_4_d,)
121 def _wait_for_data(self, n):
123 d = n.download_best_version()
125 d = download_to_data(n)
128 def _check(self, resultingdata):
130 self.failUnlessEqual(resultingdata, mutable_plaintext)
132 self.failUnlessEqual(resultingdata, immutable_plaintext)
134 def _download_and_check(self):
135 d, stage4d = self._start_download()
136 d.addCallback(self._check)
139 def _should_fail_download(self):
141 return self.shouldFail(UnrecoverableFileError, self.basedir,
142 "no recoverable versions",
143 self._download_and_check)
145 return self.shouldFail(NotEnoughSharesError, self.basedir,
147 self._download_and_check)
150 def test_10_good_sanity_check(self):
151 d = defer.succeed(None)
152 for mutable in [False, True]:
153 d.addCallback(lambda ign: self._set_up(mutable, "test_10_good_sanity_check"))
154 d.addCallback(lambda ign: self._download_and_check())
157 def test_10_good_copied_share(self):
158 d = defer.succeed(None)
159 for mutable in [False, True]:
160 d.addCallback(lambda ign: self._set_up(mutable, "test_10_good_copied_share"))
161 d.addCallback(lambda ign: self._copy_all_shares_from(self.servers[2:3], self.servers[0]))
162 d.addCallback(lambda ign: self._download_and_check())
165 def test_3_good_7_noshares(self):
166 d = defer.succeed(None)
167 for mutable in [False, True]:
168 d.addCallback(lambda ign: self._set_up(mutable, "test_3_good_7_noshares"))
169 d.addCallback(lambda ign: self._delete_all_shares_from(self.servers[3:]))
170 d.addCallback(lambda ign: self._download_and_check())
173 def test_2_good_8_broken_fail(self):
174 d = defer.succeed(None)
175 for mutable in [False, True]:
176 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_broken_fail"))
177 d.addCallback(lambda ign: self._break(self.servers[2:]))
178 d.addCallback(lambda ign: self._should_fail_download())
181 def test_2_good_8_noshares_fail(self):
182 d = defer.succeed(None)
183 for mutable in [False, True]:
184 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_noshares_fail"))
185 d.addCallback(lambda ign: self._delete_all_shares_from(self.servers[2:]))
186 d.addCallback(lambda ign: self._should_fail_download())
189 def test_2_good_8_broken_copied_share(self):
190 d = defer.succeed(None)
191 for mutable in [False, True]:
192 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_broken_copied_share"))
193 d.addCallback(lambda ign: self._copy_all_shares_from(self.servers[2:3], self.servers[0]))
194 d.addCallback(lambda ign: self._break(self.servers[2:]))
195 d.addCallback(lambda ign: self._download_and_check())
198 def test_2_good_8_broken_duplicate_share_fail(self):
199 d = defer.succeed(None)
200 for mutable in [False, True]:
201 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_broken_duplicate_share_fail"))
202 d.addCallback(lambda ign: self._copy_all_shares_from(self.servers[1:2], self.servers[0]))
203 d.addCallback(lambda ign: self._break(self.servers[2:]))
204 d.addCallback(lambda ign: self._should_fail_download())
207 # The tests below do not currently pass for mutable files.
209 def test_3_good_7_hung(self):
210 d = defer.succeed(None)
211 for mutable in [False]:
212 d.addCallback(lambda ign: self._set_up(mutable, "test_3_good_7_hung"))
213 d.addCallback(lambda ign: self._hang(self.servers[3:]))
214 d.addCallback(lambda ign: self._download_and_check())
217 def test_2_good_8_hung_then_1_recovers(self):
218 d = defer.succeed(None)
219 for mutable in [False]:
220 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_hung_then_1_recovers"))
221 d.addCallback(lambda ign: self._hang(self.servers[2:3]))
222 d.addCallback(lambda ign: self._hang(self.servers[3:]))
223 d.addCallback(lambda ign: self._unhang(self.servers[2:3]))
224 d.addCallback(lambda ign: self._download_and_check())
227 def test_2_good_8_hung_then_1_recovers_with_2_shares(self):
228 d = defer.succeed(None)
229 for mutable in [False]:
230 d.addCallback(lambda ign: self._set_up(mutable, "test_2_good_8_hung_then_1_recovers_with_2_shares"))
231 d.addCallback(lambda ign: self._copy_all_shares_from(self.servers[0:1], self.servers[2]))
232 d.addCallback(lambda ign: self._hang(self.servers[2:3]))
233 d.addCallback(lambda ign: self._hang(self.servers[3:]))
234 d.addCallback(lambda ign: self._unhang(self.servers[2:3]))
235 d.addCallback(lambda ign: self._download_and_check())
238 def test_failover_during_stage_4(self):
239 raise unittest.SkipTest("needs rewrite")
241 d = defer.succeed(None)
242 for mutable in [False]:
243 d.addCallback(lambda ign: self._set_up(mutable, "test_failover_during_stage_4"))
244 d.addCallback(lambda ign: self._corrupt_all_shares_in(self.servers[2:3], _corrupt_share_data))
245 d.addCallback(lambda ign: self._set_up(mutable, "test_failover_during_stage_4"))
246 d.addCallback(lambda ign: self._hang(self.servers[3:]))
247 d.addCallback(lambda ign: self._start_download())
248 def _after_starting_download((doned, started4d)):
249 started4d.addCallback(lambda ign: self._unhang(self.servers[3:4]))
250 doned.addCallback(self._check)
252 d.addCallback(_after_starting_download)