diff --git a/src/allmydata/storage/server.py b/src/allmydata/storage/server.py
index 7971113d6..4840cc53f 100644
--- a/src/allmydata/storage/server.py
+++ b/src/allmydata/storage/server.py
@@ -131,6 +131,9 @@ class StorageServer(service.MultiService, Referenceable):
def _clean_incomplete(self):
fileutil.rm_dir(self.incomingdir)
+ def do_statvfs(self):
+ return os.statvfs(self.storedir)
+
def get_stats(self):
# remember: RIStatsProvider requires that our return dict
# contains numeric values.
@@ -143,7 +146,7 @@ class StorageServer(service.MultiService, Referenceable):
if self.readonly_storage:
writeable = False
try:
- s = os.statvfs(self.storedir)
+ s = self.do_statvfs()
disk_total = s.f_bsize * s.f_blocks
disk_used = s.f_bsize * (s.f_blocks - s.f_bfree)
# spacetime predictors should look at the slope of disk_used.
diff --git a/src/allmydata/test/test_storage.py b/src/allmydata/test/test_storage.py
index d5723247d..d87880988 100644
--- a/src/allmydata/test/test_storage.py
+++ b/src/allmydata/test/test_storage.py
@@ -1290,6 +1290,9 @@ class Stats(unittest.TestCase):
self.failUnless(abs(output["get"]["99_0_percentile"] - 5) < 1)
self.failUnless(abs(output["get"]["99_9_percentile"] - 5) < 1)
+class NoStatvfsServer(StorageServer):
+ def do_statvfs(self):
+ raise AttributeError
class WebStatus(unittest.TestCase):
@@ -1315,6 +1318,19 @@ class WebStatus(unittest.TestCase):
self.failUnless("Accepting new shares: Yes" in s, s)
self.failUnless("Reserved space: - 0B" in s, s)
+ def test_status_no_statvfs(self):
+ # windows has no os.statvfs . Make sure the code handles that even on
+ # unix.
+ basedir = "storage/WebStatus/status_no_statvfs"
+ fileutil.make_dirs(basedir)
+ ss = NoStatvfsServer(basedir, "\x00" * 20)
+ w = StorageStatus(ss)
+ html = w.renderSynchronously()
+ self.failUnless("
Storage Server Status
" in html, html)
+ s = self.remove_tags(html)
+ self.failUnless("Accepting new shares: Yes" in s, s)
+ self.failUnless("Total disk space: ?" in s, s)
+
def test_readonly(self):
basedir = "storage/WebStatus/readonly"
fileutil.make_dirs(basedir)
diff --git a/src/allmydata/web/storage.py b/src/allmydata/web/storage.py
index 013cedb9f..a8698a47d 100644
--- a/src/allmydata/web/storage.py
+++ b/src/allmydata/web/storage.py
@@ -37,11 +37,27 @@ class StorageStatus(rend.Page):
# object in self.original that gets passed to render_* methods. I
# still don't understand Nevow.
- # all xhtml tags that are children of a tag with n:render="stats"
- # will be processed with this dictionary, so something like:
+ # Nevow has nevow.accessors.DictionaryContainer: Any data= directive
+ # that appears in a context in which the current data is a dictionary
+ # will be looked up as keys in that dictionary. So if data_stats()
+ # returns a dictionary, then we can use something like this:
+ #
#
- # - disk_total:
+ # - disk_total:
#
- # will use get_stats()["storage_server.disk_total"]
- return dict([ (remove_prefix(k, "storage_server."), v)
- for k,v in self.storage.get_stats().items() ])
+
+ # to use get_stats()["storage_server.disk_total"] . However,
+ # DictionaryContainer does a raw d[] instead of d.get(), so any
+ # missing keys will cause an error, even if the renderer can tolerate
+ # None values. To overcome this, we either need a dict-like object
+ # that always returns None for unknown keys, or we must pre-populate
+ # our dict with those missing keys (or find some way to override
+ # Nevow's handling of dictionaries).
+
+ d = dict([ (remove_prefix(k, "storage_server."), v)
+ for k,v in self.storage.get_stats().items() ])
+ d.setdefault("disk_total", None)
+ d.setdefault("disk_used", None)
+ d.setdefault("reserved_space", None)
+ d.setdefault("disk_avail", None)
+ return d