mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-30 09:48:56 +00:00
Use twisted.web.template in web/storage.py
Related to ticket:3247. Nevow usage has been removed, and generated page looks the same as its former self, but tests are failing because test_storage.py assumes that we're using nevow.
This commit is contained in:
parent
34e85ac515
commit
d142ccb159
@ -1,10 +1,10 @@
|
||||
|
||||
import time, json
|
||||
from nevow import rend, tags as T
|
||||
from twisted.python.filepath import FilePath
|
||||
from twisted.web.template import tags as T, renderer, Element, renderElement, XMLFile
|
||||
from allmydata.web.common import (
|
||||
getxmlfile,
|
||||
abbreviate_time,
|
||||
MultiFormatPage,
|
||||
MultiFormatResource
|
||||
)
|
||||
from allmydata.util.abbreviate import abbreviate_space
|
||||
from allmydata.util import time_format, idlib
|
||||
@ -16,91 +16,100 @@ def remove_prefix(s, prefix):
|
||||
return s[len(prefix):]
|
||||
|
||||
|
||||
class StorageStatus(MultiFormatPage):
|
||||
docFactory = getxmlfile("storage_status.xhtml")
|
||||
# the default 'data' argument is the StorageServer instance
|
||||
class StorageStatusElement(Element):
|
||||
loader = XMLFile(FilePath(__file__).sibling("storage_status.xhtml"))
|
||||
|
||||
def __init__(self, storage, nickname=""):
|
||||
rend.Page.__init__(self, storage)
|
||||
def __init__(self, storage, nickname):
|
||||
super(StorageStatusElement, self).__init__()
|
||||
self.storage = storage
|
||||
self.nickname = nickname
|
||||
self.nick = nickname
|
||||
|
||||
def render_JSON(self, req):
|
||||
req.setHeader("content-type", "text/plain")
|
||||
d = {"stats": self.storage.get_stats(),
|
||||
"bucket-counter": self.storage.bucket_counter.get_state(),
|
||||
"lease-checker": self.storage.lease_checker.get_state(),
|
||||
"lease-checker-progress": self.storage.lease_checker.get_progress(),
|
||||
}
|
||||
return json.dumps(d, indent=1) + "\n"
|
||||
@renderer
|
||||
def nickname(self, req, tag):
|
||||
return self.nick
|
||||
|
||||
def data_nickname(self, ctx, storage):
|
||||
return self.nickname
|
||||
def data_nodeid(self, ctx, storage):
|
||||
@renderer
|
||||
def nodeid(self, req, tag):
|
||||
return idlib.nodeid_b2a(self.storage.my_nodeid)
|
||||
|
||||
def render_storage_running(self, ctx, storage):
|
||||
if storage:
|
||||
return ctx.tag
|
||||
else:
|
||||
return T.h1["No Storage Server Running"]
|
||||
def get_stat(self, key):
|
||||
return self.storage.get_stats().get(key)
|
||||
|
||||
def render_bool(self, ctx, data):
|
||||
return {True: "Yes", False: "No"}[bool(data)]
|
||||
def str(self, tag, val):
|
||||
if val is None:
|
||||
return tag("?")
|
||||
return tag(str(val))
|
||||
|
||||
def render_abbrev_space(self, ctx, size):
|
||||
if size is None:
|
||||
return "?"
|
||||
return abbreviate_space(size)
|
||||
def abbr(self, tag, val):
|
||||
if val is None:
|
||||
return tag("?")
|
||||
return tag(abbreviate_space(val))
|
||||
|
||||
def render_space(self, ctx, size):
|
||||
if size is None:
|
||||
return "?"
|
||||
return "%d" % size
|
||||
@renderer
|
||||
def disk_total(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.disk_total"))
|
||||
|
||||
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.
|
||||
@renderer
|
||||
def disk_total_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.disk_total"))
|
||||
|
||||
# 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:
|
||||
#
|
||||
# <ul n:data="stats">
|
||||
# <li>disk_total: <span n:render="abbrev" n:data="disk_total" /></li>
|
||||
# </ul>
|
||||
@renderer
|
||||
def disk_used(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.disk_used"))
|
||||
|
||||
# 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 we should get rid of data_
|
||||
# methods that return dicts (or find some way to override Nevow's
|
||||
# handling of dictionaries).
|
||||
@renderer
|
||||
def disk_used_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.disk_used"))
|
||||
|
||||
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("disk_free_for_root", None)
|
||||
d.setdefault("disk_free_for_nonroot", None)
|
||||
d.setdefault("reserved_space", None)
|
||||
d.setdefault("disk_avail", None)
|
||||
return d
|
||||
@renderer
|
||||
def disk_free_for_root(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.disk_free_for_root"))
|
||||
|
||||
def data_last_complete_bucket_count(self, ctx, data):
|
||||
@renderer
|
||||
def disk_free_for_root_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.disk_free_for_root"))
|
||||
|
||||
@renderer
|
||||
def disk_free_for_nonroot(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.disk_free_for_nonroot"))
|
||||
|
||||
@renderer
|
||||
def disk_free_for_nonroot_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.disk_free_for_nonroot"))
|
||||
|
||||
@renderer
|
||||
def reserved_space(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.reserved_space"))
|
||||
|
||||
@renderer
|
||||
def reserved_space_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.reserved_space"))
|
||||
|
||||
@renderer
|
||||
def disk_avail(self, req, tag):
|
||||
return self.str(tag, self.get_stat("storage_server.disk_avail"))
|
||||
|
||||
@renderer
|
||||
def disk_avail_abbrev(self, req, tag):
|
||||
return self.abbr(tag, self.get_stat("storage_server.disk_avail"))
|
||||
|
||||
@renderer
|
||||
def accepting_immutable_shares(self, req, tag):
|
||||
accepting = self.get_stat("storage_server.accepting_immutable_shares")
|
||||
return {True: "Yes", False: "No"}[bool(accepting)]
|
||||
|
||||
@renderer
|
||||
def last_complete_bucket_count(self, req, tag):
|
||||
s = self.storage.bucket_counter.get_state()
|
||||
count = s.get("last-complete-bucket-count")
|
||||
if count is None:
|
||||
return "Not computed yet"
|
||||
return count
|
||||
return str(count)
|
||||
|
||||
def render_count_crawler_status(self, ctx, storage):
|
||||
@renderer
|
||||
def count_crawler_status(self, req, tag):
|
||||
p = self.storage.bucket_counter.get_progress()
|
||||
return ctx.tag[self.format_crawler_progress(p)]
|
||||
return self.format_crawler_progress(p)
|
||||
|
||||
def format_crawler_progress(self, p):
|
||||
cycletime = p["estimated-time-per-cycle"]
|
||||
@ -127,55 +136,51 @@ class StorageStatus(MultiFormatPage):
|
||||
return ["Next crawl in %s" % abbreviate_time(soon),
|
||||
cycletime_s]
|
||||
|
||||
def render_lease_expiration_enabled(self, ctx, data):
|
||||
@renderer
|
||||
def storage_running(self, req, tag):
|
||||
if self.storage:
|
||||
return tag
|
||||
return tag("No Storage Server Running")
|
||||
|
||||
@renderer
|
||||
def lease_expiration_enabled(self, req, tag):
|
||||
lc = self.storage.lease_checker
|
||||
if lc.expiration_enabled:
|
||||
return ctx.tag["Enabled: expired leases will be removed"]
|
||||
return tag("Enabled: expired leases will be removed")
|
||||
else:
|
||||
return ctx.tag["Disabled: scan-only mode, no leases will be removed"]
|
||||
return tag("Disabled: scan-only mode, no leases will be removed")
|
||||
|
||||
def render_lease_expiration_mode(self, ctx, data):
|
||||
@renderer
|
||||
def lease_expiration_mode(self, req, tag):
|
||||
lc = self.storage.lease_checker
|
||||
if lc.mode == "age":
|
||||
if lc.override_lease_duration is None:
|
||||
ctx.tag["Leases will expire naturally, probably 31 days after "
|
||||
"creation or renewal."]
|
||||
tag("Leases will expire naturally, probably 31 days after "
|
||||
"creation or renewal.")
|
||||
else:
|
||||
ctx.tag["Leases created or last renewed more than %s ago "
|
||||
"will be considered expired."
|
||||
% abbreviate_time(lc.override_lease_duration)]
|
||||
tag("Leases created or last renewed more than %s ago "
|
||||
"will be considered expired."
|
||||
% abbreviate_time(lc.override_lease_duration))
|
||||
else:
|
||||
assert lc.mode == "cutoff-date"
|
||||
localizedutcdate = time.strftime("%d-%b-%Y", time.gmtime(lc.cutoff_date))
|
||||
isoutcdate = time_format.iso_utc_date(lc.cutoff_date)
|
||||
ctx.tag["Leases created or last renewed before %s (%s) UTC "
|
||||
"will be considered expired." % (isoutcdate, localizedutcdate, )]
|
||||
tag("Leases created or last renewed before %s (%s) UTC "
|
||||
"will be considered expired."
|
||||
% (isoutcdate, localizedutcdate, ))
|
||||
if len(lc.mode) > 2:
|
||||
ctx.tag[" The following sharetypes will be expired: ",
|
||||
" ".join(sorted(lc.sharetypes_to_expire)), "."]
|
||||
return ctx.tag
|
||||
tag(" The following sharetypes will be expired: ",
|
||||
" ".join(sorted(lc.sharetypes_to_expire)), ".")
|
||||
return tag
|
||||
|
||||
def format_recovered(self, sr, a):
|
||||
def maybe(d):
|
||||
if d is None:
|
||||
return "?"
|
||||
return "%d" % d
|
||||
return "%s shares, %s buckets (%s mutable / %s immutable), %s (%s / %s)" % \
|
||||
(maybe(sr["%s-shares" % a]),
|
||||
maybe(sr["%s-buckets" % a]),
|
||||
maybe(sr["%s-buckets-mutable" % a]),
|
||||
maybe(sr["%s-buckets-immutable" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes-mutable" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes-immutable" % a]),
|
||||
)
|
||||
|
||||
def render_lease_current_cycle_progress(self, ctx, data):
|
||||
@renderer
|
||||
def lease_current_cycle_progress(self, req, tag):
|
||||
lc = self.storage.lease_checker
|
||||
p = lc.get_progress()
|
||||
return ctx.tag[self.format_crawler_progress(p)]
|
||||
return tag(self.format_crawler_progress(p))
|
||||
|
||||
def render_lease_current_cycle_results(self, ctx, data):
|
||||
@renderer
|
||||
def lease_current_cycle_results(self, req, tag):
|
||||
lc = self.storage.lease_checker
|
||||
p = lc.get_progress()
|
||||
if not p["cycle-in-progress"]:
|
||||
@ -229,10 +234,10 @@ class StorageStatus(MultiFormatPage):
|
||||
T.ul[ [T.li[ ["SI %s shnum %d" % corrupt_share
|
||||
for corrupt_share in so_far["corrupt-shares"] ]
|
||||
]]])
|
||||
return tag("Current cycle:", p)
|
||||
|
||||
return ctx.tag["Current cycle:", p]
|
||||
|
||||
def render_lease_last_cycle_results(self, ctx, data):
|
||||
@renderer
|
||||
def lease_last_cycle_results(self, req, tag):
|
||||
lc = self.storage.lease_checker
|
||||
h = lc.get_state()["history"]
|
||||
if not h:
|
||||
@ -240,15 +245,15 @@ class StorageStatus(MultiFormatPage):
|
||||
last = h[max(h.keys())]
|
||||
|
||||
start, end = last["cycle-start-finish-times"]
|
||||
ctx.tag["Last complete cycle (which took %s and finished %s ago)"
|
||||
" recovered: " % (abbreviate_time(end-start),
|
||||
abbreviate_time(time.time() - end)),
|
||||
self.format_recovered(last["space-recovered"], "actual")
|
||||
]
|
||||
tag("Last complete cycle (which took %s and finished %s ago)"
|
||||
" recovered: " % (abbreviate_time(end-start),
|
||||
abbreviate_time(time.time() - end)),
|
||||
self.format_recovered(last["space-recovered"], "actual"))
|
||||
|
||||
p = T.ul()
|
||||
|
||||
def add(*pieces):
|
||||
p[T.li[pieces]]
|
||||
p(T.li(pieces))
|
||||
|
||||
saw = self.format_recovered(last["space-recovered"], "examined")
|
||||
add("and saw a total of ", saw)
|
||||
@ -264,4 +269,37 @@ class StorageStatus(MultiFormatPage):
|
||||
for corrupt_share in last["corrupt-shares"] ]
|
||||
]]])
|
||||
|
||||
return ctx.tag[p]
|
||||
return tag(p)
|
||||
|
||||
def format_recovered(self, sr, a):
|
||||
def maybe(d):
|
||||
if d is None:
|
||||
return "?"
|
||||
return "%d" % d
|
||||
return "%s shares, %s buckets (%s mutable / %s immutable), %s (%s / %s)" % \
|
||||
(maybe(sr["%s-shares" % a]),
|
||||
maybe(sr["%s-buckets" % a]),
|
||||
maybe(sr["%s-buckets-mutable" % a]),
|
||||
maybe(sr["%s-buckets-immutable" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes-mutable" % a]),
|
||||
abbreviate_space(sr["%s-diskbytes-immutable" % a]),
|
||||
)
|
||||
|
||||
class StorageStatus(MultiFormatResource):
|
||||
def __init__(self, storage, nickname=""):
|
||||
super(StorageStatus, self).__init__()
|
||||
self.storage = storage
|
||||
self.nickname = nickname
|
||||
|
||||
def render_HTML(self, req):
|
||||
return renderElement(req, StorageStatusElement(self.storage, self.nickname))
|
||||
|
||||
def render_JSON(self, req):
|
||||
req.setHeader("content-type", "text/plain")
|
||||
d = {"stats": self.storage.get_stats(),
|
||||
"bucket-counter": self.storage.bucket_counter.get_state(),
|
||||
"lease-checker": self.storage.lease_checker.get_state(),
|
||||
"lease-checker-progress": self.storage.lease_checker.get_progress(),
|
||||
}
|
||||
return json.dumps(d, indent=1) + "\n"
|
||||
|
@ -1,4 +1,4 @@
|
||||
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
|
||||
<html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
|
||||
<head>
|
||||
<title>Tahoe-LAFS - Storage Server Status</title>
|
||||
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
|
||||
@ -7,19 +7,19 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div n:render="storage_running">
|
||||
<div t:render="storage_running">
|
||||
|
||||
<h1>Storage Server Status</h1>
|
||||
|
||||
<table n:data="stats">
|
||||
<table class="storage_status">
|
||||
<tr><td>Total disk space:</td>
|
||||
<td><span n:render="abbrev_space" n:data="disk_total" /></td>
|
||||
<td>(<span n:render="space" n:data="disk_total" />)</td>
|
||||
<td><span t:render="disk_total_abbrev" /></td>
|
||||
<td>(<span t:render="disk_total" />)</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr><td>Disk space used:</td>
|
||||
<td>- <span n:render="abbrev_space" n:data="disk_used" /></td>
|
||||
<td>(<span n:render="space" n:data="disk_used" />)</td>
|
||||
<td>- <span t:render="disk_used_abbrev" /></td>
|
||||
<td>(<span t:render="disk_used" />)</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr><td />
|
||||
@ -28,18 +28,18 @@
|
||||
<td />
|
||||
</tr>
|
||||
<tr><td>Disk space free (root):</td>
|
||||
<td><span n:render="abbrev_space" n:data="disk_free_for_root"/></td>
|
||||
<td>(<span n:render="space" n:data="disk_free_for_root"/>)</td>
|
||||
<td><span t:render="disk_free_for_root_abbrev"/></td>
|
||||
<td>(<span t:render="disk_free_for_root"/>)</td>
|
||||
<td>[see 1]</td>
|
||||
</tr>
|
||||
<tr><td>Disk space free (non-root):</td>
|
||||
<td><span n:render="abbrev_space" n:data="disk_free_for_nonroot" /></td>
|
||||
<td>(<span n:render="space" n:data="disk_free_for_nonroot" />)</td>
|
||||
<td><span t:render="disk_free_for_nonroot_abbrev" /></td>
|
||||
<td>(<span t:render="disk_free_for_nonroot" />)</td>
|
||||
<td>[see 2]</td>
|
||||
</tr>
|
||||
<tr><td>Reserved space:</td>
|
||||
<td>- <span n:render="abbrev_space" n:data="reserved_space" /></td>
|
||||
<td>(<span n:render="space" n:data="reserved_space" />)</td>
|
||||
<td>- <span t:render="reserved_space_abbrev" /></td>
|
||||
<td>(<span t:render="reserved_space" />)</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr><td />
|
||||
@ -48,23 +48,31 @@
|
||||
<td />
|
||||
</tr>
|
||||
<tr><td>Space Available to Tahoe:</td>
|
||||
<td><span n:render="abbrev_space" n:data="disk_avail" /></td>
|
||||
<td>(<span n:render="space" n:data="disk_avail" />)</td>
|
||||
<td><span t:render="disk_avail_abbrev" /></td>
|
||||
<td>(<span t:render="disk_avail" />)</td>
|
||||
<td />
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<ul>
|
||||
<li>Server Nickname: <span class="nickname mine" n:render="data" n:data="nickname" /></li>
|
||||
<li>Server Nodeid: <span class="nodeid mine data-chars" n:render="string" n:data="nodeid" /></li>
|
||||
<li n:data="stats">Accepting new shares:
|
||||
<span n:render="bool" n:data="accepting_immutable_shares" /></li>
|
||||
<li>Server Nickname:
|
||||
<span class="nickname mine">
|
||||
<span t:render="nickname" />
|
||||
</span>
|
||||
</li>
|
||||
<li>Server Nodeid:
|
||||
<span class="nodeid mine data-chars">
|
||||
<span t:render="nodeid" />
|
||||
</span>
|
||||
</li>
|
||||
<li t:data="stats">Accepting new shares:
|
||||
<span t:render="accepting_immutable_shares" /></li>
|
||||
<li>Total buckets:
|
||||
<span n:render="string" n:data="last_complete_bucket_count" />
|
||||
<span t:render="last_complete_bucket_count" />
|
||||
(the number of files and directories for which this server is holding
|
||||
a share)
|
||||
<ul>
|
||||
<li n:render="count_crawler_status" />
|
||||
<li><span t:render="count_crawler_status" /></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
@ -72,11 +80,11 @@
|
||||
<h2>Lease Expiration Crawler</h2>
|
||||
|
||||
<ul>
|
||||
<li>Expiration <span n:render="lease_expiration_enabled" /></li>
|
||||
<li n:render="lease_expiration_mode" />
|
||||
<li n:render="lease_current_cycle_progress" />
|
||||
<li n:render="lease_current_cycle_results" />
|
||||
<li n:render="lease_last_cycle_results" />
|
||||
<li>Expiration <span t:render="lease_expiration_enabled" /></li>
|
||||
<li t:render="lease_expiration_mode" />
|
||||
<li t:render="lease_current_cycle_progress" />
|
||||
<li t:render="lease_current_cycle_results" />
|
||||
<li t:render="lease_last_cycle_results" />
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
|
Loading…
Reference in New Issue
Block a user