web: add Storage status page, improve tests

This commit is contained in:
Brian Warner 2009-02-20 14:29:26 -07:00
parent ff6907a557
commit 00677ff9a5
4 changed files with 151 additions and 5 deletions

View File

@ -1,7 +1,7 @@
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import defer from twisted.internet import defer
import time, os.path, stat import time, os.path, stat, re
import itertools import itertools
from allmydata import interfaces from allmydata import interfaces
from allmydata.util import fileutil, hashutil, base32 from allmydata.util import fileutil, hashutil, base32
@ -14,6 +14,8 @@ from allmydata.immutable.layout import WriteBucketProxy, WriteBucketProxy_v2, \
ReadBucketProxy ReadBucketProxy
from allmydata.interfaces import BadWriteEnablerError from allmydata.interfaces import BadWriteEnablerError
from allmydata.test.common import LoggingServiceParent from allmydata.test.common import LoggingServiceParent
from allmydata.web.storage import StorageStatus, abbreviate_if_known, \
remove_prefix
class Marker: class Marker:
pass pass
@ -1287,3 +1289,55 @@ class Stats(unittest.TestCase):
self.failUnless(abs(output["get"]["95_0_percentile"] - 5) < 1) self.failUnless(abs(output["get"]["95_0_percentile"] - 5) < 1)
self.failUnless(abs(output["get"]["99_0_percentile"] - 5) < 1) self.failUnless(abs(output["get"]["99_0_percentile"] - 5) < 1)
self.failUnless(abs(output["get"]["99_9_percentile"] - 5) < 1) self.failUnless(abs(output["get"]["99_9_percentile"] - 5) < 1)
class WebStatus(unittest.TestCase):
def test_no_server(self):
w = StorageStatus(None)
html = w.renderSynchronously()
self.failUnless("<h1>No Storage Server Running</h1>" in html, html)
def remove_tags(self, s):
s = re.sub(r'<[^>]*>', ' ', s)
s = re.sub(r'\s+', ' ', s)
return s
def test_status(self):
basedir = "storage/WebStatus/status"
fileutil.make_dirs(basedir)
ss = StorageServer(basedir, "\x00" * 20)
w = StorageStatus(ss)
html = w.renderSynchronously()
self.failUnless("<h1>Storage Server Status</h1>" in html, html)
s = self.remove_tags(html)
self.failUnless("Accepting new shares: Yes" in s, s)
self.failUnless("Reserved space: - 0B" in s, s)
def test_readonly(self):
basedir = "storage/WebStatus/readonly"
fileutil.make_dirs(basedir)
ss = StorageServer(basedir, "\x00" * 20, readonly_storage=True)
w = StorageStatus(ss)
html = w.renderSynchronously()
self.failUnless("<h1>Storage Server Status</h1>" in html, html)
s = self.remove_tags(html)
self.failUnless("Accepting new shares: No" in s, s)
def test_reserved(self):
basedir = "storage/WebStatus/reserved"
fileutil.make_dirs(basedir)
ss = StorageServer(basedir, "\x00" * 20, reserved_space=10e6)
w = StorageStatus(ss)
html = w.renderSynchronously()
self.failUnless("<h1>Storage Server Status</h1>" in html, html)
s = self.remove_tags(html)
self.failUnless("Reserved space: - 10.00MB" in s, s)
def test_util(self):
self.failUnlessEqual(abbreviate_if_known(None), "?")
self.failUnlessEqual(abbreviate_if_known(10e6), "10.00MB")
self.failUnlessEqual(remove_prefix("foo.bar", "foo."), "bar")
self.failUnlessEqual(remove_prefix("foo.bar", "baz."), None)

View File

@ -142,6 +142,11 @@ class Root(rend.Page):
rend.Page.__init__(self, client) rend.Page.__init__(self, client)
self.client = client self.client = client
self.child_operations = operations.OphandleTable() self.child_operations = operations.OphandleTable()
try:
s = client.getServiceNamed("storage")
except KeyError:
s = None
self.child_storage = storage.StorageStatus(s)
self.child_uri = URIHandler(client) self.child_uri = URIHandler(client)
self.child_cap = URIHandler(client) self.child_cap = URIHandler(client)
@ -170,6 +175,7 @@ class Root(rend.Page):
child_reliability = NoReliability() child_reliability = NoReliability()
child_report_incident = IncidentReporter() child_report_incident = IncidentReporter()
#child_server # let's reserve this for storage-server-over-HTTP
def data_version(self, ctx, data): def data_version(self, ctx, data):
return get_package_versions_string() return get_package_versions_string()
@ -184,10 +190,15 @@ class Root(rend.Page):
ul = T.ul() ul = T.ul()
try: try:
ss = self.client.getServiceNamed("storage") ss = self.client.getServiceNamed("storage")
allocated_s = abbreviate_size(ss.allocated_size()) stats = ss.get_stats()
allocated = "about %s allocated" % allocated_s if stats["storage_server.accepting_immutable_shares"]:
reserved = "%s reserved" % abbreviate_size(ss.reserved_space) msg = "accepting new shares"
ul[T.li["Storage Server: %s, %s" % (allocated, reserved)]] else:
msg = "not accepting new shares (read-only)"
available = stats.get("storage_server.disk_avail")
if available is not None:
msg += ", %s available" % abbreviate_size(available)
ul[T.li[T.a(href="storage")["Storage Server"], ": ", msg]]
except KeyError: except KeyError:
ul[T.li["Not running storage server"]] ul[T.li["Not running storage server"]]

View File

@ -0,0 +1,47 @@
from nevow import rend, tags as T
from allmydata.web.common import getxmlfile, abbreviate_size
def abbreviate_if_known(size):
if size is None:
return "?"
return abbreviate_size(size)
def remove_prefix(s, prefix):
if not s.startswith(prefix):
return None
return s[len(prefix):]
class StorageStatus(rend.Page):
docFactory = getxmlfile("storage_status.xhtml")
# the default 'data' argument is the StorageServer instance
def __init__(self, storage):
rend.Page.__init__(self, storage)
self.storage = storage
def render_storage_running(self, ctx, storage):
if storage:
return ctx.tag
else:
return T.h1["No Storage Server Running"]
def render_bool(self, ctx, data):
return {True: "Yes", False: "No"}[bool(data)]
def render_space(self, ctx, data):
return abbreviate_if_known(data)
def data_stats(self, ctx, data):
# FYI: 'data' appears to be self, rather than the StorageServer
# 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:
# <ul n:data="stats">
# <li>disk_total: <span n:data="disk_total" /></li>
# </ul>
# 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() ])

View File

@ -0,0 +1,34 @@
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
<head>
<title>AllMyData - Tahoe - Storage Server Status</title>
<link href="/webform_css" rel="stylesheet" type="text/css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div n:render="storage_running">
<h1>Storage Server Status</h1>
<ul n:data="stats">
<li>Accepting new shares:
<span n:render="bool" n:data="accepting_immutable_shares" /></li>
</ul>
<table n:data="stats">
<tr><td>Total disk space:</td>
<td><span n:render="space" n:data="disk_total" /></td></tr>
<tr><td>Disk space used:</td>
<td>- <span n:render="space" n:data="disk_used" /></td></tr>
<tr><td>Reserved space:</td>
<td>- <span n:render="space" n:data="reserved_space" /></td></tr>
<tr><td />
<td>======</td></tr>
<tr><td>Space Available:</td>
<td>&lt; <span n:render="space" n:data="disk_avail" /></td></tr>
</table>
</div>
</body>
</html>