1 | 2 patches for repository davidsarah@dev.allmydata.org:/home/darcs/tahoe/trunk: |
---|
2 | |
---|
3 | Wed Jul 27 01:05:41 BST 2011 david-sarah@jacaranda.org |
---|
4 | * Drop upload frontend (updated), with more tests. Tests now pass on Windows. refs #1429 |
---|
5 | |
---|
6 | Wed Jul 27 03:30:03 BST 2011 david-sarah@jacaranda.org |
---|
7 | * drop-upload: make counts visible on the statistics page, and disable some debugging. refs #1429 |
---|
8 | |
---|
9 | New patches: |
---|
10 | |
---|
11 | [Drop upload frontend (updated), with more tests. Tests now pass on Windows. refs #1429 |
---|
12 | david-sarah@jacaranda.org**20110727000541 |
---|
13 | Ignore-this: d67c37a4db86c3d37a1c4b16ff299df5 |
---|
14 | ] { |
---|
15 | hunk ./src/allmydata/_auto_deps.py 22 |
---|
16 | "zope.interface == 3.3.1, == 3.5.3, == 3.6.1", |
---|
17 | |
---|
18 | # On Windows we need at least Twisted 9.0 to avoid an indirect dependency on pywin32. |
---|
19 | + # On Linux we need at least Twisted 10.1.0 for inotify support used by the drop-upload |
---|
20 | + # frontend. |
---|
21 | # We also need Twisted 10.1 for the FTP frontend in order for Twisted's FTP server to |
---|
22 | # support asynchronous close. |
---|
23 | "Twisted >= 10.1.0", |
---|
24 | hunk ./src/allmydata/client.py 153 |
---|
25 | # ControlServer and Helper are attached after Tub startup |
---|
26 | self.init_ftp_server() |
---|
27 | self.init_sftp_server() |
---|
28 | + self.init_drop_uploader() |
---|
29 | |
---|
30 | hotline_file = os.path.join(self.basedir, |
---|
31 | self.SUICIDE_PREVENTION_HOTLINE_FILE) |
---|
32 | hunk ./src/allmydata/client.py 441 |
---|
33 | sftp_portstr, pubkey_file, privkey_file) |
---|
34 | s.setServiceParent(self) |
---|
35 | |
---|
36 | + def init_drop_uploader(self): |
---|
37 | + if self.get_config("drop_upload", "enabled", False, boolean=True): |
---|
38 | + upload_uri = self.get_config("drop_upload", "upload.uri", None) |
---|
39 | + local_dir_utf8 = self.get_config("drop_upload", "local.directory", None) |
---|
40 | + |
---|
41 | + if upload_uri and local_dir_utf8: |
---|
42 | + try: |
---|
43 | + from allmydata.frontends import drop_upload |
---|
44 | + s = drop_upload.DropUploader(self, upload_uri, local_dir_utf8) |
---|
45 | + s.setServiceParent(self) |
---|
46 | + except Exception, e: |
---|
47 | + self.log("couldn't start drop-uploader: %r", args=(e,)) |
---|
48 | + else: |
---|
49 | + self.log("couldn't start drop-uploader: upload.uri or local.directory not specified") |
---|
50 | + |
---|
51 | def _check_hotline(self, hotline_file): |
---|
52 | if os.path.exists(hotline_file): |
---|
53 | mtime = os.stat(hotline_file)[stat.ST_MTIME] |
---|
54 | addfile ./src/allmydata/frontends/drop_upload.py |
---|
55 | hunk ./src/allmydata/frontends/drop_upload.py 1 |
---|
56 | + |
---|
57 | +import os, sys |
---|
58 | + |
---|
59 | +from twisted.internet import defer |
---|
60 | +from twisted.python.filepath import FilePath |
---|
61 | +from twisted.application import service |
---|
62 | + |
---|
63 | +from allmydata.interfaces import IDirectoryNode |
---|
64 | + |
---|
65 | +from allmydata.util.encodingutil import quote_output |
---|
66 | +from allmydata.immutable.upload import FileName |
---|
67 | + |
---|
68 | + |
---|
69 | +class DropUploader(service.MultiService): |
---|
70 | + def __init__(self, client, upload_uri, local_dir_utf8, inotify=None): |
---|
71 | + service.MultiService.__init__(self) |
---|
72 | + |
---|
73 | + try: |
---|
74 | + local_dir = os.path.expanduser(local_dir_utf8.decode('utf-8').encode(sys.getfilesystemencoding())) |
---|
75 | + except (UnicodeEncodeError, UnicodeDecodeError): |
---|
76 | + raise AssertionError("The drop-upload path %r was not valid UTF-8 or could not be represented in the filesystem encoding." |
---|
77 | + % quote_output(local_dir_utf8)) |
---|
78 | + |
---|
79 | + self._client = client |
---|
80 | + self._convergence = client.convergence |
---|
81 | + self._local_path = FilePath(local_dir) |
---|
82 | + self.uploaded = 0 |
---|
83 | + self.failed = 0 |
---|
84 | + self.disappeared = 0 |
---|
85 | + |
---|
86 | + if inotify is None: |
---|
87 | + from twisted.internet import inotify |
---|
88 | + self._inotify = inotify |
---|
89 | + |
---|
90 | + if not self._local_path.isdir(): |
---|
91 | + raise AssertionError("The drop-upload local path %r was not an existing directory." % quote_output(local_dir)) |
---|
92 | + |
---|
93 | + # TODO: allow a path rather than an URI. |
---|
94 | + self._parent = self._client.create_node_from_uri(upload_uri) |
---|
95 | + if not IDirectoryNode.providedBy(self._parent): |
---|
96 | + raise AssertionError("The drop-upload remote URI is not a directory URI.") |
---|
97 | + if self._parent.is_unknown() or self._parent.is_readonly(): |
---|
98 | + raise AssertionError("The drop-upload remote URI does not refer to a writeable directory.") |
---|
99 | + |
---|
100 | + self._uploaded_callback = lambda ign: None |
---|
101 | + |
---|
102 | + self._notifier = inotify.INotify() |
---|
103 | + self._notifier.startReading() |
---|
104 | + |
---|
105 | + # We don't watch for IN_CREATE, because that would cause us to read and upload a |
---|
106 | + # possibly-incomplete file before the application has closed it. There should always |
---|
107 | + # be an IN_CLOSE_WRITE after an IN_CREATE (I think). |
---|
108 | + # TODO: what about IN_MOVE_SELF? |
---|
109 | + mask = inotify.IN_CLOSE_WRITE | inotify.IN_MOVED_TO |
---|
110 | + self._notifier.watch(self._local_path, mask=mask, callbacks=[self._notify]) |
---|
111 | + |
---|
112 | + def _notify(self, opaque, path, events_mask): |
---|
113 | + self._log("inotify event %r, %r, %r\n" % (opaque, path, ', '.join(self._inotify.humanReadableMask(events_mask)))) |
---|
114 | + |
---|
115 | + d = defer.succeed(None) |
---|
116 | + |
---|
117 | + # FIXME: if this already exists as a mutable file, we replace the directory entry, |
---|
118 | + # but we should probably modify the file (as the SFTP frontend does). |
---|
119 | + def _add_file(ign): |
---|
120 | + name = path.basename().decode(sys.getfilesystemencoding()) |
---|
121 | + u = FileName(path.path, self._convergence) |
---|
122 | + return self._parent.add_file(name, u) |
---|
123 | + d.addCallback(_add_file) |
---|
124 | + |
---|
125 | + def _succeeded(ign): |
---|
126 | + self.uploaded += 1 |
---|
127 | + def _failed(f): |
---|
128 | + if path.exists(): |
---|
129 | + self._log("drop-upload: %r failed to upload due to %r" % (path.path, f)) |
---|
130 | + self.failed += 1 |
---|
131 | + return f |
---|
132 | + else: |
---|
133 | + self._log("drop-upload: notified file %r disappeared " |
---|
134 | + "(this is normal for temporary files): %r" % (path.path, f)) |
---|
135 | + self.disappeared += 1 |
---|
136 | + return None |
---|
137 | + d.addCallbacks(_succeeded, _failed) |
---|
138 | + d.addBoth(self._uploaded_callback) |
---|
139 | + return d |
---|
140 | + |
---|
141 | + def set_uploaded_callback(self, callback): |
---|
142 | + """This sets a function that will be called after a file has been uploaded.""" |
---|
143 | + self._uploaded_callback = callback |
---|
144 | + |
---|
145 | + def finish(self): |
---|
146 | + self._notifier.stopReading() |
---|
147 | + |
---|
148 | + def _log(self, msg): |
---|
149 | + self._client.log(msg) |
---|
150 | + open("events", "ab+").write(msg) |
---|
151 | hunk ./src/allmydata/scripts/create_node.py 155 |
---|
152 | c.write("enabled = false\n") |
---|
153 | c.write("\n") |
---|
154 | |
---|
155 | + c.write("[drop_upload]\n") |
---|
156 | + c.write("# Shall this node automatically upload files created or modified in a local directory?\n") |
---|
157 | + c.write("enabled = false\n") |
---|
158 | + c.write("# This must be an URI for a writeable directory.\n") |
---|
159 | + c.write("upload.uri =\n") |
---|
160 | + c.write("local.directory = ~/drop_upload\n") |
---|
161 | + c.write("\n") |
---|
162 | + |
---|
163 | c.close() |
---|
164 | |
---|
165 | from allmydata.util import fileutil |
---|
166 | addfile ./src/allmydata/test/fake_inotify.py |
---|
167 | hunk ./src/allmydata/test/fake_inotify.py 1 |
---|
168 | + |
---|
169 | +# Most of this is copied from Twisted 11.0. The reason for this hack is that |
---|
170 | +# twisted.internet.inotify can't be imported when the platform does not support inotify. |
---|
171 | + |
---|
172 | + |
---|
173 | +# from /usr/src/linux/include/linux/inotify.h |
---|
174 | + |
---|
175 | +IN_ACCESS = 0x00000001L # File was accessed |
---|
176 | +IN_MODIFY = 0x00000002L # File was modified |
---|
177 | +IN_ATTRIB = 0x00000004L # Metadata changed |
---|
178 | +IN_CLOSE_WRITE = 0x00000008L # Writeable file was closed |
---|
179 | +IN_CLOSE_NOWRITE = 0x00000010L # Unwriteable file closed |
---|
180 | +IN_OPEN = 0x00000020L # File was opened |
---|
181 | +IN_MOVED_FROM = 0x00000040L # File was moved from X |
---|
182 | +IN_MOVED_TO = 0x00000080L # File was moved to Y |
---|
183 | +IN_CREATE = 0x00000100L # Subfile was created |
---|
184 | +IN_DELETE = 0x00000200L # Subfile was delete |
---|
185 | +IN_DELETE_SELF = 0x00000400L # Self was deleted |
---|
186 | +IN_MOVE_SELF = 0x00000800L # Self was moved |
---|
187 | +IN_UNMOUNT = 0x00002000L # Backing fs was unmounted |
---|
188 | +IN_Q_OVERFLOW = 0x00004000L # Event queued overflowed |
---|
189 | +IN_IGNORED = 0x00008000L # File was ignored |
---|
190 | + |
---|
191 | +IN_ONLYDIR = 0x01000000 # only watch the path if it is a directory |
---|
192 | +IN_DONT_FOLLOW = 0x02000000 # don't follow a sym link |
---|
193 | +IN_MASK_ADD = 0x20000000 # add to the mask of an already existing watch |
---|
194 | +IN_ISDIR = 0x40000000 # event occurred against dir |
---|
195 | +IN_ONESHOT = 0x80000000 # only send event once |
---|
196 | + |
---|
197 | +IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE # closes |
---|
198 | +IN_MOVED = IN_MOVED_FROM | IN_MOVED_TO # moves |
---|
199 | +IN_CHANGED = IN_MODIFY | IN_ATTRIB # changes |
---|
200 | + |
---|
201 | +IN_WATCH_MASK = (IN_MODIFY | IN_ATTRIB | |
---|
202 | + IN_CREATE | IN_DELETE | |
---|
203 | + IN_DELETE_SELF | IN_MOVE_SELF | |
---|
204 | + IN_UNMOUNT | IN_MOVED_FROM | IN_MOVED_TO) |
---|
205 | + |
---|
206 | + |
---|
207 | +_FLAG_TO_HUMAN = [ |
---|
208 | + (IN_ACCESS, 'access'), |
---|
209 | + (IN_MODIFY, 'modify'), |
---|
210 | + (IN_ATTRIB, 'attrib'), |
---|
211 | + (IN_CLOSE_WRITE, 'close_write'), |
---|
212 | + (IN_CLOSE_NOWRITE, 'close_nowrite'), |
---|
213 | + (IN_OPEN, 'open'), |
---|
214 | + (IN_MOVED_FROM, 'moved_from'), |
---|
215 | + (IN_MOVED_TO, 'moved_to'), |
---|
216 | + (IN_CREATE, 'create'), |
---|
217 | + (IN_DELETE, 'delete'), |
---|
218 | + (IN_DELETE_SELF, 'delete_self'), |
---|
219 | + (IN_MOVE_SELF, 'move_self'), |
---|
220 | + (IN_UNMOUNT, 'unmount'), |
---|
221 | + (IN_Q_OVERFLOW, 'queue_overflow'), |
---|
222 | + (IN_IGNORED, 'ignored'), |
---|
223 | + (IN_ONLYDIR, 'only_dir'), |
---|
224 | + (IN_DONT_FOLLOW, 'dont_follow'), |
---|
225 | + (IN_MASK_ADD, 'mask_add'), |
---|
226 | + (IN_ISDIR, 'is_dir'), |
---|
227 | + (IN_ONESHOT, 'one_shot') |
---|
228 | +] |
---|
229 | + |
---|
230 | + |
---|
231 | + |
---|
232 | +def humanReadableMask(mask): |
---|
233 | + """ |
---|
234 | + Auxiliary function that converts an hexadecimal mask into a series |
---|
235 | + of human readable flags. |
---|
236 | + """ |
---|
237 | + s = [] |
---|
238 | + for k, v in _FLAG_TO_HUMAN: |
---|
239 | + if k & mask: |
---|
240 | + s.append(v) |
---|
241 | + return s |
---|
242 | + |
---|
243 | + |
---|
244 | +# This class is not copied from Twisted; it acts as a mock. |
---|
245 | +class INotify(object): |
---|
246 | + def startReading(self): |
---|
247 | + pass |
---|
248 | + |
---|
249 | + def stopReading(self): |
---|
250 | + pass |
---|
251 | + |
---|
252 | + def watch(self, filepath, mask=IN_WATCH_MASK, autoAdd=False, callbacks=None, recursive=False): |
---|
253 | + self.callbacks = callbacks |
---|
254 | + |
---|
255 | + def event(self, filepath, mask): |
---|
256 | + for cb in self.callbacks: |
---|
257 | + cb(None, filepath, mask) |
---|
258 | + |
---|
259 | + |
---|
260 | +__all__ = ["INotify", "humanReadableMask", "IN_WATCH_MASK", "IN_ACCESS", |
---|
261 | + "IN_MODIFY", "IN_ATTRIB", "IN_CLOSE_NOWRITE", "IN_CLOSE_WRITE", |
---|
262 | + "IN_OPEN", "IN_MOVED_FROM", "IN_MOVED_TO", "IN_CREATE", |
---|
263 | + "IN_DELETE", "IN_DELETE_SELF", "IN_MOVE_SELF", "IN_UNMOUNT", |
---|
264 | + "IN_Q_OVERFLOW", "IN_IGNORED", "IN_ONLYDIR", "IN_DONT_FOLLOW", |
---|
265 | + "IN_MASK_ADD", "IN_ISDIR", "IN_ONESHOT", "IN_CLOSE", |
---|
266 | + "IN_MOVED", "IN_CHANGED"] |
---|
267 | addfile ./src/allmydata/test/test_drop_upload.py |
---|
268 | hunk ./src/allmydata/test/test_drop_upload.py 1 |
---|
269 | + |
---|
270 | +import os, sys, platform |
---|
271 | + |
---|
272 | +from twisted.trial import unittest |
---|
273 | +from twisted.python import filepath, runtime |
---|
274 | +from twisted.internet import defer, base |
---|
275 | + |
---|
276 | +from allmydata.interfaces import IDirectoryNode, NoSuchChildError |
---|
277 | + |
---|
278 | +from allmydata.util import fileutil |
---|
279 | +from allmydata.util.consumer import download_to_data |
---|
280 | +from allmydata.test.no_network import GridTestMixin |
---|
281 | +from allmydata.test.common_util import ReallyEqualMixin |
---|
282 | +from allmydata.test.common import ShouldFailMixin |
---|
283 | +from allmydata.test import fake_inotify |
---|
284 | + |
---|
285 | +from allmydata.frontends.drop_upload import DropUploader |
---|
286 | + |
---|
287 | + |
---|
288 | +class DropUploadTestMixin(GridTestMixin, ShouldFailMixin, ReallyEqualMixin): |
---|
289 | + """ |
---|
290 | + These tests will be run both with a mock notifier, and (on platforms that support it) |
---|
291 | + with the real INotify. |
---|
292 | + """ |
---|
293 | + |
---|
294 | + def _test(self): |
---|
295 | + self.uploader = None |
---|
296 | + self.set_up_grid() |
---|
297 | + self.local_dir = os.path.join(self.basedir, "local_dir") |
---|
298 | + os.mkdir(self.local_dir) |
---|
299 | + |
---|
300 | + self.client = self.g.clients[0] |
---|
301 | + d = self.client.create_dirnode() |
---|
302 | + def _made_upload_dir(n): |
---|
303 | + self.failUnless(IDirectoryNode.providedBy(n)) |
---|
304 | + self.upload_dirnode = n |
---|
305 | + self.upload_uri = n.get_uri() |
---|
306 | + self.uploader = DropUploader(self.client, self.upload_uri, self.local_dir, inotify=self.inotify) |
---|
307 | + d.addCallback(_made_upload_dir) |
---|
308 | + |
---|
309 | + # Write something short enough for a LIT file. |
---|
310 | + d.addCallback(lambda ign: self._test_file("short", "test")) |
---|
311 | + |
---|
312 | + # Write to the same file again with different data. |
---|
313 | + d.addCallback(lambda ign: self._test_file("short", "different")) |
---|
314 | + |
---|
315 | + # Test that temporary files are not uploaded. |
---|
316 | + d.addCallback(lambda ign: self._test_file("tempfile", "test", temporary=True)) |
---|
317 | + |
---|
318 | + # Test that we tolerate creation of a subdirectory. |
---|
319 | + d.addCallback(lambda ign: os.mkdir(os.path.join(self.local_dir, "directory"))) |
---|
320 | + |
---|
321 | + # Write something longer, and also try to test a Unicode name if the fs can represent it. |
---|
322 | + try: |
---|
323 | + name = u"l\u00F8ng".encode(sys.getfilesystemencoding()) |
---|
324 | + except UnicodeEncodeError: |
---|
325 | + name = "long" |
---|
326 | + d.addCallback(lambda ign: self._test_file(name, "test"*100)) |
---|
327 | + |
---|
328 | + # TODO: test that causes an upload failure. |
---|
329 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.failed, 0)) |
---|
330 | + |
---|
331 | + # Prevent unclean reactor errors. |
---|
332 | + def _cleanup(res): |
---|
333 | + if self.uploader is not None: |
---|
334 | + self.uploader.finish() |
---|
335 | + return res |
---|
336 | + d.addBoth(_cleanup) |
---|
337 | + return d |
---|
338 | + |
---|
339 | + def _test_file(self, name, data, temporary=False): |
---|
340 | + previously_uploaded = self.uploader.uploaded |
---|
341 | + previously_disappeared = self.uploader.disappeared |
---|
342 | + |
---|
343 | + d = defer.Deferred() |
---|
344 | + |
---|
345 | + # Note: this relies on the fact that we only get one IN_CLOSE_WRITE notification per file |
---|
346 | + # (otherwise we would get a defer.AlreadyCalledError). Should we be relying on that? |
---|
347 | + self.uploader.set_uploaded_callback(d.callback) |
---|
348 | + |
---|
349 | + path = filepath.FilePath(os.path.join(self.local_dir, name)) |
---|
350 | + unicode_name = name.decode(sys.getfilesystemencoding()) |
---|
351 | + |
---|
352 | + f = open(path.path, "wb") |
---|
353 | + try: |
---|
354 | + if temporary and sys.platform != "win32": |
---|
355 | + os.unlink(path.path) |
---|
356 | + f.write(data) |
---|
357 | + finally: |
---|
358 | + f.close() |
---|
359 | + if temporary and sys.platform == "win32": |
---|
360 | + os.unlink(path.path) |
---|
361 | + self.notify_close_write(path) |
---|
362 | + |
---|
363 | + if temporary: |
---|
364 | + d.addCallback(lambda ign: self.shouldFail(NoSuchChildError, 'temp file not uploaded', None, |
---|
365 | + self.upload_dirnode.get, unicode_name)) |
---|
366 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.disappeared, previously_disappeared + 1)) |
---|
367 | + else: |
---|
368 | + d.addCallback(lambda ign: self.upload_dirnode.get(unicode_name)) |
---|
369 | + d.addCallback(download_to_data) |
---|
370 | + d.addCallback(lambda actual_data: self.failUnlessReallyEqual(actual_data, data)) |
---|
371 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.uploaded, previously_uploaded + 1)) |
---|
372 | + return d |
---|
373 | + |
---|
374 | + |
---|
375 | +class MockTest(DropUploadTestMixin, unittest.TestCase): |
---|
376 | + """This can run on any platform, and even if twisted.internet.inotify can't be imported.""" |
---|
377 | + |
---|
378 | + def test_errors(self): |
---|
379 | + self.basedir = "drop_upload.MockTest.test_errors" |
---|
380 | + self.set_up_grid() |
---|
381 | + errors_dir = os.path.join(self.basedir, "errors_dir") |
---|
382 | + os.mkdir(errors_dir) |
---|
383 | + |
---|
384 | + client = self.g.clients[0] |
---|
385 | + d = client.create_dirnode() |
---|
386 | + def _made_upload_dir(n): |
---|
387 | + self.failUnless(IDirectoryNode.providedBy(n)) |
---|
388 | + upload_uri = n.get_uri() |
---|
389 | + readonly_uri = n.get_readonly_uri() |
---|
390 | + |
---|
391 | + self.shouldFail(AssertionError, 'invalid local dir', 'could not be represented', |
---|
392 | + DropUploader, client, upload_uri, '\xFF', inotify=fake_inotify) |
---|
393 | + self.shouldFail(AssertionError, 'non-existant local dir', 'not an existing directory', |
---|
394 | + DropUploader, client, upload_uri, os.path.join(self.basedir, "Laputa"), inotify=fake_inotify) |
---|
395 | + |
---|
396 | + self.shouldFail(AssertionError, 'bad URI', 'not a directory URI', |
---|
397 | + DropUploader, client, 'bad', errors_dir, inotify=fake_inotify) |
---|
398 | + self.shouldFail(AssertionError, 'non-directory URI', 'not a directory URI', |
---|
399 | + DropUploader, client, 'URI:LIT:foo', errors_dir, inotify=fake_inotify) |
---|
400 | + self.shouldFail(AssertionError, 'readonly directory URI', 'does not refer to a writeable directory', |
---|
401 | + DropUploader, client, readonly_uri, errors_dir, inotify=fake_inotify) |
---|
402 | + d.addCallback(_made_upload_dir) |
---|
403 | + return d |
---|
404 | + |
---|
405 | + def test_drop_upload(self): |
---|
406 | + self.inotify = fake_inotify |
---|
407 | + self.basedir = "drop_upload.MockTest.test_drop_upload" |
---|
408 | + return self._test() |
---|
409 | + |
---|
410 | + def notify_close_write(self, path): |
---|
411 | + self.uploader._notifier.event(path, self.inotify.IN_CLOSE_WRITE) |
---|
412 | + |
---|
413 | + |
---|
414 | +class RealTest(DropUploadTestMixin, unittest.TestCase): |
---|
415 | + """This is skipped unless both Twisted and the platform support inotify.""" |
---|
416 | + |
---|
417 | + def test_drop_upload(self): |
---|
418 | + # We should always have runtime.platform.supportsINotify, because we're using |
---|
419 | + # Twisted >= 10.1. |
---|
420 | + if not runtime.platform.supportsINotify(): |
---|
421 | + raise unittest.SkipTest("Drop-upload support can only be tested for-real on an OS that supports inotify.") |
---|
422 | + |
---|
423 | + self.inotify = None # use the real twisted.internet.inotify |
---|
424 | + self.basedir = "drop_upload.RealTest.test_drop_upload" |
---|
425 | + return self._test() |
---|
426 | + |
---|
427 | + def notify_close_write(self, path): |
---|
428 | + # Writing to the file causes the notification. |
---|
429 | + pass |
---|
430 | hunk ./src/allmydata/test/test_runner.py 257 |
---|
431 | self.failUnless(re.search(r"\n\[storage\]\n#.*\nenabled = true\n", content), content) |
---|
432 | self.failUnless("\nreserved_space = 1G\n" in content) |
---|
433 | |
---|
434 | + self.failUnless(re.search(r"\n\[drop_upload\]\n#.*\nenabled = false\n", content), content) |
---|
435 | + |
---|
436 | # creating the node a second time should be rejected |
---|
437 | rc, out, err = self.run_tahoe(argv) |
---|
438 | self.failIfEqual(rc, 0, str((out, err, rc))) |
---|
439 | } |
---|
440 | [drop-upload: make counts visible on the statistics page, and disable some debugging. refs #1429 |
---|
441 | david-sarah@jacaranda.org**20110727023003 |
---|
442 | Ignore-this: 4e25022cca41d6012da067e96fadb1bf |
---|
443 | ] { |
---|
444 | hunk ./src/allmydata/frontends/drop_upload.py 7 |
---|
445 | from twisted.internet import defer |
---|
446 | from twisted.python.filepath import FilePath |
---|
447 | from twisted.application import service |
---|
448 | +from foolscap.api import eventually |
---|
449 | |
---|
450 | from allmydata.interfaces import IDirectoryNode |
---|
451 | |
---|
452 | hunk ./src/allmydata/frontends/drop_upload.py 26 |
---|
453 | % quote_output(local_dir_utf8)) |
---|
454 | |
---|
455 | self._client = client |
---|
456 | + self._stats_provider = client.stats_provider |
---|
457 | self._convergence = client.convergence |
---|
458 | self._local_path = FilePath(local_dir) |
---|
459 | hunk ./src/allmydata/frontends/drop_upload.py 29 |
---|
460 | - self.uploaded = 0 |
---|
461 | - self.failed = 0 |
---|
462 | - self.disappeared = 0 |
---|
463 | |
---|
464 | if inotify is None: |
---|
465 | from twisted.internet import inotify |
---|
466 | hunk ./src/allmydata/frontends/drop_upload.py 48 |
---|
467 | |
---|
468 | self._notifier = inotify.INotify() |
---|
469 | self._notifier.startReading() |
---|
470 | + self._stats_provider.count('drop_upload.dirs_monitored', 1) |
---|
471 | |
---|
472 | # We don't watch for IN_CREATE, because that would cause us to read and upload a |
---|
473 | # possibly-incomplete file before the application has closed it. There should always |
---|
474 | hunk ./src/allmydata/frontends/drop_upload.py 60 |
---|
475 | def _notify(self, opaque, path, events_mask): |
---|
476 | self._log("inotify event %r, %r, %r\n" % (opaque, path, ', '.join(self._inotify.humanReadableMask(events_mask)))) |
---|
477 | |
---|
478 | + self._stats_provider.count('drop_upload.files_queued', 1) |
---|
479 | + eventually(self._process, opaque, path, events_mask) |
---|
480 | + |
---|
481 | + def _process(self, opaque, path, events_mask): |
---|
482 | d = defer.succeed(None) |
---|
483 | |
---|
484 | # FIXME: if this already exists as a mutable file, we replace the directory entry, |
---|
485 | hunk ./src/allmydata/frontends/drop_upload.py 75 |
---|
486 | d.addCallback(_add_file) |
---|
487 | |
---|
488 | def _succeeded(ign): |
---|
489 | - self.uploaded += 1 |
---|
490 | + self._stats_provider.count('drop_upload.files_queued', -1) |
---|
491 | + self._stats_provider.count('drop_upload.files_uploaded', 1) |
---|
492 | def _failed(f): |
---|
493 | hunk ./src/allmydata/frontends/drop_upload.py 78 |
---|
494 | + self._stats_provider.count('drop_upload.files_queued', -1) |
---|
495 | if path.exists(): |
---|
496 | self._log("drop-upload: %r failed to upload due to %r" % (path.path, f)) |
---|
497 | hunk ./src/allmydata/frontends/drop_upload.py 81 |
---|
498 | - self.failed += 1 |
---|
499 | + self._stats_provider.count('drop_upload.files_failed', 1) |
---|
500 | return f |
---|
501 | else: |
---|
502 | self._log("drop-upload: notified file %r disappeared " |
---|
503 | hunk ./src/allmydata/frontends/drop_upload.py 86 |
---|
504 | "(this is normal for temporary files): %r" % (path.path, f)) |
---|
505 | - self.disappeared += 1 |
---|
506 | + self._stats_provider.count('drop_upload.files_disappeared', 1) |
---|
507 | return None |
---|
508 | d.addCallbacks(_succeeded, _failed) |
---|
509 | d.addBoth(self._uploaded_callback) |
---|
510 | hunk ./src/allmydata/frontends/drop_upload.py 98 |
---|
511 | |
---|
512 | def finish(self): |
---|
513 | self._notifier.stopReading() |
---|
514 | + self._stats_provider.count('drop_upload.dirs_monitored', -1) |
---|
515 | |
---|
516 | def _log(self, msg): |
---|
517 | self._client.log(msg) |
---|
518 | hunk ./src/allmydata/frontends/drop_upload.py 102 |
---|
519 | - open("events", "ab+").write(msg) |
---|
520 | + #open("events", "ab+").write(msg) |
---|
521 | hunk ./src/allmydata/test/test_drop_upload.py 26 |
---|
522 | with the real INotify. |
---|
523 | """ |
---|
524 | |
---|
525 | + def _get_count(self, name): |
---|
526 | + return self.stats_provider.get_stats()["counters"].get(name, 0) |
---|
527 | + |
---|
528 | def _test(self): |
---|
529 | self.uploader = None |
---|
530 | self.set_up_grid() |
---|
531 | hunk ./src/allmydata/test/test_drop_upload.py 36 |
---|
532 | os.mkdir(self.local_dir) |
---|
533 | |
---|
534 | self.client = self.g.clients[0] |
---|
535 | + self.stats_provider = self.client.stats_provider |
---|
536 | + |
---|
537 | d = self.client.create_dirnode() |
---|
538 | def _made_upload_dir(n): |
---|
539 | self.failUnless(IDirectoryNode.providedBy(n)) |
---|
540 | hunk ./src/allmydata/test/test_drop_upload.py 66 |
---|
541 | d.addCallback(lambda ign: self._test_file(name, "test"*100)) |
---|
542 | |
---|
543 | # TODO: test that causes an upload failure. |
---|
544 | - d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.failed, 0)) |
---|
545 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('drop_upload.files_failed'), 0)) |
---|
546 | |
---|
547 | # Prevent unclean reactor errors. |
---|
548 | def _cleanup(res): |
---|
549 | hunk ./src/allmydata/test/test_drop_upload.py 77 |
---|
550 | return d |
---|
551 | |
---|
552 | def _test_file(self, name, data, temporary=False): |
---|
553 | - previously_uploaded = self.uploader.uploaded |
---|
554 | - previously_disappeared = self.uploader.disappeared |
---|
555 | + previously_uploaded = self._get_count('drop_upload.files_uploaded') |
---|
556 | + previously_disappeared = self._get_count('drop_upload.files_disappeared') |
---|
557 | |
---|
558 | d = defer.Deferred() |
---|
559 | |
---|
560 | hunk ./src/allmydata/test/test_drop_upload.py 103 |
---|
561 | if temporary: |
---|
562 | d.addCallback(lambda ign: self.shouldFail(NoSuchChildError, 'temp file not uploaded', None, |
---|
563 | self.upload_dirnode.get, unicode_name)) |
---|
564 | - d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.disappeared, previously_disappeared + 1)) |
---|
565 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('drop_upload.files_disappeared'), |
---|
566 | + previously_disappeared + 1)) |
---|
567 | else: |
---|
568 | d.addCallback(lambda ign: self.upload_dirnode.get(unicode_name)) |
---|
569 | d.addCallback(download_to_data) |
---|
570 | hunk ./src/allmydata/test/test_drop_upload.py 109 |
---|
571 | d.addCallback(lambda actual_data: self.failUnlessReallyEqual(actual_data, data)) |
---|
572 | - d.addCallback(lambda ign: self.failUnlessReallyEqual(self.uploader.uploaded, previously_uploaded + 1)) |
---|
573 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('drop_upload.files_uploaded'), |
---|
574 | + previously_uploaded + 1)) |
---|
575 | + |
---|
576 | + d.addCallback(lambda ign: self.failUnlessReallyEqual(self._get_count('drop_upload.files_queued'), 0)) |
---|
577 | return d |
---|
578 | |
---|
579 | |
---|
580 | hunk ./src/allmydata/web/statistics.xhtml 12 |
---|
581 | |
---|
582 | <h1>Node Statistics</h1> |
---|
583 | |
---|
584 | +<h2>General</h2> |
---|
585 | + |
---|
586 | <ul> |
---|
587 | <li>Load Average: <span n:render="load_average" /></li> |
---|
588 | <li>Peak Load: <span n:render="peak_load" /></li> |
---|
589 | hunk ./src/allmydata/web/statistics.xhtml 23 |
---|
590 | <li>Files Retrieved (mutable): <span n:render="retrieves" /></li> |
---|
591 | </ul> |
---|
592 | |
---|
593 | +<h2>Drop-Uploader</h2> |
---|
594 | + |
---|
595 | +<ul> |
---|
596 | + <li>Local Directories Monitored: <span n:render="drop_monitored" /></li> |
---|
597 | + <li>Files Uploaded: <span n:render="drop_uploads" /></li> |
---|
598 | + <li>File Changes Queued: <span n:render="drop_queued" /></li> |
---|
599 | + <li>Failed Uploads: <span n:render="drop_failed" /></li> |
---|
600 | +</ul> |
---|
601 | + |
---|
602 | <h2>Raw Stats:</h2> |
---|
603 | <pre n:render="raw" /> |
---|
604 | |
---|
605 | hunk ./src/allmydata/web/status.py 1290 |
---|
606 | return "%s files / %s bytes (%s)" % (files, bytes, |
---|
607 | abbreviate_size(bytes)) |
---|
608 | |
---|
609 | + def render_drop_monitored(self, ctx, data): |
---|
610 | + dirs = data["counters"].get("drop_upload.dirs_monitored", 0) |
---|
611 | + return "%s directories" % (dirs,) |
---|
612 | + |
---|
613 | + def render_drop_uploads(self, ctx, data): |
---|
614 | + # TODO: bytes uploaded |
---|
615 | + files = data["counters"].get("drop_upload.files_uploaded", 0) |
---|
616 | + return "%s files" % (files,) |
---|
617 | + |
---|
618 | + def render_drop_queued(self, ctx, data): |
---|
619 | + files = data["counters"].get("drop_upload.files_queued", 0) |
---|
620 | + return "%s files" % (files,) |
---|
621 | + |
---|
622 | + def render_drop_failed(self, ctx, data): |
---|
623 | + files = data["counters"].get("drop_upload.files_failed", 0) |
---|
624 | + return "%s files" % (files,) |
---|
625 | + |
---|
626 | def render_raw(self, ctx, data): |
---|
627 | raw = pprint.pformat(data) |
---|
628 | return ctx.tag[raw] |
---|
629 | } |
---|
630 | |
---|
631 | Context: |
---|
632 | |
---|
633 | [docs/man/tahoe.1: add man page. fixes #1420 |
---|
634 | david-sarah@jacaranda.org**20110724171728 |
---|
635 | Ignore-this: fc7601ec7f25494288d6141d0ae0004c |
---|
636 | ] |
---|
637 | [misc/build_helpers/run_trial.py: undo change to block pywin32 (it didn't work because run_trial.py is no longer used). refs #1334 |
---|
638 | david-sarah@jacaranda.org**20110722035402 |
---|
639 | Ignore-this: 5d03f544c4154f088e26c7107494bf39 |
---|
640 | ] |
---|
641 | [misc/build_helpers/run_trial.py: ensure that pywin32 is not on the sys.path when running the test suite. Includes some temporary debugging printouts that will be removed. refs #1334 |
---|
642 | david-sarah@jacaranda.org**20110722024907 |
---|
643 | Ignore-this: 5141a9f83a4085ed4ca21f0bbb20bb9c |
---|
644 | ] |
---|
645 | [frontends/ftpd.py: remove the check for IWriteFile.close since we're now guaranteed to be using Twisted >= 10.1 which has it. |
---|
646 | david-sarah@jacaranda.org**20110722000320 |
---|
647 | Ignore-this: 55cd558b791526113db3f83c00ec328a |
---|
648 | ] |
---|
649 | [Update the dependency on zope.interface to fix an incompatiblity between Nevow and zope.interface 3.6.4. fixes #1435 |
---|
650 | david-sarah@jacaranda.org**20110721234941 |
---|
651 | Ignore-this: 2ff3fcfc030fca1a4d4c7f1fed0f2aa9 |
---|
652 | ] |
---|
653 | [Update the dependency on Twisted to >= 10.1. This allows us to simplify some documentation: it's no longer necessary to install pywin32 on Windows, or apply a patch to Twisted in order to use the FTP frontend. fixes #1274, #1438. refs #1429 |
---|
654 | david-sarah@jacaranda.org**20110721233658 |
---|
655 | Ignore-this: 81b41745477163c9b39c0b59db91cc62 |
---|
656 | ] |
---|
657 | [docs/running.rst: use 'tahoe run ~/.tahoe' instead of 'tahoe run' (the default is the current directory, unlike 'tahoe start'). |
---|
658 | david-sarah@jacaranda.org**20110718005949 |
---|
659 | Ignore-this: 81837fbce073e93d88a3e7ae3122458c |
---|
660 | ] |
---|
661 | [docs/running.rst: say to put the introducer.furl in tahoe.cfg. |
---|
662 | david-sarah@jacaranda.org**20110717194315 |
---|
663 | Ignore-this: 954cc4c08e413e8c62685d58ff3e11f3 |
---|
664 | ] |
---|
665 | [setup: remove the dependency on foolscap's "secure_connections" extra, add a dependency on pyOpenSSL |
---|
666 | zooko@zooko.com**20110717114226 |
---|
667 | Ignore-this: df222120d41447ce4102616921626c82 |
---|
668 | fixes #1383 |
---|
669 | ] |
---|
670 | [docs: add missing link in NEWS.rst |
---|
671 | zooko@zooko.com**20110712153307 |
---|
672 | Ignore-this: be7b7eb81c03700b739daa1027d72b35 |
---|
673 | ] |
---|
674 | [contrib: remove the contributed fuse modules and the entire contrib/ directory, which is now empty |
---|
675 | zooko@zooko.com**20110712153229 |
---|
676 | Ignore-this: 723c4f9e2211027c79d711715d972c5 |
---|
677 | Also remove a couple of vestigial references to figleaf, which is long gone. |
---|
678 | fixes #1409 (remove contrib/fuse) |
---|
679 | ] |
---|
680 | [README.txt: say that quickstart.rst is in the docs directory. |
---|
681 | david-sarah@jacaranda.org**20110717192400 |
---|
682 | Ignore-this: bc6d35a85c496b77dbef7570677ea42a |
---|
683 | ] |
---|
684 | [test_sftp.py cleanup: remove a redundant definition of failUnlessReallyEqual. |
---|
685 | david-sarah@jacaranda.org**20110716181813 |
---|
686 | Ignore-this: 50113380b368c573f07ac6fe2eb1e97f |
---|
687 | ] |
---|
688 | [add Protovis.js-based download-status timeline visualization |
---|
689 | Brian Warner <warner@lothar.com>**20110629222606 |
---|
690 | Ignore-this: 477ccef5c51b30e246f5b6e04ab4a127 |
---|
691 | |
---|
692 | provide status overlap info on the webapi t=json output, add decode/decrypt |
---|
693 | rate tooltips, add zoomin/zoomout buttons |
---|
694 | ] |
---|
695 | [add more download-status data, fix tests |
---|
696 | Brian Warner <warner@lothar.com>**20110629222555 |
---|
697 | Ignore-this: e9e0b7e0163f1e95858aa646b9b17b8c |
---|
698 | ] |
---|
699 | [prepare for viz: improve DownloadStatus events |
---|
700 | Brian Warner <warner@lothar.com>**20110629222542 |
---|
701 | Ignore-this: 16d0bde6b734bb501aa6f1174b2b57be |
---|
702 | |
---|
703 | consolidate IDownloadStatusHandlingConsumer stuff into DownloadNode |
---|
704 | ] |
---|
705 | [docs: fix error in crypto specification that was noticed by Taylor R Campbell <campbell+tahoe@mumble.net> |
---|
706 | zooko@zooko.com**20110629185711 |
---|
707 | Ignore-this: b921ed60c1c8ba3c390737fbcbe47a67 |
---|
708 | ] |
---|
709 | [setup.py: don't make bin/tahoe.pyscript executable. fixes #1347 |
---|
710 | david-sarah@jacaranda.org**20110130235809 |
---|
711 | Ignore-this: 3454c8b5d9c2c77ace03de3ef2d9398a |
---|
712 | ] |
---|
713 | [Makefile: remove targets relating to 'setup.py check_auto_deps' which no longer exists. fixes #1345 |
---|
714 | david-sarah@jacaranda.org**20110626054124 |
---|
715 | Ignore-this: abb864427a1b91bd10d5132b4589fd90 |
---|
716 | ] |
---|
717 | [Makefile: add 'make check' as an alias for 'make test'. Also remove an unnecessary dependency of 'test' on 'build' and 'src/allmydata/_version.py'. fixes #1344 |
---|
718 | david-sarah@jacaranda.org**20110623205528 |
---|
719 | Ignore-this: c63e23146c39195de52fb17c7c49b2da |
---|
720 | ] |
---|
721 | [Rename test_package_initialization.py to (much shorter) test_import.py . |
---|
722 | Brian Warner <warner@lothar.com>**20110611190234 |
---|
723 | Ignore-this: 3eb3dbac73600eeff5cfa6b65d65822 |
---|
724 | |
---|
725 | The former name was making my 'ls' listings hard to read, by forcing them |
---|
726 | down to just two columns. |
---|
727 | ] |
---|
728 | [tests: fix tests to accomodate [20110611153758-92b7f-0ba5e4726fb6318dac28fb762a6512a003f4c430] |
---|
729 | zooko@zooko.com**20110611163741 |
---|
730 | Ignore-this: 64073a5f39e7937e8e5e1314c1a302d1 |
---|
731 | Apparently none of the two authors (stercor, terrell), three reviewers (warner, davidsarah, terrell), or one committer (me) actually ran the tests. This is presumably due to #20. |
---|
732 | fixes #1412 |
---|
733 | ] |
---|
734 | [wui: right-align the size column in the WUI |
---|
735 | zooko@zooko.com**20110611153758 |
---|
736 | Ignore-this: 492bdaf4373c96f59f90581c7daf7cd7 |
---|
737 | Thanks to Ted "stercor" Rolle Jr. and Terrell Russell. |
---|
738 | fixes #1412 |
---|
739 | ] |
---|
740 | [docs: three minor fixes |
---|
741 | zooko@zooko.com**20110610121656 |
---|
742 | Ignore-this: fec96579eb95aceb2ad5fc01a814c8a2 |
---|
743 | CREDITS for arc for stats tweak |
---|
744 | fix link to .zip file in quickstart.rst (thanks to ChosenOne for noticing) |
---|
745 | English usage tweak |
---|
746 | ] |
---|
747 | [docs/running.rst: fix stray HTML (not .rst) link noticed by ChosenOne. |
---|
748 | david-sarah@jacaranda.org**20110609223719 |
---|
749 | Ignore-this: fc50ac9c94792dcac6f1067df8ac0d4a |
---|
750 | ] |
---|
751 | [server.py: get_latencies now reports percentiles _only_ if there are sufficient observations for the interpretation of the percentile to be unambiguous. |
---|
752 | wilcoxjg@gmail.com**20110527120135 |
---|
753 | Ignore-this: 2e7029764bffc60e26f471d7c2b6611e |
---|
754 | interfaces.py: modified the return type of RIStatsProvider.get_stats to allow for None as a return value |
---|
755 | NEWS.rst, stats.py: documentation of change to get_latencies |
---|
756 | stats.rst: now documents percentile modification in get_latencies |
---|
757 | test_storage.py: test_latencies now expects None in output categories that contain too few samples for the associated percentile to be unambiguously reported. |
---|
758 | fixes #1392 |
---|
759 | ] |
---|
760 | [docs: revert link in relnotes.txt from NEWS.rst to NEWS, since the former did not exist at revision 5000. |
---|
761 | david-sarah@jacaranda.org**20110517011214 |
---|
762 | Ignore-this: 6a5be6e70241e3ec0575641f64343df7 |
---|
763 | ] |
---|
764 | [docs: convert NEWS to NEWS.rst and change all references to it. |
---|
765 | david-sarah@jacaranda.org**20110517010255 |
---|
766 | Ignore-this: a820b93ea10577c77e9c8206dbfe770d |
---|
767 | ] |
---|
768 | [docs: remove out-of-date docs/testgrid/introducer.furl and containing directory. fixes #1404 |
---|
769 | david-sarah@jacaranda.org**20110512140559 |
---|
770 | Ignore-this: 784548fc5367fac5450df1c46890876d |
---|
771 | ] |
---|
772 | [scripts/common.py: don't assume that the default alias is always 'tahoe' (it is, but the API of get_alias doesn't say so). refs #1342 |
---|
773 | david-sarah@jacaranda.org**20110130164923 |
---|
774 | Ignore-this: a271e77ce81d84bb4c43645b891d92eb |
---|
775 | ] |
---|
776 | [setup: don't catch all Exception from check_requirement(), but only PackagingError and ImportError |
---|
777 | zooko@zooko.com**20110128142006 |
---|
778 | Ignore-this: 57d4bc9298b711e4bc9dc832c75295de |
---|
779 | I noticed this because I had accidentally inserted a bug which caused AssertionError to be raised from check_requirement(). |
---|
780 | ] |
---|
781 | [M-x whitespace-cleanup |
---|
782 | zooko@zooko.com**20110510193653 |
---|
783 | Ignore-this: dea02f831298c0f65ad096960e7df5c7 |
---|
784 | ] |
---|
785 | [docs: fix typo in running.rst, thanks to arch_o_median |
---|
786 | zooko@zooko.com**20110510193633 |
---|
787 | Ignore-this: ca06de166a46abbc61140513918e79e8 |
---|
788 | ] |
---|
789 | [relnotes.txt: don't claim to work on Cygwin (which has been untested for some time). refs #1342 |
---|
790 | david-sarah@jacaranda.org**20110204204902 |
---|
791 | Ignore-this: 85ef118a48453d93fa4cddc32d65b25b |
---|
792 | ] |
---|
793 | [relnotes.txt: forseeable -> foreseeable. refs #1342 |
---|
794 | david-sarah@jacaranda.org**20110204204116 |
---|
795 | Ignore-this: 746debc4d82f4031ebf75ab4031b3a9 |
---|
796 | ] |
---|
797 | [replace remaining .html docs with .rst docs |
---|
798 | zooko@zooko.com**20110510191650 |
---|
799 | Ignore-this: d557d960a986d4ac8216d1677d236399 |
---|
800 | Remove install.html (long since deprecated). |
---|
801 | Also replace some obsolete references to install.html with references to quickstart.rst. |
---|
802 | Fix some broken internal references within docs/historical/historical_known_issues.txt. |
---|
803 | Thanks to Ravi Pinjala and Patrick McDonald. |
---|
804 | refs #1227 |
---|
805 | ] |
---|
806 | [docs: FTP-and-SFTP.rst: fix a minor error and update the information about which version of Twisted fixes #1297 |
---|
807 | zooko@zooko.com**20110428055232 |
---|
808 | Ignore-this: b63cfb4ebdbe32fb3b5f885255db4d39 |
---|
809 | ] |
---|
810 | [munin tahoe_files plugin: fix incorrect file count |
---|
811 | francois@ctrlaltdel.ch**20110428055312 |
---|
812 | Ignore-this: 334ba49a0bbd93b4a7b06a25697aba34 |
---|
813 | fixes #1391 |
---|
814 | ] |
---|
815 | [corrected "k must never be smaller than N" to "k must never be greater than N" |
---|
816 | secorp@allmydata.org**20110425010308 |
---|
817 | Ignore-this: 233129505d6c70860087f22541805eac |
---|
818 | ] |
---|
819 | [Fix a test failure in test_package_initialization on Python 2.4.x due to exceptions being stringified differently than in later versions of Python. refs #1389 |
---|
820 | david-sarah@jacaranda.org**20110411190738 |
---|
821 | Ignore-this: 7847d26bc117c328c679f08a7baee519 |
---|
822 | ] |
---|
823 | [tests: add test for including the ImportError message and traceback entry in the summary of errors from importing dependencies. refs #1389 |
---|
824 | david-sarah@jacaranda.org**20110410155844 |
---|
825 | Ignore-this: fbecdbeb0d06a0f875fe8d4030aabafa |
---|
826 | ] |
---|
827 | [allmydata/__init__.py: preserve the message and last traceback entry (file, line number, function, and source line) of ImportErrors in the package versions string. fixes #1389 |
---|
828 | david-sarah@jacaranda.org**20110410155705 |
---|
829 | Ignore-this: 2f87b8b327906cf8bfca9440a0904900 |
---|
830 | ] |
---|
831 | [remove unused variable detected by pyflakes |
---|
832 | zooko@zooko.com**20110407172231 |
---|
833 | Ignore-this: 7344652d5e0720af822070d91f03daf9 |
---|
834 | ] |
---|
835 | [allmydata/__init__.py: Nicer reporting of unparseable version numbers in dependencies. fixes #1388 |
---|
836 | david-sarah@jacaranda.org**20110401202750 |
---|
837 | Ignore-this: 9c6bd599259d2405e1caadbb3e0d8c7f |
---|
838 | ] |
---|
839 | [update FTP-and-SFTP.rst: the necessary patch is included in Twisted-10.1 |
---|
840 | Brian Warner <warner@lothar.com>**20110325232511 |
---|
841 | Ignore-this: d5307faa6900f143193bfbe14e0f01a |
---|
842 | ] |
---|
843 | [control.py: remove all uses of s.get_serverid() |
---|
844 | warner@lothar.com**20110227011203 |
---|
845 | Ignore-this: f80a787953bd7fa3d40e828bde00e855 |
---|
846 | ] |
---|
847 | [web: remove some uses of s.get_serverid(), not all |
---|
848 | warner@lothar.com**20110227011159 |
---|
849 | Ignore-this: a9347d9cf6436537a47edc6efde9f8be |
---|
850 | ] |
---|
851 | [immutable/downloader/fetcher.py: remove all get_serverid() calls |
---|
852 | warner@lothar.com**20110227011156 |
---|
853 | Ignore-this: fb5ef018ade1749348b546ec24f7f09a |
---|
854 | ] |
---|
855 | [immutable/downloader/fetcher.py: fix diversity bug in server-response handling |
---|
856 | warner@lothar.com**20110227011153 |
---|
857 | Ignore-this: bcd62232c9159371ae8a16ff63d22c1b |
---|
858 | |
---|
859 | When blocks terminate (either COMPLETE or CORRUPT/DEAD/BADSEGNUM), the |
---|
860 | _shares_from_server dict was being popped incorrectly (using shnum as the |
---|
861 | index instead of serverid). I'm still thinking through the consequences of |
---|
862 | this bug. It was probably benign and really hard to detect. I think it would |
---|
863 | cause us to incorrectly believe that we're pulling too many shares from a |
---|
864 | server, and thus prefer a different server rather than asking for a second |
---|
865 | share from the first server. The diversity code is intended to spread out the |
---|
866 | number of shares simultaneously being requested from each server, but with |
---|
867 | this bug, it might be spreading out the total number of shares requested at |
---|
868 | all, not just simultaneously. (note that SegmentFetcher is scoped to a single |
---|
869 | segment, so the effect doesn't last very long). |
---|
870 | ] |
---|
871 | [immutable/downloader/share.py: reduce get_serverid(), one left, update ext deps |
---|
872 | warner@lothar.com**20110227011150 |
---|
873 | Ignore-this: d8d56dd8e7b280792b40105e13664554 |
---|
874 | |
---|
875 | test_download.py: create+check MyShare instances better, make sure they share |
---|
876 | Server objects, now that finder.py cares |
---|
877 | ] |
---|
878 | [immutable/downloader/finder.py: reduce use of get_serverid(), one left |
---|
879 | warner@lothar.com**20110227011146 |
---|
880 | Ignore-this: 5785be173b491ae8a78faf5142892020 |
---|
881 | ] |
---|
882 | [immutable/offloaded.py: reduce use of get_serverid() a bit more |
---|
883 | warner@lothar.com**20110227011142 |
---|
884 | Ignore-this: b48acc1b2ae1b311da7f3ba4ffba38f |
---|
885 | ] |
---|
886 | [immutable/upload.py: reduce use of get_serverid() |
---|
887 | warner@lothar.com**20110227011138 |
---|
888 | Ignore-this: ffdd7ff32bca890782119a6e9f1495f6 |
---|
889 | ] |
---|
890 | [immutable/checker.py: remove some uses of s.get_serverid(), not all |
---|
891 | warner@lothar.com**20110227011134 |
---|
892 | Ignore-this: e480a37efa9e94e8016d826c492f626e |
---|
893 | ] |
---|
894 | [add remaining get_* methods to storage_client.Server, NoNetworkServer, and |
---|
895 | warner@lothar.com**20110227011132 |
---|
896 | Ignore-this: 6078279ddf42b179996a4b53bee8c421 |
---|
897 | MockIServer stubs |
---|
898 | ] |
---|
899 | [upload.py: rearrange _make_trackers a bit, no behavior changes |
---|
900 | warner@lothar.com**20110227011128 |
---|
901 | Ignore-this: 296d4819e2af452b107177aef6ebb40f |
---|
902 | ] |
---|
903 | [happinessutil.py: finally rename merge_peers to merge_servers |
---|
904 | warner@lothar.com**20110227011124 |
---|
905 | Ignore-this: c8cd381fea1dd888899cb71e4f86de6e |
---|
906 | ] |
---|
907 | [test_upload.py: factor out FakeServerTracker |
---|
908 | warner@lothar.com**20110227011120 |
---|
909 | Ignore-this: 6c182cba90e908221099472cc159325b |
---|
910 | ] |
---|
911 | [test_upload.py: server-vs-tracker cleanup |
---|
912 | warner@lothar.com**20110227011115 |
---|
913 | Ignore-this: 2915133be1a3ba456e8603885437e03 |
---|
914 | ] |
---|
915 | [happinessutil.py: server-vs-tracker cleanup |
---|
916 | warner@lothar.com**20110227011111 |
---|
917 | Ignore-this: b856c84033562d7d718cae7cb01085a9 |
---|
918 | ] |
---|
919 | [upload.py: more tracker-vs-server cleanup |
---|
920 | warner@lothar.com**20110227011107 |
---|
921 | Ignore-this: bb75ed2afef55e47c085b35def2de315 |
---|
922 | ] |
---|
923 | [upload.py: fix var names to avoid confusion between 'trackers' and 'servers' |
---|
924 | warner@lothar.com**20110227011103 |
---|
925 | Ignore-this: 5d5e3415b7d2732d92f42413c25d205d |
---|
926 | ] |
---|
927 | [refactor: s/peer/server/ in immutable/upload, happinessutil.py, test_upload |
---|
928 | warner@lothar.com**20110227011100 |
---|
929 | Ignore-this: 7ea858755cbe5896ac212a925840fe68 |
---|
930 | |
---|
931 | No behavioral changes, just updating variable/method names and log messages. |
---|
932 | The effects outside these three files should be minimal: some exception |
---|
933 | messages changed (to say "server" instead of "peer"), and some internal class |
---|
934 | names were changed. A few things still use "peer" to minimize external |
---|
935 | changes, like UploadResults.timings["peer_selection"] and |
---|
936 | happinessutil.merge_peers, which can be changed later. |
---|
937 | ] |
---|
938 | [storage_client.py: clean up test_add_server/test_add_descriptor, remove .test_servers |
---|
939 | warner@lothar.com**20110227011056 |
---|
940 | Ignore-this: efad933e78179d3d5fdcd6d1ef2b19cc |
---|
941 | ] |
---|
942 | [test_client.py, upload.py:: remove KiB/MiB/etc constants, and other dead code |
---|
943 | warner@lothar.com**20110227011051 |
---|
944 | Ignore-this: dc83c5794c2afc4f81e592f689c0dc2d |
---|
945 | ] |
---|
946 | [test: increase timeout on a network test because Francois's ARM machine hit that timeout |
---|
947 | zooko@zooko.com**20110317165909 |
---|
948 | Ignore-this: 380c345cdcbd196268ca5b65664ac85b |
---|
949 | I'm skeptical that the test was proceeding correctly but ran out of time. It seems more likely that it had gotten hung. But if we raise the timeout to an even more extravagant number then we can be even more certain that the test was never going to finish. |
---|
950 | ] |
---|
951 | [docs/configuration.rst: add a "Frontend Configuration" section |
---|
952 | Brian Warner <warner@lothar.com>**20110222014323 |
---|
953 | Ignore-this: 657018aa501fe4f0efef9851628444ca |
---|
954 | |
---|
955 | this points to docs/frontends/*.rst, which were previously underlinked |
---|
956 | ] |
---|
957 | [web/filenode.py: avoid calling req.finish() on closed HTTP connections. Closes #1366 |
---|
958 | "Brian Warner <warner@lothar.com>"**20110221061544 |
---|
959 | Ignore-this: 799d4de19933f2309b3c0c19a63bb888 |
---|
960 | ] |
---|
961 | [Add unit tests for cross_check_pkg_resources_versus_import, and a regression test for ref #1355. This requires a little refactoring to make it testable. |
---|
962 | david-sarah@jacaranda.org**20110221015817 |
---|
963 | Ignore-this: 51d181698f8c20d3aca58b057e9c475a |
---|
964 | ] |
---|
965 | [allmydata/__init__.py: .name was used in place of the correct .__name__ when printing an exception. Also, robustify string formatting by using %r instead of %s in some places. fixes #1355. |
---|
966 | david-sarah@jacaranda.org**20110221020125 |
---|
967 | Ignore-this: b0744ed58f161bf188e037bad077fc48 |
---|
968 | ] |
---|
969 | [Refactor StorageFarmBroker handling of servers |
---|
970 | Brian Warner <warner@lothar.com>**20110221015804 |
---|
971 | Ignore-this: 842144ed92f5717699b8f580eab32a51 |
---|
972 | |
---|
973 | Pass around IServer instance instead of (peerid, rref) tuple. Replace |
---|
974 | "descriptor" with "server". Other replacements: |
---|
975 | |
---|
976 | get_all_servers -> get_connected_servers/get_known_servers |
---|
977 | get_servers_for_index -> get_servers_for_psi (now returns IServers) |
---|
978 | |
---|
979 | This change still needs to be pushed further down: lots of code is now |
---|
980 | getting the IServer and then distributing (peerid, rref) internally. |
---|
981 | Instead, it ought to distribute the IServer internally and delay |
---|
982 | extracting a serverid or rref until the last moment. |
---|
983 | |
---|
984 | no_network.py was updated to retain parallelism. |
---|
985 | ] |
---|
986 | [TAG allmydata-tahoe-1.8.2 |
---|
987 | warner@lothar.com**20110131020101] |
---|
988 | Patch bundle hash: |
---|
989 | 18798aff91799b04023f2d3784ab3970d1618249 |
---|