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.internet import defer
import time, os.path, stat
import time, os.path, stat, re
import itertools
from allmydata import interfaces
from allmydata.util import fileutil, hashutil, base32
@ -14,6 +14,8 @@ from allmydata.immutable.layout import WriteBucketProxy, WriteBucketProxy_v2, \
ReadBucketProxy
from allmydata.interfaces import BadWriteEnablerError
from allmydata.test.common import LoggingServiceParent
from allmydata.web.storage import StorageStatus, abbreviate_if_known, \
remove_prefix
class Marker:
pass
@ -1287,3 +1289,55 @@ class Stats(unittest.TestCase):
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_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)
self.client = client
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_cap = URIHandler(client)
@ -170,6 +175,7 @@ class Root(rend.Page):
child_reliability = NoReliability()
child_report_incident = IncidentReporter()
#child_server # let's reserve this for storage-server-over-HTTP
def data_version(self, ctx, data):
return get_package_versions_string()
@ -184,10 +190,15 @@ class Root(rend.Page):
ul = T.ul()
try:
ss = self.client.getServiceNamed("storage")
allocated_s = abbreviate_size(ss.allocated_size())
allocated = "about %s allocated" % allocated_s
reserved = "%s reserved" % abbreviate_size(ss.reserved_space)
ul[T.li["Storage Server: %s, %s" % (allocated, reserved)]]
stats = ss.get_stats()
if stats["storage_server.accepting_immutable_shares"]:
msg = "accepting new shares"
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:
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>