mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-04-17 07:28:57 +00:00
stats: add /statistics web page to show them, add tests
This commit is contained in:
parent
8bea663fb2
commit
5b8320442a
@ -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 '')
|
||||
|
||||
|
@ -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
|
||||
|
28
src/allmydata/web/statistics.xhtml
Normal file
28
src/allmydata/web/statistics.xhtml
Normal file
@ -0,0 +1,28 @@
|
||||
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
|
||||
<head>
|
||||
<title>Stats - AllMyData Tahoe</title>
|
||||
<!-- <link href="http://www.allmydata.com/common/css/styles.css"
|
||||
rel="stylesheet" type="text/css"/> -->
|
||||
<link href="/webform_css" rel="stylesheet" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Node Statistics</h1>
|
||||
|
||||
<ul>
|
||||
<li>Load Average: <span n:render="load_average" /></li>
|
||||
<li>Peak Load: <span n:render="peak_load" /></li>
|
||||
<li>Files Uploaded (immutable): <span n:render="uploads" /></li>
|
||||
<li>Files Downloaded (immutable): <span n:render="downloads" /></li>
|
||||
<li>Files Published (mutable): <span n:render="publishes" /></li>
|
||||
<li>Files Retrieved (mutable): <span n:render="retrieves" /></li>
|
||||
</ul>
|
||||
|
||||
<h2>Raw Stats:</h2>
|
||||
<pre n:render="raw" />
|
||||
|
||||
<div>Return to the <a href="/">Welcome Page</a></div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -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]
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user