diff --git a/src/allmydata/stats.py b/src/allmydata/stats.py
index 96fea7a68..1a0ae0d82 100644
--- a/src/allmydata/stats.py
+++ b/src/allmydata/stats.py
@@ -103,15 +103,17 @@ class StatsProvider(foolscap.Referenceable, service.MultiService):
def register_producer(self, stats_producer):
self.stats_producers.append(IStatsProducer(stats_producer))
- def remote_get_stats(self):
+ def get_stats(self):
stats = {}
for sp in self.stats_producers:
stats.update(sp.get_stats())
- #return { 'counters': self.counters, 'stats': stats }
ret = { 'counters': self.counters, 'stats': stats }
- log.msg(format='get_stats() -> %s', args=(pprint.pformat(ret),), level=12)
+ log.msg(format='get_stats() -> %(stats)s', stats=ret, level=log.NOISY)
return ret
+ def remote_get_stats(self):
+ return self.get_stats()
+
def _connected(self, gatherer, nickname):
gatherer.callRemoteOnly('provide', self, nickname or '')
diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py
index c191671bc..970c5adab 100644
--- a/src/allmydata/test/test_system.py
+++ b/src/allmydata/test/test_system.py
@@ -1354,6 +1354,18 @@ class SystemTest(testutil.SignalMixin, testutil.PollMixin, unittest.TestCase):
self.failUnlessEqual(data, {})
d.addCallback(_got_non_helper_status_json)
+ # see if the statistics page exists
+ d.addCallback(lambda res: self.GET("statistics"))
+ def _got_stats(res):
+ self.failUnless("Node Statistics" in res)
+ self.failUnless(" 'downloader.files_downloaded': 8," in res)
+ d.addCallback(_got_stats)
+ d.addCallback(lambda res: self.GET("statistics?t=json"))
+ def _got_stats_json(res):
+ data = simplejson.loads(res)
+ self.failUnlessEqual(data["counters"]["uploader.files_uploaded"], 5)
+ self.failUnlessEqual(data["stats"]["chk_upload_helper.upload_need_upload"], 1)
+ d.addCallback(_got_stats_json)
# TODO: mangle the second segment of a file, to test errors that
# occur after we've already sent some good data, which uses a
diff --git a/src/allmydata/web/statistics.xhtml b/src/allmydata/web/statistics.xhtml
new file mode 100644
index 000000000..fa956ab77
--- /dev/null
+++ b/src/allmydata/web/statistics.xhtml
@@ -0,0 +1,28 @@
+
+
+ Stats - AllMyData Tahoe
+
+
+
+
+
+
+Node Statistics
+
+
+ - Load Average:
+ - Peak Load:
+ - Files Uploaded (immutable):
+ - Files Downloaded (immutable):
+ - Files Published (mutable):
+ - Files Retrieved (mutable):
+
+
+Raw Stats:
+
+
+
+
+
+
diff --git a/src/allmydata/web/status.py b/src/allmydata/web/status.py
index 49518f98f..1b07045db 100644
--- a/src/allmydata/web/status.py
+++ b/src/allmydata/web/status.py
@@ -1,11 +1,11 @@
-import time
+import time, pprint
import simplejson
from twisted.internet import defer
from nevow import rend, inevow, tags as T
from allmydata.util import base32, idlib
from allmydata.web.common import IClient, getxmlfile, abbreviate_time, \
- abbreviate_rate, get_arg
+ abbreviate_rate, abbreviate_size, get_arg
from allmydata.interfaces import IUploadStatus, IDownloadStatus, \
IPublishStatus, IRetrieveStatus
@@ -808,3 +808,48 @@ class HelperStatus(rend.Page):
def render_upload_bytes_encoded(self, ctx, data):
return str(data["chk_upload_helper.encoded_bytes"])
+
+class Statistics(rend.Page):
+ docFactory = getxmlfile("statistics.xhtml")
+
+ def renderHTTP(self, ctx):
+ provider = IClient(ctx).stats_provider
+ stats = {'stats': {}, 'counters': {}}
+ if provider:
+ stats = provider.get_stats()
+ t = get_arg(inevow.IRequest(ctx), "t")
+ if t == "json":
+ return simplejson.dumps(stats, indent=1)
+ # is there a better way to provide 'data' to all rendering methods?
+ self.original = stats
+ return rend.Page.renderHTTP(self, ctx)
+
+ def render_load_average(self, ctx, data):
+ return str(data["stats"].get("load_monitor.avg_load"))
+
+ def render_peak_load(self, ctx, data):
+ return str(data["stats"].get("load_monitor.max_load"))
+
+ def render_uploads(self, ctx, data):
+ files = data["counters"].get("uploader.files_uploaded")
+ bytes = data["counters"].get("uploader.bytes_uploaded")
+ return ("%s files / %s bytes (%s)" %
+ (files, bytes, abbreviate_size(bytes)))
+
+ def render_downloads(self, ctx, data):
+ files = data["counters"].get("downloader.files_uploaded")
+ bytes = data["counters"].get("downloader.bytes_uploaded")
+ return ("%s files / %s bytes (%s)" %
+ (files, bytes, abbreviate_size(bytes)))
+
+ def render_publishes(self, ctx, data):
+ files = data["counters"].get("mutable.files_published")
+ return "%s files" % (files,)
+
+ def render_retrieves(self, ctx, data):
+ files = data["counters"].get("mutable.files_retrieved")
+ return "%s files" % (files,)
+
+ def render_raw(self, ctx, data):
+ raw = pprint.pformat(data)
+ return ctx.tag[raw]
diff --git a/src/allmydata/webish.py b/src/allmydata/webish.py
index 5afc3fb92..4f0bebf66 100644
--- a/src/allmydata/webish.py
+++ b/src/allmydata/webish.py
@@ -1446,6 +1446,7 @@ class Root(rend.Page):
child_provisioning = provisioning.ProvisioningTool()
child_status = status.Status()
child_helper_status = status.HelperStatus()
+ child_statistics = status.Statistics()
def data_version(self, ctx, data):
return get_package_versions_string()