Ticket #637: ticket637-tested.darcspatch.txt

File ticket637-tested.darcspatch.txt, 13.0 KB (added by davidsarah, at 2009-11-06T07:27:45Z)

Tested fix for #637 and #814

Line 
1diff -rN old-tahoe/_auto_deps.py new-tahoe/_auto_deps.py
240,41c40,42
3< ## can be easy_install'ed and also which actually makes "import win32api" succeed.  Users have
4< ## to manually install pywin32 on Windows before installing Tahoe.
5---
6> ## can be easy_install'ed and also which actually makes "import win32api" succeed.
7> ## See http://sourceforge.net/tracker/index.php?func=detail&aid=1799934&group_id=78018&atid=551954
8> ## Users have to manually install pywin32 on Windows before installing Tahoe.
947,52c48,51
10< ##    # management.  Then the specification and the evolution of Twisted's reliance on pywin32 can
11< ##    # be confined to the Twisted setup data, and Tahoe can remain blissfully ignorant about such
12< ##    # things as if a future version of Twisted requires a different version of pywin32, or if a
13< ##    # future version of Twisted implements process management without using pywin32 at all,
14< ##    # etc..  That is twisted ticket #3238 -- http://twistedmatrix.com/trac/ticket/3238 .  But
15< ##    # until Twisted does that, Tahoe needs to be non-ignorant of the following requirement:
16---
17> ##    # management.  That is twisted ticket #3238 -- http://twistedmatrix.com/trac/ticket/3238 .
18> ##    # On the other hand, Tahoe also depends on pywin32 for getting free disk space statistics
19> ##    # (although that is not a hard requirement: if win32api can't be imported then we don't
20> ##    # rely on having the disk stats).
21diff -rN old-tahoe/docs/configuration.txt new-tahoe/docs/configuration.txt
22305,306c305,308
23<  server will not accept any share which causes the amount of free space (as
24<  measured by 'df', or more specifically statvfs(2)) to drop below this value.
25---
26>  server will not accept any share which causes the amount of free disk space
27>  to drop below this value. (The free space is measured by a call to statvfs(2)
28>  on Unix, or GetDiskFreeSpaceEx on Windows, and is the space available to the
29>  user account under which the storage server runs.)
30diff -rN old-tahoe/src/allmydata/storage/server.py new-tahoe/src/allmydata/storage/server.py
3138a39,47
32>     windows = False
33>
34>     try:
35>         import win32api, win32con
36>         windows = True
37>         # <http://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx>
38>         win32api.SetErrorMode(win32con.SEM_FAILCRITICALERRORS | win32con.SEM_NOOPENFILEERRORBOX)
39>     except ImportError:
40>         pass
4173c82
42<                 log.msg("warning: [storage]reserved_space= is set, but this platform does not support statvfs(2), so this reservation cannot be honored",
43---
44>                 log.msg("warning: [storage]reserved_space= is set, but this platform does not support an API to get disk statistics (statvfs(2) or GetDiskFreeSpaceEx), so this reservation cannot be honored",
45150,151c159,183
46<     def do_statvfs(self):
47<         return os.statvfs(self.storedir)
48---
49>     def get_disk_stats(self):
50>         """Return disk statistics for the storage disk, in the form of a dict with the following fields.
51>           total:            total bytes on disk
52>           free_for_root:    bytes actually free on disk
53>           free_for_nonroot: bytes free for "a non-privileged user" [Unix] or the current user [Windows];
54>                               might take into account quotas depending on platform
55>           used:             bytes used on disk
56>           avail:            bytes available excluding reserved space
57>         An AttributeError can occur if the OS has no API to get disk information.
58>         An EnvironmentError can occur if the OS call fails."""
59>
60>         if self.windows:
61>             # For Windows systems, where os.statvfs is not available, use GetDiskFreeSpaceEx.
62>             # <http://docs.activestate.com/activepython/2.5/pywin32/win32api__GetDiskFreeSpaceEx_meth.html>
63>             #
64>             # Although the docs say that the argument should be the root directory of a disk, GetDiskFreeSpaceEx
65>             # actually accepts any path on that disk (like its Win32 equivalent).
66>
67>             (free_for_nonroot, total, free_for_root) = self.win32api.GetDiskFreeSpaceEx(self.storedir)
68>         else:
69>             # For Unix-like systems.
70>             # <http://docs.python.org/library/os.html#os.statvfs>
71>             # <http://opengroup.org/onlinepubs/7990989799/xsh/fstatvfs.html>
72>             # <http://opengroup.org/onlinepubs/7990989799/xsh/sysstatvfs.h.html>
73>             s = os.statvfs(self.storedir)
74153,165d184
75<     def get_stats(self):
76<         # remember: RIStatsProvider requires that our return dict
77<         # contains numeric values.
78<         stats = { 'storage_server.allocated': self.allocated_size(), }
79<         stats["storage_server.reserved_space"] = self.reserved_space
80<         for category,ld in self.get_latencies().items():
81<             for name,v in ld.items():
82<                 stats['storage_server.latencies.%s.%s' % (category, name)] = v
83<         writeable = True
84<         if self.readonly_storage:
85<             writeable = False
86<         try:
87<             s = self.do_statvfs()
88176,190c195,217
89<             disk_total = s.f_frsize * s.f_blocks
90<             disk_used = s.f_frsize * (s.f_blocks - s.f_bfree)
91<             # spacetime predictors should look at the slope of disk_used.
92<             disk_free_for_root = s.f_frsize * s.f_bfree
93<             disk_free_for_nonroot = s.f_frsize * s.f_bavail
94<
95<             # include our local policy here: if we stop accepting shares when
96<             # the available space drops below 1GB, then include that fact in
97<             # disk_avail.
98<             disk_avail = disk_free_for_nonroot - self.reserved_space
99<             disk_avail = max(disk_avail, 0)
100<             if self.readonly_storage:
101<                 disk_avail = 0
102<             if disk_avail == 0:
103<                 writeable = False
104---
105>             total = s.f_frsize * s.f_blocks
106>             free_for_root = s.f_frsize * s.f_bfree
107>             free_for_nonroot = s.f_frsize * s.f_bavail
108>
109>         # valid for all platforms:
110>         used = total - free_for_root
111>         avail = max(free_for_nonroot - self.reserved_space, 0)
112>
113>         return { 'total': total, 'free_for_root': free_for_root, 'free_for_nonroot': free_for_nonroot,
114>                  'used': used, 'avail': avail, }
115>
116>     def get_stats(self):
117>         # remember: RIStatsProvider requires that our return dict
118>         # contains numeric values.
119>         stats = { 'storage_server.allocated': self.allocated_size(), }
120>         stats['storage_server.reserved_space'] = self.reserved_space
121>         for category,ld in self.get_latencies().items():
122>             for name,v in ld.items():
123>                 stats['storage_server.latencies.%s.%s' % (category, name)] = v
124>
125>         try:
126>             disk = self.get_disk_stats()
127>             writeable = disk['avail'] > 0
128193,197c220,224
129<             stats["storage_server.disk_total"] = disk_total
130<             stats["storage_server.disk_used"] = disk_used
131<             stats["storage_server.disk_free_for_root"] = disk_free_for_root
132<             stats["storage_server.disk_free_for_nonroot"] = disk_free_for_nonroot
133<             stats["storage_server.disk_avail"] = disk_avail
134---
135>             stats['storage_server.disk_total'] = disk['total']
136>             stats['storage_server.disk_used'] = disk['used']
137>             stats['storage_server.disk_free_for_root'] = disk['free_for_root']
138>             stats['storage_server.disk_free_for_nonroot'] = disk['free_for_nonroot']
139>             stats['storage_server.disk_avail'] = disk['avail']
140199,201c226,235
141<             # os.statvfs is available only on unix
142<             pass
143<         stats["storage_server.accepting_immutable_shares"] = int(writeable)
144---
145>             writeable = True
146>         except EnvironmentError:
147>             log.msg("OS call to get disk statistics failed", level=log.UNUSUAL)
148>             writeable = False
149>
150>         if self.readonly_storage:
151>             stats['storage_server.disk_avail'] = 0
152>             writeable = False
153>
154>         stats['storage_server.accepting_immutable_shares'] = int(writeable)
155205c239
156<             stats["storage_server.total_bucket_count"] = bucket_count
157---
158>             stats['storage_server.total_bucket_count'] = bucket_count
159208,214d241
160<
161<     def stat_disk(self, d):
162<         s = os.statvfs(d)
163<         # s.f_bavail: available to non-root users
164<         disk_avail = s.f_frsize * s.f_bavail
165<         return disk_avail
166<
167216c243,246
168<         # returns None if it cannot be measured (windows)
169---
170>         """Returns available space for share storage in bytes, or None if no API to get this
171>            information is available."""
172>         if self.readonly_storage:
173>             return 0
174218,219c248
175<             disk_avail = self.stat_disk(self.storedir)
176<             disk_avail -= self.reserved_space
177---
178>             return self.get_disk_stats()['avail']
179221,224c250,253
180<             disk_avail = None
181<         if self.readonly_storage:
182<             disk_avail = 0
183<         return disk_avail
184---
185>             return None
186>         except EnvironmentError:
187>             log.msg("OS call to get disk statistics failed", level=log.UNUSUAL)
188>             return 0
189235,236c264
190<             # we're on a platform that doesn't have 'df', so make a vague
191<             # guess.
192---
193>             # We're on a platform that has no API to get disk stats.
194237a266
195>
196291c320
197<         # self.readonly_storage causes remaining_space=0
198---
199>         # self.readonly_storage causes remaining_space <= 0
200diff -rN old-tahoe/src/allmydata/test/test_storage.py new-tahoe/src/allmydata/test/test_storage.py
201231,232c231,233
202<     def stat_disk(self, d):
203<         return self.DISKAVAIL
204---
205>     DISKAVAIL = 0
206>     def get_disk_stats(self):
207>         return { 'free_for_nonroot': self.DISKAVAIL, 'avail': max(self.DISKAVAIL - self.reserved_space, 0), }
208415c416
209<         # the FakeDiskStorageServer doesn't do real statvfs() calls
210---
211>         # the FakeDiskStorageServer doesn't do real calls to get_disk_stats
212470a472,488
213>     def test_disk_stats(self):
214>         # This will spuriously fail if there is zero disk space left (but so will other tests).
215>         ss = self.create("test_disk_stats", reserved_space=0)
216>
217>         disk = ss.get_disk_stats()
218>         self.failUnless(disk['total'] > 0, disk['total'])
219>         self.failUnless(disk['used'] > 0, disk['used'])
220>         self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
221>         self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
222>         self.failUnless(disk['avail'] > 0, disk['avail'])
223>
224>     def test_disk_stats_avail_nonnegative(self):
225>         ss = self.create("test_disk_stats_avail_nonnegative", reserved_space=2**64)
226>
227>         disk = ss.get_disk_stats()
228>         self.failUnlessEqual(disk['avail'], 0)
229>
230627,628c645
231<         self.failUnlessEqual(stats["storage_server.accepting_immutable_shares"],
232<                              False)
233---
234>         self.failUnlessEqual(stats["storage_server.accepting_immutable_shares"], 0)
235630,632c647,648
236<             # windows does not have os.statvfs, so it doesn't give us disk
237<             # stats. But if there are stats, readonly_storage means
238<             # disk_avail=0
239---
240>             # Some platforms may not have an API to get disk stats.
241>             # But if there are stats, readonly_storage means disk_avail=0
2422408,2409c2424,2425
243< class NoStatvfsServer(StorageServer):
244<     def do_statvfs(self):
245---
246> class NoDiskStatsServer(StorageServer):
247>     def get_disk_stats(self):
2482411a2428,2431
249> class BadDiskStatsServer(StorageServer):
250>     def get_disk_stats(self):
251>         raise OSError
252>
2532453,2456c2473,2476
254<     def test_status_no_statvfs(self):
255<         # windows has no os.statvfs . Make sure the code handles that even on
256<         # unix.
257<         basedir = "storage/WebStatus/status_no_statvfs"
258---
259>     def test_status_no_disk_stats(self):
260>         # Some platforms may have no disk stats API. Make sure the code can handle that
261>         # (test runs on all platforms).
262>         basedir = "storage/WebStatus/status_no_disk_stats"
2632458c2478
264<         ss = NoStatvfsServer(basedir, "\x00" * 20)
265---
266>         ss = NoDiskStatsServer(basedir, "\x00" * 20)
2672465a2486,2503
268>         self.failUnless("Space Available to Tahoe: ?" in s, s)
269>         self.failUnless(ss.get_available_space() is None)
270>
271>     def test_status_bad_disk_stats(self):
272>         # If the API to get disk stats exists but a call to it fails, then the status should
273>         # show that no shares will be accepted, and get_available_space() should be 0.
274>         basedir = "storage/WebStatus/status_bad_disk_stats"
275>         fileutil.make_dirs(basedir)
276>         ss = BadDiskStatsServer(basedir, "\x00" * 20)
277>         ss.setServiceParent(self.s)
278>         w = StorageStatus(ss)
279>         html = w.renderSynchronously()
280>         self.failUnless("<h1>Storage Server Status</h1>" in html, html)
281>         s = remove_tags(html)
282>         self.failUnless("Accepting new shares: No" in s, s)
283>         self.failUnless("Total disk space: ?" in s, s)
284>         self.failUnless("Space Available to Tahoe: ?" in s, s)
285>         self.failUnless(ss.get_available_space() == 0)